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;
=====
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
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.