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