// SpruceBand.Sr - last modified: November 20, 1978 7:06 AM // B A N D -- Band management for scan // errors 900 get "Spruce.d" get "SpruceFiles.d" get "Alloc.Decl" // outgoing procedures external // defined here [ BandInit BandClose BandBeginPage BandEndPage BandFlush FlushBuffers PageData // Read/Write scan pass summary (~~ s/b in Check format!) winVec ] // incoming procedures external // external [ //WINDOW CurrentPos PageToPos WindowCreateStream WindowCopy WindowFlush WindowGetBounds WindowGetPosition WindowNextPage WindowPositionPtr WindowRead WindowReadBlock WindowSetBounds WindowSetPage WindowSetPosition WindowWrite WindowWriteBlock PutEofError //SPRUCE Utilities EmergencyOver SpruceCondition SpruceError FSGetRelease FSGetX FSGet FSPut Min Usc //SPRUCEML Ugt DoubleAdd DoubleCop // SpruceBandMl FlushChars //OS MoveBlock Zero CallSwat Closes //METER MeterBlock //CURSOR CursorToggle // ISF IndexedPageIO ] // incoming statics external [ DPzero //Double precision zero. BandFile BandWindow emergencyStorage nVisibleBands //Number of bands required for this device nLeadingBands nTrailingBands Report DoEtherReport DoMeter DebugSystem onlyOnCopy OverlayTop PermanentBottom SpruceZone BandTable BandFree BandAvail CopyTable partNumber ] // internal statics static [ BandBufList BandBuf BandBufSizeTotal BandSegments //Number of "dumps" on disk bandOutPos //Save position of "main" band output firstMergePage //first page used for intermediate merge output winVec ] // buffer management values manifest [ nahMergeOut = 4 // output file ahead count nahMergeIn = 5 // input file ditto // Performance-related sizes: nMaxMergeInputs=4 //Max number of band segments to merge at once lnMaxMergeInputs=2 BandBufSize = 1024+minLenSPrucePage // Max SPrucePage size MUDone = 0 MUCant = 1 ] // BandInit(), BandClose(), BandBeginPage() let BandInit() be [ compileif BEMaxSize ge BandBufSize then [ foo=0 ] BandTable=FSGetX(nVisibleBands) CopyTable=FSGetX(nVisibleBands) BandBufList = 0; ReleaseBandBufs(true) ] and BandClose() be [ BandTable = FSPut(BandTable) CopyTable = FSPut(CopyTable) ReleaseBandBufs(false) ] and BandBeginPage() be // ~~ could easily eliminate [ BandSegments=0 //None on the disk. ] // BandEndPage(page) Segment Merging // Unless the entire page's band entries are in memory, we must now merge all the segments. There // are BandSegments segments to merge; the first begins in page 1 of MergeFile, the 2d in page 2, etc. // Each then continues at nMaxMergInputs-page intervals until its entries are exhausted. All segments // have bands running from 0 to nVisibleBands-1. They must be merged into the main band file. Then // the MergeFile must be discarded. Four segments should be sufficient for any envisioned use. and BandEndPage(page) be [ unless BandSegments do [ BandFlush(page); return ] // write entire page, return BandFlush(0) //Go handle current buffer. CursorToggle(0) let p0, pn = table [ 0; 0 ], vec 2 PageToPos(pn, firstMergePage, 0, wordItem, BandFile) WindowSetBounds(BandWindow, p0, pn) BandWindow>>SS.stepSize = 1 // back to normal page-sequencing WindowSetPosition(BandWindow, bandOutPos) BandFinal(page, 0) BandSegments = BandSegments-1 let v = vec nMaxMergeInputs; winVec = v for s = 0 to BandSegments do [ let pos = vec 2 PageToPos(pos, firstMergePage+s, 0, wordItem, BandFile) winVec!s = WindowCreateStream(BandFile, ksTypeReadOnly, 0, nahMergeIn, pos, 0, nMaxMergeInputs) ] // Band Buffer asserted poised for next page, so one buffer is empty and initialized for b = 0 to nVisibleBands-1 do [ // Bands from different segments must be merged in reverse order for s=BandSegments by -1 to 0 do [ // eLen is length of (end of band) entry -- non-zero only during last iteration let str, eLen, len = winVec!s, (s eq 0? 2, 0), WindowRead(str)+eLen if BandAvail+len ge 0 % emergencyStorage eq 0 then FlushMergeBuffers(true) BandAvail = BandAvail+len; if BandAvail ge 0 then SpruceCondition(906, ECFileTerminate, partNumber) if len then WindowReadBlock(str, BandFree, len-eLen) BandFree = BandFree+len if eLen then BandFree!-2, BandFree!-1 = BEEndH, BEEndH // BandEnd simulation ] ] FlushMergeBuffers(false) // final writing for s=0 to BandSegments do Closes(winVec!s); winVec = 0 WindowGetPosition(BandWindow, bandOutPos) WindowSetBounds(BandWindow) WindowSetPosition(BandWindow, bandOutPos) BandFinal(page, 1) AddBandBuf() // leave set up for next page ] and FlushMergeBuffers(getAnother) be [ // release all buffered pages for input streams to make room for output let posns = vec nMaxMergeInputs*2 for s = 0 to BandSegments do [ let str = winVec!s let pos = posns+s lshift 1 // ~~ if stream at end of page, the GetPosition/SetPosition pair could advance to // ~~ the next page -- since these streams are not proceding through sequential // ~~ pages, this leads to an error. This solution is ugly. // WindowGetPosition(str, pos) // ~~ (the error did occur) pos!0 = str>>SS.sprucePage>>SPrucePage.pageNumber // ~~ needs abstraction pos!1 = CurrentPos(str) WindowFlush(str, true) // release the current page ] FlushBuffers(getAnother) // write current buffer set unless getAnother return for s = 0 to BandSegments do [ // WindowSetPosition(winVec!s, posns+s lshift 1) // ~~ other half of error let str, pos = winVec!s, posns+s lshift 1 WindowSetPage(str, pos!0) WindowPositionPtr(str, pos!1) ] ] // BandFlush( [page] ) // BandFlush is called for three reasons: // BandFlush() called when bands overflow during output. // BandFlush(0) called to flush final partial output before merger // BandFlush(page) called if we get to end of page before band overflow // Two different loops. If this is the final pass, we can simply write the band lists out. If this is not // the final pass, we first count the number of words in the band entry (not counting the EndH) and BandFlush(page; numargs n) be [ if n eq 0 then page=0 let final= n eq 1 // don't allocate more bands if BandFlush(0) unless final%not AddBandBuf(Yes) return final = page ne 0 // now final only if page argument supplied CursorToggle(BandSegments) test final then BandFinal(page, 0) or SetupMergeFile() for b=0 to nVisibleBands-1 do [ // compute band size loop // *** p, nextP, and left must remain contiguous -- used in microcode *** let p, nextP, left = BandTable!b, nil, nil unless final do [ let len=0 while p do [ // ~~ stresses speed; must adjust when new types added -- knows abs. sizes and shapes let typeWd = @p len=len+(typeWd<0? 2, typeWd eq BERectangleH? 4, 2) p=p!-1 ] unless final then WindowWrite(BandWindow, len) p = BandTable!b ] // Write band entries loop (microcode linkage) [ unless p break // ML linkage responsible for keeping stream data correct // FlushChars updates p, nextP, and left (nextP not always derivable any longer) // left is # words left to write in entry indicated by p switchon FlushChars(lv p, lv BandWindow>>FS.fsp) into [ case MUCant: // write current entry, maybe overlapped, go on to (now non-empty) next pp. WindowWriteBlock(BandWindow, p, left+1); endcase case MUDone: break default: SpruceError(102) ] p = nextP ] repeat //Now write end code (only if final file) if final then BandEnd() ] if final then BandFinal(page, 1) // Now reset band buffer stuff ReleaseBandBufs(true) ] // SetupMergeFile() BandFinal(page, last) BandEnd() AddBandBuf() and SetupMergeFile() be // close main BandWindow, set it up on MergeSubfile, position appropriately [ let t1 = BandFile>>SPruceFile.lnPageSize if BandSegments eq 0 then [ bandOutPos = table [ 0; 0 ] WindowGetPosition(BandWindow, bandOutPos) let t2 = BandBufSizeTotal+nVisibleBands+2500 t1 = (t2 rshift (t1-lnMaxMergeInputs))+nMaxMergeInputs-1 let numPages = BandFile>>SPruceFile.numPages let remPages = numPages-BandWindow>>SS.sprucePage>>SPrucePage.pageNumber-1 t1 = Min(t1, remPages) firstMergePage = numPages-t1 BandWindow>>SS.stepSize = nMaxMergeInputs // interlace all intputs to merge ] WindowSetPage(BandWindow, firstMergePage+BandSegments) BandSegments = BandSegments+1 if BandSegments>nMaxMergeInputs then SpruceCondition(900, ECFileTerminate, partNumber) ] and BandFinal(page, last) be [ if last then for i=1 to nTrailingBands do BandEnd() let rec=WindowNextPage(BandWindow) test last eq 0 then page>>PageG.bandPos=rec or page>>PageG.nRecords=rec-page>>PageG.bandPos unless last then for i=1 to nLeadingBands do BandEnd() ] and BandEnd() be for i=0 to 1 do WindowWrite(BandWindow, BEEndH) and AddBandBuf(useFontWidthStorage; numargs na) = valof [ if na<1 then useFontWidthStorage = No unless emergencyStorage resultis false let bb = FSGet(BandBufSize) if not bb&useFontWidthStorage then [ FSGetRelease(0); bb = FSGet(BandBufSize) ] unless bb resultis false BandBufSizeTotal = BandBufSizeTotal+BandBufSize bb!0 = 0 test BandBuf then BandBuf!0 = bb or BandBufList = bb BandBuf = bb BandFree, BandAvail = BandBuf+2, -BandBufSize+BEMaxSize+1 resultis true ] and ReleaseBandBufs(reInitialize) be [ if BandTable then Zero(BandTable, nVisibleBands) let nextBuf = BandBufList while nextBuf do [ let buf = nextBuf; nextBuf = @nextBuf; FSPut(buf) ] BandBufList, BandBufSizeTotal, BandBuf = 0, 0, 0 EmergencyOver() if reInitialize then AddBandBuf() ] // Used to obtain these buffers for alternate uses during merging and font copying and FlushBuffers(getAnother) be [ if BandBuf then BandBuf!1 = BandFree-BandBuf-2 if getAnother&AddBandBuf() return let nextBuf, buf = BandBufList, nil while nextBuf do [ buf, nextBuf = nextBuf, @nextBuf let len = buf!1 if len then unless WindowWriteBlock(BandWindow, buf+2, len) eq len do PutEofError(BandWindow) ] ReleaseBandBufs(getAnother) ] // ------------------------------------------------------ and PageData(pDoc) = valof test pDoc then // ~~ This is all wrong, given cp exist // ------------------------------------------------------ [ WindowSetPosition(BandWindow, DPzero) WindowWriteBlock(BandWindow, pDoc, size DocG/16) WindowWriteBlock(BandWindow, pDoc>>DocG.Pages, pDoc>>DocG.nPages*(size PageG/16)) WindowWriteBlock(BandWindow, pDoc>>DocG.Fonts, pDoc>>DocG.nFontLoads*(size FontG/16)) resultis nil ] or [ let p=OverlayTop // ~~ BandFile c/b truncated, or replaced by map here IndexedPageIO(BandFile>>SPruceFile.map, 1, p, 4, isfRead) let pagelen=p>>DocG.nPages*(size PageG/16) let fontlen=p>>DocG.nFontLoads*(size FontG/16) let totlen=pagelen+fontlen+(size DocG/16) p>>DocG.Fonts=PermanentBottom-fontlen p>>DocG.Pages=PermanentBottom-fontlen-pagelen if Ugt(totlen, 4*256) then SpruceError(100) PermanentBottom=PermanentBottom-totlen MoveBlock(PermanentBottom, p, totlen) resultis PermanentBottom ] // ------- History . . . // DCS, July 27, 1977 11:11 PM, minor (Window stream) revisions // August 4, 1977 10:18 PM, remove unused 500 word vector // September 26, 1977 8:59 AM, handle "only on copy" // September 27, 1977 10:38 AM, "only on copy" when merges required // September 28, 1977 10:14 AM, speed up band flush // October 9, 1977 3:47 PM, repair many Branch reloc. problems // January 23, 1978 9:53 AM, move PageData here, at least for the present // March 11, 1978 1:51 PM, diminish likelihood of storage bombing // October 5, 1978 9:27 PM, make winVec static for external monitoring // October 15, 1978 3:30 PM, modify buffering for fast files // October 23, 1978 12:04 PM, use microcode (via asm code in SpruceBandMl) for buffer flushing // October 24, 1978 8:04 AM, remove linkage method for only-on-copy branching -- use CopyTable // October 25, 1978 7:33 AM, vastly simplify and enhance merge loop -- interlaced streams // October 26, 1978 10:42 PM, cache merge input in large buffers, code shared with FontMake // November 3, 1978 10:32 AM, issue end-of-band entry only after all segments merged // November 4, 1978 12:28 AM, toss out fonts when entering merge mode, to minimize merges // November 20, 1978 7:06 AM, FlushMergeBuffers must avoid destroying odd page succession for // merge streams //