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.