TTYImpl.mesa
Copyright Ó 1983, 1984, 1986, 1987 by Xerox Corporation. All rights reserved.
Tim Diebert: January 6, 1987 5:08:53 pm PST
DIRECTORY
Ascii USING [BS, ControlA, ControlQ, ControlR, ControlV, ControlW, ControlX, CR, DEL, FF, SP, TAB],
Basics USING [UnsafeBlock, HighByte, HighHalf, LowByte, LowHalf, RawBytes],
Convert USING [IntFromRope, RopeFromInt],
IO,
Process USING [Abort, DisableTimeout, EnableAborts],
PrincOpsUtils USING [IsBound],
RefText USING [Append, ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope USING [ROPE, ToRefText],
TTY USING [CharStatus, CreateTTYInstance, -- DateFormat, -- EchoClass, Handle --, NumberFormat-- ],
TTYConstants,
TTYStream USING [SendAttention, SetSST, SubSequenceType, WaitAttention];
TTYImpl: MONITOR LOCKS h USING h: Handle
IMPORTS Basics, Convert, IO, PrincOpsUtils, Process, RefText, Rope, TTY, TTYStream
EXPORTS TTY = BEGIN
Handle: PUBLIC TYPE = REF Object;
Object: PUBLIC TYPE = MONITORED RECORD [
stream: IO.STREAMNIL,
echo: TTY.EchoClass ← plain,
newline: BOOLEANTRUE,
inputAborted: BOOLEANFALSE,
next: Handle,
buffer: Q ← Init[],
backingStream: IO.STREAMNIL,
altStream: AltHandle ← NIL,
inputProcess: PROCESSNIL,
inputCondition: CONDITION,
tty: TTY.Handle ← NIL];
AltHandle: PUBLIC TYPE = REF AltObject;
AltObject: TYPE = RECORD [s: IO.STREAM, next: AltHandle];
list: Handle ← NIL;
OutOfInstances: PUBLIC ERROR = CODE;
NoDefaultInstance: PUBLIC ERROR = CODE;
aborted: BYTE = TTYConstants.aborted;
notAborted: BYTE = TTYConstants.notAborted;
normal: TTYStream.SubSequenceType = TTYConstants.normal;
setBackingSize: TTYStream.SubSequenceType = TTYConstants.setBackingSize;
removeChars: TTYStream.SubSequenceType = TTYConstants.removeChars;
blinkDisplay: TTYStream.SubSequenceType = TTYConstants.blinkDisplay;
Create: PUBLIC SAFE PROCEDURE [name: Rope.ROPENIL,
backingStream, ttyImpl: IO.STREAMNIL] RETURNS [h: Handle] = TRUSTED {
h ← NEW[Object ← [stream: NIL, backingStream: NIL, next: list]];
IF ttyImpl = NIL THEN
SELECT PrincOpsUtils.IsBound[LOOPHOLE[TTY.CreateTTYInstance]] FROM
TRUE => [ttyImpl, backingStream] ← TTY.CreateTTYInstance[name, backingStream, h.tty];
FALSE => ERROR NoDefaultInstance;
ENDCASE;
h.backingStream ← backingStream;
h.stream ← ttyImpl;
list ← h;
Process.DisableTimeout[@h.inputCondition];
Process.EnableAborts[@h.inputCondition];
h.inputProcess ← FORK ListenForInput[h]};
Destroy: PUBLIC SAFE PROC [h: Handle, deleteBackingFile: BOOLEANFALSE] = TRUSTED {
P: ENTRY PROCEDURE [h: Handle] = {
ENABLE UNWIND => NULL;
IF list = h
THEN list ← h.next
ELSE
FOR l: Handle ← list, l.next UNTIL l.next = NIL DO
IF l.next = h THEN {l.next ← l.next.next; EXIT}; ENDLOOP;
IF ~IsEmpty[h.buffer] THEN Empty[h.buffer]};
TTYStream.SetSST[h.stream, TTYConstants.deleteBackingFile];
TTYStream.SetSST[h.stream, normal];
Process.Abort[h.inputProcess];
JOIN h.inputProcess;
P[h];
IO.Close[h.stream];
};
ListenForInput: PROCEDURE [h: Handle] = TRUSTED BEGIN
DoIt: ENTRY PROCEDURE [h: Handle] = {
ENABLE UNWIND => NULL;
wakeUp: BOOLEAN = IsEmpty[h.buffer];
BlockToQR[block, h.buffer];
IF wakeUp THEN BROADCAST h.inputCondition};
AbortClient: ENTRY PROCEDURE [h: Handle] = {
h.inputAborted ← TRUE; BROADCAST h.inputCondition};
blockSize: CARDINAL = 20;
block: Basics.UnsafeBlock;
storage: PACKED ARRAY [0..blockSize) OF BYTE;
block ← [base: LOOPHOLE[LONG[@storage]], startIndex: 0, count: blockSize];
DO
block.count ← blockSize;
block.count ← IO.UnsafeGetBlock[h.stream, block ! ABORTED => EXIT];
DoIt[h];
ENDLOOP;
AbortClient[h];
END;
GetEcho: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] RETURNS [TTY.EchoClass] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; RETURN[h.echo]};
SetEcho: PUBLIC ENTRY SAFE PROCEDURE [h: Handle, new: TTY.EchoClass]
RETURNS
[old: TTY.EchoClass] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; old ← h.echo; h.echo ← new};
CharsAvailable: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] RETURNS [CARDINAL] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; RETURN[Count[h.buffer]];
-- note that this count is incorrect if there are alternate input streams --};
GetChar: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] RETURNS [c: CHARACTER] = TRUSTED {
ENABLE UNWIND => NULL;
advanceAltStream: BOOLEANFALSE;
temp: AltHandle;
WHILE h.altStream # NIL DO
c ← IO.GetChar[h.altStream.s ! IO.EndOfStream => {advanceAltStream ← TRUE; CONTINUE}];
IF ~advanceAltStream THEN RETURN[c];
IO.Close[h.altStream.s];
temp ← h.altStream; h.altStream ← h.altStream.next;
advanceAltStream ← FALSE;
ENDLOOP;
WHILE IsEmpty[h.buffer] DO InternalCheckState[h]; WAIT h.inputCondition ENDLOOP;
c ← GetF[h.buffer]};
PopAlternateInputStreams: PUBLIC ENTRY SAFE PROC [h: Handle, howMany: CARDINAL ← 1] = TRUSTED BEGIN
ENABLE UNWIND => NULL;
temp: AltHandle;
FOR i: CARDINAL IN [0..howMany) WHILE h.altStream # NIL DO
IO.Close[h.altStream.s];
temp ← h.altStream; h.altStream ← h.altStream.next;
ENDLOOP;
END;
PushAlternateInputStream: PUBLIC ENTRY SAFE PROCEDURE [h: Handle, stream: IO.STREAM] = TRUSTED BEGIN
ENABLE UNWIND => NULL;
h.altStream ← NEW[AltObject ← [s: stream, next: h.altStream]];
END;
PutBackChar: PUBLIC ENTRY SAFE PROCEDURE [h: Handle, c: CHARACTER] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; PutR[h.buffer, c];
-- note that this does not work if there are alternate input streams --};
ResetUserAbort: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h];
TTYStream.SendAttention[h.stream, notAborted]};
UserAbort: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] RETURNS [BOOLEAN] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h];
RETURN[SELECT TTYStream.WaitAttention[h.stream] FROM
aborted => TRUE,
ENDCASE => FALSE]};
SetUserAbort: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; TTYStream.SendAttention[h.stream, aborted]};
BlinkDisplay: PUBLIC SAFE PROC [h: Handle] = TRUSTED {
CheckState[h];
TTYStream.SetSST[h.stream, blinkDisplay];
TTYStream.SetSST[h.stream, normal]};
NewLine: PUBLIC ENTRY SAFE PROCEDURE [h: Handle] RETURNS [BOOLEAN] = TRUSTED {
ENABLE UNWIND => NULL;
InternalCheckState[h]; RETURN[h.newline]};
PutChar: PUBLIC SAFE PROC [h: Handle, c: CHARACTER] = TRUSTED {
P: ENTRY PROCEDURE [h: Handle] = {
ENABLE UNWIND => NULL; h.newline ← c = Ascii.CR};
CheckState[h];
P[h];
IO.PutChar[h.stream, c]};
PutString, PutLongString, PutText: PROCEDURE [h: Handle, s: REF TEXT] = TRUSTED {
P: ENTRY PROCEDURE [h: Handle] = {
ENABLE UNWIND => NULL;
h.newline ← s[s.length - 1] = Ascii.CR};
CheckState[h];
IF s = NIL THEN RETURN;
IO.UnsafePutBlock[h.stream, [LOOPHOLE[@s.text], 0, s.length]];
P[h]};
RemoveCharacter, RemoveCharacters: PUBLIC SAFE PROC [h: Handle, n: CARDINAL] = TRUSTED {
CheckState[h];
TTYStream.SetSST[h.stream, removeChars];
IO.PutChar[h.stream, LOOPHOLE[Basics.HighByte[n]]];
IO.PutChar[h.stream, LOOPHOLE[Basics.LowByte[n]]];
TTYStream.SetSST[h.stream, normal]};
Backing Files
NoBackingFile: PUBLIC ERROR = CODE;
BackingStream: PUBLIC SAFE PROC [h: Handle] RETURNS [IO.STREAM] = TRUSTED {
CheckState[h]; RETURN[h.backingStream]};
SetBackingSize: PUBLIC SAFE PROC [h: Handle, size: LONG CARDINAL] = TRUSTED {
lowHalf, highHalf: CARDINAL;
CheckState[h];
highHalf ← Basics.HighHalf[size];
lowHalf ← Basics.LowHalf[size];
TTYStream.SetSST[h.stream, setBackingSize];
IO.PutChar[h.stream, LOOPHOLE[Basics.HighByte[highHalf]]];
IO.PutChar[h.stream, LOOPHOLE[Basics.LowByte[highHalf]]];
IO.PutChar[h.stream, LOOPHOLE[Basics.HighByte[lowHalf]]];
IO.PutChar[h.stream, LOOPHOLE[Basics.LowByte[lowHalf]]];
TTYStream.SetSST[h.stream, normal]};
Basic input
GetDecimal: PUBLIC SAFE PROC [h: Handle] RETURNS [i: INTEGER] = TRUSTED {
s: REF TEXT ← RefText.ObtainScratch[10];
CheckState[h];
[] ← GetEditedString[h, s, IsAtom];
i ← Convert.IntFromRope[RefText.TrustTextAsRope[s], 10];
RefText.ReleaseScratch[s];
RETURN[i]};
GetID: PUBLIC SAFE PROC [h: Handle, s: REF TEXT] = TRUSTED {
CheckState[h]; [] ← GetEditedString[h, s, IsAtom]};
GetLine: PUBLIC SAFE PROC [h: Handle, s: REF TEXT] = TRUSTED {
CheckState[h]; [] ← GetEditedString[h, s, IsCR]; PutChar[h, Ascii.CR]};
GetPassword: PUBLIC SAFE PROC [h: Handle, s: REF TEXT] = TRUSTED {
old: TTY.EchoClass;
CheckState[h];
old ← SetEcho[h, stars];
IF old = none THEN [] ← SetEcho[h, none];
[] ← GetEditedString[h, s, IsAtom ! UNWIND => [] ← SetEcho[h, old]];
[] ← SetEcho[h, old]};
GetLongDecimal: PUBLIC SAFE PROC [h: Handle] RETURNS [n: INT] = TRUSTED {
s: REF TEXT ← RefText.ObtainScratch[32];
CheckState[h];
[] ← GetEditedString[h, s, IsAtom];
n ← Convert.IntFromRope[RefText.TrustTextAsRope[s], 10];
RefText.ReleaseScratch[s];
RETURN[n]};
GetLongNumber: PUBLIC SAFE PROC [h: Handle, default: LONG UNSPECIFIED,
radix: CARDINAL, showDefault: BOOLEAN] RETURNS [n: LONG UNSPECIFIED] = TRUSTED {
s: REF TEXT ← RefText.ObtainScratch[32];
r: REF TEXT ← RefText.ObtainScratch[32];
CheckState[h];
IF showDefault THEN {
IF radix = 10 AND LOOPHOLE[default, LONG INTEGER] < 0
THEN {s[0] ← '-; s.length ← 1; default ← -default};
r ← Rope.ToRefText[Convert.RopeFromInt[default, radix]];
s ← RefText.Append[s, r, radix]};
IF radix = 8 THEN String.AppendChar[s, 'B]}; -- Done by Convert.RopeFromInt
[] ← GetEditedString[h, s, IsAtom];
n ← Convert.IntFromRope[RefText.TrustTextAsRope[s], 10];
RefText.ReleaseScratch[s];
RefText.ReleaseScratch[r];
RETURN[n]};
GetLongOctal: PUBLIC SAFE PROC [h: Handle] RETURNS [n: LONG UNSPECIFIED] = TRUSTED {
s: REF TEXT ← RefText.ObtainScratch[32];
CheckState[h];
[] ← GetEditedString[h, s, IsAtom];
n ← Convert.IntFromRope[RefText.TrustTextAsRope[s], 8];
RefText.ReleaseScratch[s];
RETURN[n]};
GetNumber: PUBLIC SAFE PROC [h: Handle, default: UNSPECIFIED, radix: CARDINAL,
showDefault: BOOLEAN] RETURNS [n: UNSPECIFIED] = TRUSTED {
sDefault: LONG UNSPECIFIEDLONG[default];
result: LONG UNSPECIFIED;
result ← GetLongNumber[h, sDefault, radix, showDefault];
n ← Basics.LowHalf[LOOPHOLE[result]];
RETURN[n]};
GetOctal: PUBLIC SAFE PROC [h: Handle] RETURNS [n: UNSPECIFIED] = TRUSTED {
result: LONG UNSPECIFIED;
resultGetLongOctal[h];
n ← Basics.LowHalf[LOOPHOLE[result]];
RETURN[n]};
GetString: PUBLIC SAFE PROC [h: Handle, s: REF TEXT,
t: PROCEDURE [c: CHARACTER] RETURNS [status: TTY.CharStatus]] = TRUSTED {
CheckState[h]; PutChar[h, GetEditedString[h, s, t]]};
LineOverflow: PUBLIC SAFE SIGNAL [s: REF TEXT] RETURNS [ns: REF TEXT] = CODE;
Rubout: PUBLIC SAFE SIGNAL = CODE;
GetEditedString: PUBLIC SAFE PROC [h: Handle, s: REF TEXT,
t: PROCEDURE [c: CHARACTER] RETURNS [status: TTY.CharStatus]]
RETURNS [c: CHARACTER] = TRUSTED {
WeirdEcho: PROCEDURE [c: CHARACTER] = {
SELECT GetEcho[h] FROM
none => NULL;
plain => PutChar[h, c];
stars => PutChar[h, '*]
ENDCASE};
WeirdErase: PROCEDURE [c: CHARACTER] = {
SELECT GetEcho[h] FROM
none => NULL;
plain =>RemoveCharacters[h,SELECT c FROM
Ascii.CR, Ascii.BS, Ascii.FF => 0,
IN [0C..Ascii.SP) => 2,
IN [Ascii.SP..'~] => 1,
Ascii.TAB => 1, -- ARGH!!!!!!
ENDCASE => 0];
stars => RemoveCharacter[h, 1]
ENDCASE};
WeirdPutString: PROCEDURE = INLINE {
FOR i: CARDINAL IN [0..s.length) DO WeirdEcho[s[i]] ENDLOOP};
KillString: PROCEDURE = {
FOR i: CARDINAL DECREASING IN [0..s.length) DO WeirdErase[s[i]] ENDLOOP;
s.length ← 0};
firstChar: BOOLEANTRUE;
CheckState[h];
WeirdPutString[]; -- show whatever default string is passed in, if any
DO
SELECT t[c ← GetChar[h]] FROM
stop => RETURN;
ignore => LOOP;
ok =>SELECT c FROM
Ascii.DEL => SIGNAL Rubout;
Ascii.ControlA, Ascii.BS => -- backspace
IF s.length > 0 THEN WeirdErase[s[s.length ← s.length - 1]];
Ascii.ControlW, Ascii.ControlQ => { -- backword
text to be backed up is of the form ...<li><v><ti>, the <v> and <ti> are to be removed.
state: {ti, v, li} ← ti;
FOR i: CARDINAL DECREASING IN [0..s.length) DO
SELECT s[i] FROM
IN ['A..'Z], IN ['a..'z], IN ['0..'9] => IF state = ti THEN state ← v;
ENDCASE => IF state = v THEN state ← li;
IF state = li THEN GO TO Done;
WeirdErase[s[i]];
REPEAT Done => s.length ← i + 1; FINISHED => s.length ← 0;
ENDLOOP};
Ascii.ControlX => KillString[];
Ascii.ControlR => { -- refresh--
IF GetEcho[h] # none THEN {PutChar[h, Ascii.CR]; WeirdPutString[]};
LOOP};
Ascii.ControlV => { -- dont parse next char
IF firstChar THEN KillString[];
WHILE s.length >= s.maxLength DO s ← SIGNAL LineOverflow[s] ENDLOOP;
WeirdEcho[s[s.length] ← c ← GetChar[h]];
s.length ← s.length + 1};
ENDCASE => {
IF firstChar THEN KillString[];
WHILE s.length >= s.maxLength DO s ← SIGNAL LineOverflow[s] ENDLOOP;
WeirdEcho[s[s.length] ← c];
s.length ← s.length + 1};
ENDCASE;
firstChar ← FALSE;
ENDLOOP};
IsAtom: PROCEDURE [c: CHARACTER] RETURNS [TTY.CharStatus] = {
RETURN[IF c = Ascii.SP OR c = Ascii.CR THEN stop ELSE ok]};
IsCR: PROCEDURE [c: CHARACTER] RETURNS [TTY.CharStatus] = {
RETURN[IF c = Ascii.CR THEN stop ELSE ok]};
CheckState: ENTRY PROCEDURE [h: Handle] = INLINE {
ENABLE UNWIND => NULL;
IF h.inputAborted THEN ERROR ABORTED};
InternalCheckState: PROCEDURE [h: Handle] = INLINE {
IF h.inputAborted THEN ERROR ABORTED};
extended output procedures
OutString: PROCEDURE [s: REF TEXT, clientData: Handle] = {
P: ENTRY PROCEDURE [h: Handle] = {
ENABLE UNWIND => NULL;
h.newline ← s[s.length - 1] = Ascii.CR};
IF s = NIL THEN RETURN;
IO.UnsafePutBlock[clientData.stream, [LOOPHOLE[@s.text, LONG POINTER] + SIZE[TEXT[0]], 0, s.length]];
P[clientData]};
PutBlank, PutBlanks: PUBLIC SAFE PROC [h: Handle, n: CARDINAL] = TRUSTED {
s: REF TEXT ← RefText.ObtainScratch[n*2];
CheckState[h];
FOR i: CARDINAL IN [0 .. s.maxLength) DO s[i] ← ' ; ENDLOOP;
s.length ← n;
OutString[s, h];
RefText.ReleaseScratch[s]};
PutBlock: PUBLIC SAFE PROC [h: Handle, block: Basics.UnsafeBlock] = TRUSTED {
CheckState[h];
IO.UnsafePutBlock[h.stream, block]};
PutDate: PUBLIC SAFE PROC [h: Handle, gmt: Time.Packed, format: TTY.DateFormat,
zone: Time.TimeZoneStandard] = TRUSTED {CheckState[h]};
CheckState[h]; Format.Date[OutString, gmt, format, zone, h]};
PutDecimal: PUBLIC SAFE PROC [h: Handle, n: INTEGER] = TRUSTED {
CheckState[h];
IO.PutF[h.stream, "%g", IO.int[n]]};
PutLine: PUBLIC SAFE PROC [h: Handle, s: REF TEXT] = TRUSTED {
CheckState[h]; PutString[h, s]; PutChar[h, Ascii.CR]};
PutLongDecimal: PUBLIC SAFE PROC [h: Handle, n: LONG INTEGER] = TRUSTED {
CheckState[h];
IO.PutF1[h.stream, "%g", IO.int[n]]};
PutLongNumber: PUBLIC SAFE PROC [h: Handle, n: LONG UNSPECIFIED,
format: TTY.NumberFormat] = TRUSTED {CheckState[h]};
CheckState[h];
Format.LongNumber[OutString, n, format, h]};
PutLongOctal: PUBLIC SAFE PROC [h: Handle, n: LONG UNSPECIFIED] = TRUSTED {
CheckState[h]};
CheckState[h]; Format.LongOctal[OutString, n, h]};
PutNumber: PUBLIC SAFE PROC [h: Handle, n: UNSPECIFIED, format: TTY.NumberFormat] = TRUSTED {
CheckState[h]};
Format.Number[OutString, n, format, h]};
PutOctal: PUBLIC SAFE PROC [h: Handle, n: UNSPECIFIED] = TRUSTED {
CheckState[h]};
Format.Octal[OutString, n, h]};
PutLongSubString, PutSubString: PUBLIC SAFE PROC [
h: Handle, ss: String.SubString] = TRUSTED {
CheckState[h]; Format.SubString[OutString, ss, h]};
Queue implementation
BYTE: PRIVATE TYPE = [0..255];
Q: TYPE = REF QHead;
QHead: TYPE = RECORD [front, rear: REF QElement];
NullQHead: QHead = [NIL, NIL];
QElement: PRIVATE TYPE = RECORD [
flink, rlink: REF QElement ← NIL,
front, rear: CARDINAL ← 0,
data: PACKED SEQUENCE maxLen: CARDINAL OF BYTE];
pQE: PRIVATE TYPE = REF QElement;
defaultElementSize: PRIVATE CARDINAL = 20;
EmptyQ: SAFE SIGNAL [q: Q] = CODE;
Count: PROCEDURE [q: Q] RETURNS [elements: CARDINAL ← 0] = BEGIN
IF q = NIL THEN RETURN;
FOR qp: pQE ← q.front, qp.flink UNTIL qp = NIL DO
elements ← elements +
(IF qp.front > qp.rear THEN qp.maxLen - qp.front + qp.rear + 1 ELSE qp.rear - qp.front + 1);
ENDLOOP;
RETURN
END;
IsEmpty: PROCEDURE [q: Q] RETURNS [BOOLEAN] = {
RETURN[q = NIL OR q.front = NIL]};
Empty: PROCEDURE [q: Q] = BEGIN
UNTIL IsEmpty[q] DO
qf: pQE ← q.front;
nqf: pQE = qf.flink;
z.FREE[@qf];
IF nqf = NIL THEN q.rear ← NIL ELSE nqf.rlink ← NIL;
q.front ← nqf;
ENDLOOP;
RETURN
END;
Init: PROCEDURE RETURNS [Q] = {RETURN[NIL]};
GetF: PROCEDURE [q: Q] RETURNS [i: UNSPECIFIED [0..255]] = BEGIN
qf: pQE;
qff: CARDINAL;
IF q = NIL OR q.front = NIL THEN {SIGNAL EmptyQ[q]; RETURN[0]};
qf ← q.front;
i ← qf[qff ← qf.front];
IF qf.front = qf.rear
THEN BEGIN -- element empty
nf: pQE = qf.flink;
IF nf = NIL THEN q.rear ← NIL ELSE nf.rlink ← NIL;
z.FREE[@qf];
q.front ← nf;
END
ELSE BEGIN qff ← IF qff = qf.maxLen - 1 THEN 0 ELSE qff + 1; qf.front ← qff; END;
RETURN
END;
GetR: PROCEDURE [q: Q] RETURNS [i: UNSPECIFIED [0..255]] = BEGIN
qr: pQE;
qrr: CARDINAL;
IF q = NIL OR q.rear = NIL THEN {SIGNAL EmptyQ[q]; RETURN[0]};
qr ← q.rear;
i ← qr[qrr ← qr.rear];
IF qr.rear = qr.front
THEN BEGIN -- element empty
nr: pQE = qr.rlink;
IF nr = NIL THEN q.front ← NIL ELSE nr.flink ← NIL;
z.FREE[@qr];
q.rear ← nr;
END
ELSE BEGIN qrr ← IF qrr = 0 THEN qr.maxLen - 1 ELSE qrr - 1; qr.rear ← qrr; END;
RETURN
END;
PutR: PROCEDURE [q: Q, i: UNSPECIFIED [0..255]] = BEGIN
qr: pQE;
qrr: CARDINAL;
IF q = NIL THEN RETURN;
qr ← q.rear;
IF qr = NIL OR FullElement[qr]
THEN BEGIN
nr: pQE ← NEW[QElement[defaultElementSize]];
nr.rlink ← qr;
IF qr = NIL THEN q.front ← nr ELSE qr.flink ← nr;
q.rear ← qr ← nr;
qrr ← 0;
END
ELSE qrr ← IF qr.rear = qr.maxLen - 1 THEN 0 ELSE qr.rear + 1;
qr[qrr] ← i;
qr.rear ← qrr;
RETURN
END;
BlockToQR: PROCEDURE [block: Basics.UnsafeBlock, q: Q] = BEGIN
FOR i: INT IN [block.startIndex..block.startIndex+block.count) DO
base: LONG POINTER TO Basics.RawBytes ← LOOPHOLE[block.base];
PutR[q, base[i]] ENDLOOP;
END;
FullElement: PROCEDURE [qe: pQE] RETURNS [BOOLEAN] = {OPEN qe;
RETURN[(IF rear < front THEN maxLen + rear - front ELSE rear - front) = maxLen - 1]};
END....