-- FEPSIvOutputImpl.mesa -- Copyright (C) 1985 by Xerox Corporation. All rights reserved. -- last edited by castillo 10-Jul-85 9:57:12 DIRECTORY Environment USING [ Block, bitsPerByte, bitsPerWord, Byte, bytesPerPage, bytesPerWord, PageCount, wordsPerPage], FEPSIvOutput, FEPSMergerOps USING [OutputH], Inline USING [BITAND, BITSHIFT, HighHalf, LowByte, LowHalf], Interpress USING [ LongOperator, ShortNumber, shortNumberBias, ShortOperator, Token], NSFile USING [Access, Error, Handle, nullHandle], NSSegment USING [ ByteCount, defaultID, Map, Origin, PageCount, PageNumber, SetSizeInBytes], Runtime USING [CallDebugger], Space USING [Activate, Interval, Kill, nullInterval, Unmap], Stream USING [CompletionCode, Handle, GetBlock], XeroxCompress USING [PutBitsProc]; FEPSIvOutputImpl: PROGRAM IMPORTS Inline, NSFile, NSSegment, Runtime, Space, Stream EXPORTS FEPSIvOutput SHARES Interpress = BEGIN OPEN FEPSIvOutput, Env: Environment, IP: Interpress; -- This module exports the interface FEPSIvOutput. The procedures -- in the interface implements the FEPS output routines whose -- primary function is to write data to the output interleaved -- master. This implementation uses NS Filing routines to -- do the I/O. -- ========= -- CONSTANTS -- ========= readWrite: NSFile.Access = [read: TRUE, write: TRUE]; -- ===== -- TYPES -- ===== Window: TYPE = RECORD [page: NSSegment.PageNumber, size: NSSegment.PageCount]; -- ================ -- GLOBAL VARIABLES -- ================ bitBuf: CARDINAL ← 0; -- buffer containing word being build bitPos: CARDINAL ← 0; -- current bit position within bitBuf bufSize: Env.PageCount ← 0; -- size of buffer (ie., window) bufAddr: LONG POINTER TO CARDINAL ← NIL; -- start of buffer currWindow: Window ← [0, 0]; currWrdPtr: LONG POINTER TO CARDINAL ← NIL; -- pointer to current word file: NSFile.Handle ← NSFile.nullHandle; -- current file handle fileSize: NSSegment.PageCount ← 0; -- current size of file endWrdPtr: LONG POINTER TO CARDINAL ← NIL; -- pointer to last word in buffer growSize: NSSegment.PageCount ← 0; -- size file should grow by mapUnit: Space.Interval ← Space.nullInterval; -- current mapUnit -- ====== -- SIGNAL -- ====== Exception: PUBLIC SIGNAL [code: ExceptionKind] = CODE; -- ========== -- PROCEDURES -- ========== -- ===== AppendShortNumber: PROCEDURE [n: IP.ShortNumber] = BEGIN shortSeq: shortNumber IP.Token ← [shortNumber[number: n + IP.shortNumberBias]]; PutWord[shortSeq]; END; -- AppendShortNumber -- ===== ByteAlign: PROCEDURE = INLINE BEGIN IF bitPos MOD Env.bitsPerByte # 0 THEN PutBits[0, 4]; END; -- ByteAlign -- ===== BringNewWindow: PROCEDURE [window: Window] RETURNS [addr: LONG POINTER, endAddr: LONG POINTER, newWindow: Window] = BEGIN origin: NSSegment.Origin ← [ file, window.page, window.size, NSSegment.defaultID]; IF mapUnit.pointer # NIL THEN mapUnit.pointer ← Space.Unmap[mapUnit.pointer]; mapUnit ← NSSegment.Map[origin: origin, access: readWrite]; Space.Activate[mapUnit]; newWindow.page ← window.page; newWindow.size ← mapUnit.count; addr ← mapUnit.pointer; endAddr ← addr + (newWindow.size * Env.wordsPerPage); END; -- BringNewWindow -- ===== GetNextWindow: PROCEDURE = BEGIN newPageNum: NSSegment.PageNumber ← currWindow.page + currWindow.size; -- see if we have enough pages allocated to the file IF newPageNum + bufSize > fileSize THEN { IF mapUnit.pointer # NIL THEN { -- unmap first in case of no resources mapUnit.pointer ← Space.Unmap[mapUnit.pointer]; mapUnit ← Space.nullInterval;}; fileSize ← GrowFileSize[fileSize, growSize];}; -- change mapping [bufAddr, endWrdPtr, currWindow] ← BringNewWindow[[newPageNum, bufSize]]; currWrdPtr ← bufAddr; END; -- GetNextWindow -- ===== GrowFileSize: PROCEDURE [size: NSSegment.PageCount, incr: NSSegment.PageCount] RETURNS [newSize: NSSegment.PageCount] = BEGIN -- This routine will increase the size of a file by "incr". -- If the request cannot be satisfied, then the increment is -- reduced by two. If the increment is bufSize and there is -- still no enough disk space, then signal is raised. The -- routine catching the signal may wait until disk space is -- available. newIncr: NSSegment.PageCount ← incr; minSize: NSSegment.PageCount ← bufSize; NSSegment.SetSizeInBytes[ file: file, bytes: (size + newIncr) * Env.bytesPerPage ! NSFile.Error => { WITH errorRec: error SELECT FROM space => { IF errorRec.problem = mediumFull THEN { newIncr ← newIncr / 2; -- try getting smaller chunk IF newIncr < minSize THEN { -- indicate that we can't SIGNAL Exception[noResources]; newIncr ← incr}; RETRY;} -- assume that we have enough now ELSE SIGNAL Exception[unknown]; } -- space ENDCASE => SIGNAL Exception[unknown]; }]; newSize ← size + newIncr; END; -- GrowFileSize -- ================= -- PUBLIC PROCEDURES -- ================= -- ===== CloseOutput: PUBLIC PROCEDURE = BEGIN byteSize: NSSegment.ByteCount; -- flush out bit buffer -- Assume that it is byte aligned now (kluge because we -- know it is true due to its use). IF bitPos # 0 THEN currWrdPtr↑ ← bitBuf; byteSize ← (currWindow.page * Env.bytesPerPage) + ((currWrdPtr - bufAddr) * Env.bytesPerWord); IF bitPos # 0 THEN byteSize ← byteSize + 1; IF mapUnit.pointer # NIL THEN mapUnit.pointer ← Space.Unmap[mapUnit.pointer]; mapUnit ← Space.nullInterval; NSSegment.SetSizeInBytes[file: file, bytes: byteSize]; END; -- CloseOutput -- ===== EmptyOutput: PUBLIC PROCEDURE = BEGIN IF mapUnit.pointer # NIL THEN { Space.Kill[mapUnit]; mapUnit.pointer ← Space.Unmap[mapUnit.pointer];}; mapUnit ← Space.nullInterval; NSSegment.SetSizeInBytes[file: file, bytes: 0]; END; -- EmptyOutput -- ===== GetCurrentPosition: PUBLIC PROCEDURE RETURNS [pos: Position] = BEGIN ByteAlign[]; pos.offset ← (currWrdPtr - bufAddr) * Env.bytesPerWord; IF bitPos # 0 THEN pos.offset ← pos.offset + 1; -- Get the nearest page pos.page ← currWindow.page + (pos.offset / Env.bytesPerPage); pos.offset ← pos.offset MOD Env.bytesPerPage; END; -- GetCurrentPosition -- ===== Initialize: PUBLIC PROCEDURE [ output: FEPSMergerOps.OutputH, initialSize: NSSegment.PageCount, growIncrement: NSSegment.PageCount, bufferSize: NSSegment.PageCount] = BEGIN origin: NSSegment.Origin; growSize ← growIncrement; bufSize ← bufferSize; file ← output.ivMasterH; fileSize ← GrowFileSize[0, MAX[initialSize, bufSize]]; -- create the mapUnit and map to start of file origin ← [file: file, base: 0, count: bufSize]; mapUnit ← NSSegment.Map[origin: origin, access: readWrite]; -- ***** TEMPORARY ***** IF mapUnit.count # origin.count THEN Runtime.CallDebugger["Map unit mismatch in Initialize."L]; Space.Activate[mapUnit]; currWindow ← [0, mapUnit.count]; currWrdPtr ← bufAddr ← mapUnit.pointer; endWrdPtr ← bufAddr + (currWindow.size * Env.wordsPerPage); bitBuf ← 0; bitPos ← 0; END; -- Initialize -- ===== PutBits: PUBLIC XeroxCompress.PutBitsProc = BEGIN remainder: INTEGER; currBitPos: INTEGER ← bitPos + bits; -- current bit position remainder ← Env.bitsPerWord - currBitPos; -- Processing is funtion of resulting buffer word position SELECT TRUE FROM (remainder > 0) => -- Incomplete BEGIN bitBuf ← bitBuf + Inline.BITSHIFT[val, remainder]; bitPos ← currBitPos; END; (remainder = 0) => -- Full BEGIN currWrdPtr↑ ← bitBuf + val; currWrdPtr ← currWrdPtr + 1; IF currWrdPtr = endWrdPtr THEN GetNextWindow[]; bitBuf ← 0; bitPos ← 0; END; ENDCASE => -- Overflow BEGIN bitPos ← -remainder; currWrdPtr↑ ← bitBuf + Inline.BITSHIFT[val, remainder]; bitBuf ← Inline.BITSHIFT[val, Env.bitsPerWord - bitPos]; currWrdPtr ← currWrdPtr + 1; IF currWrdPtr = endWrdPtr THEN GetNextWindow[]; END; END; -- PutBits -- ===== PutByte: PUBLIC PROCEDURE [val: CARDINAL] = BEGIN -- see if at byte boundary ByteAlign[]; PutBits[val, 8]; END; -- PutByte -- ===== PutWord: PUBLIC PROCEDURE [val: UNSPECIFIED] = BEGIN ByteAlign[]; PutBits[val, 16]; END; -- PutWord -- ===== Retrieve: PUBLIC PROCEDURE [inputSH: Stream.Handle, byteCount: LONG CARDINAL] = BEGIN -- Retrieve byteCount bytes from stream to output block: Env.Block; bytesMoved: CARDINAL ← 0; start: CARDINAL ← 0; LMin: PROC [l: LONG CARDINAL, c: CARDINAL] RETURNS [r: CARDINAL] = { IF l > c THEN r ← c ELSE r ← Inline.LowHalf[l]}; -- byte align ByteAlign[]; -- flush out current bit buffer if not word aligned IF bitPos # 0 THEN {currWrdPtr↑ ← bitBuf; start ← 1} ELSE start ← 0; -- kluge to avoid overflow in case byteCount is LAST[LONG CARDINAL] -- If so, it is assumed to mean retrieve til EOF. IF byteCount = LAST[LONG CARDINAL] THEN byteCount ← byteCount - 1; DO status: Stream.CompletionCode; endIndexPlusOne: CARDINAL; block.blockPointer ← LOOPHOLE[currWrdPtr]; block.startIndex ← start; endIndexPlusOne ← Inline.LowHalf[(endWrdPtr - currWrdPtr)] * Env.bytesPerWord; block.stopIndexPlusOne ← LMin[byteCount + block.startIndex, endIndexPlusOne]; [bytesMoved, status] ← Stream.GetBlock[inputSH, block]; byteCount ← byteCount - bytesMoved; currWrdPtr ← currWrdPtr + (block.startIndex + bytesMoved) / Env.bytesPerWord; -- if output buffer is full, get a new one IF currWrdPtr = endWrdPtr THEN {GetNextWindow[]; bitBuf ← 0; bitPos ← 0}; IF (status = endOfStream) OR byteCount = 0 THEN EXIT; start ← 0; ENDLOOP; -- while bytes left -- if one byte is left over, write it out to buffer bitPos ← ((block.startIndex + bytesMoved) MOD Env.bytesPerWord) * Env.bitsPerByte; IF bitPos # 0 THEN bitBuf ← Inline.BITAND[currWrdPtr↑, 177400B] ELSE bitBuf ← 0; END; -- Retrieve -- ===== SetPosition: PUBLIC PROCEDURE [pos: Position] = BEGIN -- Set the output to the specified position -- -- Make sure current bit buffer is written out IF bitPos # 0 THEN currWrdPtr↑ ← Inline.BITAND[currWrdPtr↑, 377B] + bitBuf; -- see if we have enough pages allocated to the file IF pos.page + bufSize > fileSize THEN fileSize ← GrowFileSize[fileSize, growSize]; -- grow file -- change mapping [bufAddr, endWrdPtr, currWindow] ← BringNewWindow[[pos.page, bufSize]]; currWrdPtr ← bufAddr + (pos.offset / Env.bytesPerWord); -- see if at word boundary bitPos ← Inline.LowHalf[(pos.offset MOD Env.bytesPerWord) * Env.bitsPerByte]; IF bitPos = 0 THEN bitBuf ← 0 ELSE {bitBuf ← Inline.BITAND[currWrdPtr↑, 177400B]}; END; -- SetPosition -- Interpress output routines -- ===== IPIdentifier: PUBLIC PROCEDURE [s: LONG STRING] = BEGIN OPEN Inline; shortToken: shortSequence operatorOrSequence IP.Token ← [ operatorOrSequence[ shortSequence[ type: sequenceIdentifier, length: LowByte[LowHalf[s.length]]]]]; PutWord[shortToken]; FOR i: CARDINAL IN [0..s.length) DO PutByte[LOOPHOLE[s[i], Env.Byte]]; ENDLOOP; END; -- IPIdentifier -- ===== IPInteger: PUBLIC PROCEDURE [n: LONG INTEGER, length: CARDINAL ← 0] = BEGIN IF (length = 1) OR (length = 0 AND n IN [-IP.shortNumberBias..(LAST[INTEGER] - IP.shortNumberBias)]) THEN AppendShortNumber[Inline.LowHalf[n]] ELSE { shortSeq: shortSequence operatorOrSequence IP.Token; IF length = 0 THEN length ← IF n IN [FIRST[INTEGER]..LAST[INTEGER]] THEN 2 ELSE 4; shortSeq ← [ operatorOrSequence[shortSequence[type: sequenceInteger, length: length]]]; PutWord[shortSeq]; IF length = 4 THEN PutWord[Inline.HighHalf[n]]; PutWord[Inline.LowHalf[n]]; }; END; -- IPInteger -- ===== IPSeqCompressedPixel: PUBLIC PROCEDURE [size: LONG CARDINAL] = BEGIN longSeq: longSequence operatorOrSequence IP.Token ← [ operatorOrSequence[ longSequence[ type: sequenceCompressedPixelVector, lengthHigh: Inline.HighHalf[size], lengthLow: Inline.LowHalf[size]]]]; ptrToToken: LONG POINTER ← @longSeq; ptr: LONG POINTER TO LONG CARDINAL ← LOOPHOLE[ptrToToken]; PutWord[Inline.LowHalf[ptr↑]]; PutWord[Inline.HighHalf[ptr↑]]; END; -- IPSeqCompressedPixel -- ===== OpOnly: PUBLIC PROCEDURE [code: Interpress.LongOperator] = BEGIN IF code IN IP.ShortOperator THEN { sOp: shortOperator operatorOrSequence IP.Token ← [ operatorOrSequence[shortOperator[operator: code]]]; pOp: POINTER TO PACKED ARRAY [0..1] OF Environment.Byte ← LOOPHOLE[@sOp]; PutByte[pOp[0]]} ELSE { lOp: longOperator operatorOrSequence IP.Token ← [ operatorOrSequence[longOperator[operator: code]]]; PutWord[lOp]; }; END; -- OpOnly END... LOG 20Nov84 - Okamoto - Changed FreeOutput to CloseOutput and added EmptyOutput. 22Jan85 - Okamoto - Changed so that mapUnit is unmapped before set size in EmptyOutput and GrowFileSize call in GetNextWindow. 10Jul85 - castillo - copyright notice.