-- File: CourierImplC.mesa - last edit:
-- AOF 12-Mar-87 11:24:40
-- Copyright (C) 1984, 1985 , 1986, 1986, 1987, 1987, 1987, 1987 by Xerox Corporation. All rights reserved.
DIRECTORY
Courier USING [
Description, Error, InvalidArguments, NoteSize, NoteLongCardinal, NoteString,
NoteArrayDescriptor, NoteParameters, NoteChoice, NoteDisjointData, NoteBlock,
NotesObject, Parameters, SystemElement, NoteSpace, NoteDeadSpace],
CourierInternal USING [ConnectionObject, doStats, StatType, ExchWords,
ConnectionHandle],
CourierOps USING [
Block, stackBlockLength, StackBlockPush, StackBlockPop, ceiling, floor,
StoreHandle, FetchHandle, FreeHandle, StackHandle, StackObject, StackBase,
stackObjectLimit, StackBlockHandle, NotesObject],
CourierProtocol USING [dataSST],
Environment USING [Byte, bytesPerWord],
Heap USING [systemZone],
Inline USING [LongCOPY, LowHalf],
NetworkStream USING [closeSST],
NSConstants USING [courierSocket],
Process USING [Abort, GetCurrent],
Router USING [FindDestinationRelativeNetID, FindMyHostID],
Stream USING [Block, CompletionCode, Handle, SubSequenceType, TimeOut],
System USING [nullNetworkAddress, nullNetworkNumber];
CourierImplC: PROGRAM
IMPORTS
Courier, CourierInternal, CourierOps, Inline, Heap,
Process, Router, Stream
EXPORTS Courier, CourierInternal, CourierOps =
BEGIN
stats: PUBLIC ARRAY CourierInternal.StatType OF LONG CARDINAL ← ALL[0];
localSystemElement: PUBLIC Courier.SystemElement;
nullString: LONG STRING ← [0]; --this is shared by all clients!!!
Tag: TYPE = CARDINAL;
bpw: CARDINAL = Environment.bytesPerWord;
Rawdata: TYPE = RECORD[SEQUENCE COMPUTED CARDINAL OF WORD];
LongDescriptor: TYPE = LONG DESCRIPTOR FOR ARRAY OF UNSPECIFIED;
Closed: PUBLIC ERROR = CODE;
DeserializeParameters: PUBLIC PROC[
parameters: Courier.Parameters, sH: Stream.Handle, zone: UNCOUNTED ZONE] =
BEGIN
<<
Basically the same as Call results except the client has provided
the stream. Available to both local and remote client.
>>
IF (parameters.location # NIL) AND (parameters.description # NIL) THEN
BEGIN
connect: client CourierInternal.ConnectionObject ← [
owner: Process.GetCurrent[], link: NIL, transFilter: LOOPHOLE[sH],
object: [
remote: System.nullNetworkAddress,
programNumber: 0, versionNumber: 0,
sH: sH, classOfService: bulk, zone: zone],
body: client[]];
Store[@connect, parameters.location, parameters.description !
Courier.InvalidArguments => GOTO translate];
END;
EXITS translate => ERROR Courier.Error[invalidArguments];
END; --DeserializeParameters
Free: PUBLIC PROC[parameters: Courier.Parameters, zone: UNCOUNTED ZONE] =
BEGIN
<<
Used to free results obtained from CallRemoteProcedure and to free
parameters in DeserializeParameters. Only storage elements allocated were
for strings and arrays. The former can be deallocated directly for they
are terminal. Arrays must be pushed and deallocated from a stack 'cause
they may themselves have substructures that allocate space.
>>
notes: CourierOps.NotesObject;
stack: CourierOps.FreeHandle ← NIL;
freeSize: Courier.NoteSize = {RETURN[parameters.location]};
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER];
freeLongNumber: PROC[site: LONG POINTER] = {};
freeSpace, freeDeadSpace: Courier.NoteSpace = {};
freeString: Courier.NoteString =
BEGIN
--PROC[site: LONG POINTER TO LONG STRING];
--Releases hyperspace store allocated for string.
--Beware of NIL strings.
IF (site↑ # NIL) AND (site↑ # nullString) THEN
BEGIN
zone.FREE[site];
IF CourierInternal.doStats THEN stats[zoneFreed] ← stats[zoneFreed] + 1;
END;
END; --freeString
freeArrayDescriptor: PROC[
site: LONG POINTER TO LongDescriptor, elementSize, upperBound: CARDINAL] =
BEGIN
--PROC[site: LONG POINTER, elementSize, upperBound: CARDINAL];
--Push entry to free hyperspace store allocated for array.
--Beware of NULL arrays.
IF (BASE[site↑] # NIL) AND (LENGTH[site↑] # 0) THEN
BEGIN
stack ← StackPush[stack];
stack↑ ← [free[LOOPHOLE[site], array]];
END;
END; --freeArrayDescriptor
freeChoice: Courier.NoteChoice = {};
freeParameters: Courier.NoteParameters =
--PROC[site: LONG POINTER, description: Description];
BEGIN
freeMinorSize: Courier.NoteSize = {RETURN[site]};
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
IF (site # NIL) AND (description # NIL) THEN
BEGIN
notes.object.noteSize ← freeMinorSize;
description[@notes.object]; --process the minor description
END;
END; --freeParameters
freeDisjointData: Courier.NoteDisjointData =
<<
PROC[site: LONG POINTER TO LONG POINTER, description: Description];
Push entry to free hyperspace store allocated for disjoint record.
Beware of NULLs.
>>
BEGIN
freeDisjointSize: Courier.NoteSize =
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
BEGIN
IF size # 0 THEN
BEGIN
stack ← StackPush[stack];
stack↑ ← [free[site, disjoint]];
END;
RETURN[site↑]; --of the disjoint, not the major description
END; --freeDisjointSize
IF (site # NIL) AND (description # NIL) AND (site↑ # NIL) THEN
BEGIN
notes.object.noteSize ← freeDisjointSize;
description[@notes.object]; --process the disjoint description
END;
END; --freeDisjointData
freeBlock: Courier.NoteBlock = {};
--Procedure body
IF (parameters.location # NIL) AND (parameters.description # NIL) THEN
BEGIN
notes.ch ← NIL;
notes.object ← [
zone, free, freeSize, freeLongNumber, freeLongNumber, freeParameters,
freeChoice, freeDeadSpace, freeString, freeSpace, freeArrayDescriptor,
freeDisjointData, freeBlock];
parameters.description[@notes.object ! UNWIND => Unstack[stack]];
UNTIL stack = NIL DO
zone.FREE[stack.p];
IF CourierInternal.doStats THEN
stats[zoneFreed] ← stats[zoneFreed] + 1;
stack ← StackPop[stack];
ENDLOOP;
END;
END; --Free
FlushToEndOfMessage: PUBLIC PROC[ch: CourierInternal.ConnectionHandle]
RETURNS[BOOLEAN] =
BEGIN
<<
Trying to find the end of a message. Return a BOOLEAN that is TRUE if
there is something wrong, FALSE if all appears ok. If we already have
the end, just return with FALSE.
>>
buffer: PACKED ARRAY [0..20) OF Environment.Byte;
bytes, totalBytes: CARDINAL ← 0;
status: Stream.CompletionCode;
IF ~ch.endRecord THEN
BEGIN
sH: Stream.Handle ← @ch.transFilter.object;
DO
[bytes, status, ] ← sH.get[sH, [@buffer, 0, LENGTH[buffer]], sH.options !
Stream.TimeOut, Courier.Error => --Can only be problem with stream.
GOTO exit];
totalBytes ← totalBytes + bytes;
SELECT status FROM
normal => NULL; --i.e., LOOP
endRecord => EXIT; --This is good if totalBytes = 0
ENDCASE => GOTO exit; --That will be bad
REPEAT exit => totalBytes ← 1;
ENDLOOP;
END;
IF CourierInternal.doStats THEN
stats[bytesFlushed] ← stats[bytesFlushed] + totalBytes;
RETURN[totalBytes # 0];
END; --FlushToEndOfMessage
GetAttention: PROC[sH: Stream.Handle] =
BEGIN
[] ← sH.getByte[sH]; --the inband notification
Process.Abort[Process.GetCurrent[]];
--UNTIL ABORTED-- DO
ENABLE ABORTED => EXIT;
[] ← sH.waitAttention[sH];
ENDLOOP;
END; --GetAttention
GetBlock: PUBLIC PROC[
ch: CourierInternal.ConnectionHandle, block: CourierOps.Block] =
BEGIN
bytes: CARDINAL;
attn: BOOLEAN ← FALSE;
sst: Stream.SubSequenceType;
status: Stream.CompletionCode;
total: CARDINAL ← block.stop - block.start;
sH: Stream.Handle ← @ch.transFilter.object;
DO --until bytes = total or exceptional condition
WITH h: ch SELECT FROM
client =>
BEGIN
[bytes, status, ] ← sH.get[sH, LOOPHOLE[block], sH.options];
IF CourierInternal.doStats THEN
stats[bytesReceived] ← stats[bytesReceived] + bytes;
IF bytes = total THEN EXIT; --status is secondary, transfer completed
IF status # normal THEN GOTO error; --this is probably superflous
END;
user, server =>
BEGIN
IF h.endRecord THEN GOTO error; --Can't continue past end of record
[bytes, status, sst] ← sH.get[sH, LOOPHOLE[block], sH.options];
IF CourierInternal.doStats THEN
stats[bytesReceived] ← stats[bytesReceived] + bytes;
<<
THE FOLLOWING SELECT STATEMENT IS FRAGILE! IT LOOKS TO BE COMPLICATED
AND IT IS. IT WAS WRITTEN SO THAT THE NORMAL AND HOPEFULLY DOMINANT
CASE IS THE VERY FIRST CHECK. THAT CHECK IS EQUIVALENT TO
(status = normal) AND (bytes = total) AND
(sst = CourierProtocol.dataSST)
>>
SELECT status FROM
normal =>
BEGIN
--(status = normal) implies (bytes = total)
SELECT sst FROM
CourierProtocol.dataSST => EXIT; --dominant case
ENDCASE; -- flush
END;
sstChange =>
BEGIN
--it changed to 'sst', wonder what it was before
SELECT TRUE FROM
--if new SST is dataSST, then what was the previous value?
(sst = CourierProtocol.dataSST) => NULL; -- flush previous
(sst = NetworkStream.closeSST) => ERROR Closed; --end of stream
(bytes = total) => EXIT; --no longer dataSST is ok
ENDCASE; -- flush
END;
endRecord, endOfStream =>
BEGIN
SELECT TRUE FROM
(sst # CourierProtocol.dataSST) => NULL; -- flush
(bytes = total) => {h.endRecord ← TRUE; EXIT}; --shouldn't happen
ENDCASE => {h.endRecord ← TRUE; GOTO error}; --(bytes # total)
END;
attention => GetAttention[sH]; --costly, heh?
ENDCASE => GOTO error; --???
END;
ENDCASE;
IF CourierInternal.doStats THEN
stats[bytesFlushed] ← stats[bytesFlushed] + bytes;
REPEAT error => ERROR Courier.Error[parameterInconsistency];
ENDLOOP;
END; --GetBlock
InRange: PROC[address, left, right: LONG POINTER]
RETURNS[BOOLEAN] = --INLINE--
BEGIN
Olp: TYPE = RECORD[p: LONG ORDERED POINTER];
{OPEN a: LOOPHOLE[address, Olp],
l: LOOPHOLE[left, Olp],
r: LOOPHOLE[right, Olp];
RETURN[a.p IN[l.p..r.p]]};
END; --InRange
LocalSystemElement: PUBLIC PROC RETURNS[Courier.SystemElement] =
BEGIN
IF localSystemElement.net = System.nullNetworkNumber THEN
BEGIN
localSystemElement.net ←
Router.FindDestinationRelativeNetID[System.nullNetworkNumber];
localSystemElement.host ← Router.FindMyHostID[];
localSystemElement.socket ← NSConstants.courierSocket;
END;
RETURN[localSystemElement];
END; --LocalSystemElement
PutBlock: PUBLIC PROC[
ch: CourierInternal.ConnectionHandle, block: CourierOps.Block] =
BEGIN
ch.transFilter.object.put[@ch.transFilter.object, LOOPHOLE[block], FALSE];
IF CourierInternal.doStats THEN
stats[bytesTransmitted] ← stats[bytesTransmitted] +
block.stop - block.start;
END; --PutBlock
Store: PUBLIC PROC[
ch: CourierInternal.ConnectionHandle, site: LONG POINTER,
description: Courier.Description] =
BEGIN
notes: CourierOps.NotesObject;
stack: CourierOps.StoreHandle ← NIL;
storeSize: Courier.NoteSize =
BEGIN
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER];
stack ← StackPush[stack];
stack↑ ← [store[site, site + size]];
RETURN[site];
END; --rSize
storeLongNumber: PROC[site: LONG POINTER TO LONG UNSPECIFIED] =
BEGIN
storeUnnoted[site];
GetBlock[ch, [site, 0, SIZE[LONG CARDINAL]*bpw]];
site↑ ← CourierInternal.ExchWords[site↑];
stack.left ← stack.left + SIZE[LONG CARDINAL];
END; --storeLongNumber
storeSpace: Courier.NoteSpace =
BEGIN
<<PROC [site: LONG POINTER, size: CARDINAL]
reads 'size' WORDs into 'space', consuming no portion of stack>>
GetBlock[ch, [site, 0, MIN[size, 77777B]*bpw]];
IF size > 77777B THEN GetBlock[ch, [site + 77777B, 0, size - 77777B*bpw]];
END; --storeSpace
storeDeadSpace: Courier.NoteDeadSpace =
BEGIN
<<
PROC [site: LONG POINTER, size: CARDINAL]
consumes 'size' from the stack starting at 'site'
>>
storeUnnoted[site]; --get any unnoted data in front of dead space
IF ~InRange[stack.left + size, stack.left, stack.right] THEN
ERROR Courier.Error[parameterInconsistency];
stack.left ← stack.left + size;
END; --storeDeadSpace
storeString: Courier.NoteString =
BEGIN
<<
PROC[site: LONG POINTER TO LONG STRING];
Allocates StringBody from hyperspace IFF length # 0 ELSE use nullString.
>>
length: CARDINAL;
storeUnnoted[site];
GetBlock[ch, [@length, 0, SIZE[CARDINAL]*bpw]];
IF length # 0 THEN
BEGIN
maxlength: CARDINAL = length + (length MOD bpw);
site↑ ← ch.object.zone.NEW[
StringBody [length] ← [length: length, maxlength: maxlength, text:]];
IF CourierInternal.doStats THEN
stats[zoneAllocated] ← stats[zoneAllocated] + 1;
GetBlock[ch, [site↑ + LONG[SIZE[StringBody]], 0, maxlength]];
END
ELSE site↑ ← nullString;
stack.left ← stack.left + SIZE[LONG STRING];
END; --storeString
storeArrayDescriptor: PROC[
site: LONG POINTER TO LongDescriptor, elementSize, upperBound: CARDINAL] =
BEGIN
<<
PROC[site: LONG POINTER, elementSize, upperBound: CARDINAL];
Fill clients descriptor with proper BASE and LENGTH.
Allocates storage for array from hyperspace IFF length # 0.
Creates a new stack entry even IFF the array length # 0
>>
length, size: CARDINAL;
storeUnnoted[site];
GetBlock[ch, [@length, 0, SIZE[CARDINAL]*bpw]];
IF length > upperBound THEN ERROR Courier.InvalidArguments;
size ← length*elementSize;
stack.left ← stack.left + SIZE[LongDescriptor];
IF length = 0 THEN site↑ ← DESCRIPTOR[NIL, 0]
ELSE
BEGIN
base: LONG POINTER TO Rawdata;
base ← ch.object.zone.NEW[Rawdata[size]];
base[0] ← 0; Inline.LongCOPY[@base[0], size - 1, @base[1]];
site↑ ← DESCRIPTOR[base, length];
stack ← StackPush[stack];
stack↑ ← [store[base, base + size]];
IF CourierInternal.doStats THEN
stats[zoneAllocated] ← stats[zoneAllocated] + 1;
END;
END; --storeArrayDescriptor
storeChoice: Courier.NoteChoice =
BEGIN
<<
PROC[site: LONG POINTER, size: CARDINAL, variant: LongDescriptor,
tag: LONG POINTER]
>>
storeUnnoted[site]; --process up to pointer to CHOICE data type
IF tag = NIL THEN tag ← site; --compatability feature
GetBlock[ch, [tag, 0, SIZE[Tag]*bpw]]; --the variant tag
IF tag↑ ~IN[0..variant.LENGTH) THEN ERROR Courier.InvalidArguments;
stack.left ← stack.left + size; --count the record as received
stack ← StackPush[stack];
stack↑ ← [store[tag + SIZE[Tag], site + variant[tag↑]]];
IF site # tag THEN
BEGIN --this is unnoted ahead of the tag
stack ← StackPush[stack];
stack↑ ← [store[site, tag]];
END;
END; --storeChoice
storeParameters: Courier.NoteParameters =
BEGIN
<<
PROC[site: LONG POINTER, description, Description];
Define "minor" portion of the parameter area.
>>
storeMinorSize: Courier.NoteSize =
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
BEGIN
--new parameter area must reside entirely inside old
IF ~InRange[site + size, stack.left, stack.right] THEN
ERROR Courier.Error[parameterInconsistency];
RETURN[site]; --of the minor, not the major description
END; --storeMinorSize
IF (site # NIL) AND (description # NIL) THEN
BEGIN
storeUnnoted[site]; --note up tp new left hand side
notes.object.noteSize ← storeMinorSize;
description[@notes.object]; --process the minor description
END;
END; --storeParameters
storeDisjointData: Courier.NoteDisjointData =
<<
PROC[site: LONG POINTER TO LONG POINTER, description, Description];
This consumes a portion of the "major" record even if NIL.
>>
BEGIN
storeDisjointSize: Courier.NoteSize =
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
BEGIN
--Allocate block of store IFF # 0 ELSE force pointer to NIL
IF size = 0 THEN site↑ ← NIL
ELSE
BEGIN
base: LONG POINTER TO Rawdata;
base ← ch.object.zone.NEW[Rawdata[size]];
base[0] ← 0; Inline.LongCOPY[@base[0], size - 1, @base[1]];
IF CourierInternal.doStats THEN
stats[zoneAllocated] ← stats[zoneAllocated] + 1;
stack ← StackPush[stack];
stack↑ ← [store[base, base + size]];
site↑ ← base;
END;
RETURN[site↑]; --of the disjoint, not the major description
END; --storeDisjointSize
storeUnnoted[site];
stack.left ← stack.left + SIZE[LONG POINTER];
IF (site # NIL) AND (description # NIL) THEN
BEGIN
notes.object.noteSize ← storeDisjointSize;
description[@notes.object]; --process the disjoint discription
END;
END; --storeDisjointData
<<
The block is completely disjoint (i.e., we consume no parameter area)
and we don't do an unnoted on entry. Its kinda like a NoteSpace. The
client is responsible for alignment and all that.
>>
storeBlock: Courier.NoteBlock = {GetBlock[ch, LOOPHOLE[block]]};
storeUnnoted: PROC[site: LONG POINTER] =
<<
Checks range of site against the stack, transmitting unnoted data
when required, and pruning the stack when possible.
Errors if stack is NIL on entry or NIL on exit and site # NIL.
>>
BEGIN
IF stack = NIL THEN ERROR Courier.Error[parameterInconsistency];
UNTIL stack = NIL DO
IF stack.left = stack.right THEN
BEGIN
--This block is already used; pop it and try again.
stack ← StackPop[stack];
LOOP;
END;
IF site = stack.left THEN RETURN; --we're already there
IF InRange[site, stack.left + 1, stack.right - 1] THEN
BEGIN
--New site is within this limit block, but unnoted data is first.
GetBlock[ch, [stack.left, 0, CARDINAL[(site - stack.left)]*bpw]];
stack.left ← site;
RETURN;
END;
--Site is not in this block; assume remainder of data in the block
--unnoted: transmit it, then set left = right so it will be pruned.
GetBlock[ch, [stack.left, 0, CARDINAL[(stack.right - stack.left)]*bpw]];
stack.left ← stack.right;
ENDLOOP;
IF site # NIL THEN ERROR Courier.Error[parameterInconsistency];
END; --storeUnnoted
notes.ch ← ch;
notes.object ← [
ch.object.zone, store, storeSize, storeLongNumber,storeLongNumber,
storeParameters, storeChoice, storeDeadSpace, storeString,
storeSpace, storeArrayDescriptor, storeDisjointData, storeBlock];
BEGIN
ENABLE UNWIND => Unstack[stack];
description[@notes.object];
storeUnnoted[NIL]; --Get any bytes left in stream
END;
END; --Store
SerializeParameters: PUBLIC PROC[
parameters: Courier.Parameters, sH: Stream.Handle--, zone: UNCOUNTED ZONE--] =
BEGIN
<<
This is basically the same as Call except the client has supplied the
stream.
Note: Next release, change Courier.mesa to require a zone for this proc.
>>
IF (parameters.location # NIL) AND (parameters.description # NIL) THEN
BEGIN
connect: client CourierInternal.ConnectionObject ← [
owner: Process.GetCurrent[], link: NIL, transFilter: LOOPHOLE[sH],
object: [
remote: System.nullNetworkAddress,
programNumber: 0, versionNumber: 0,
sH: sH, classOfService: bulk,
zone: Heap.systemZone],
body: client[]];
Fetch[@connect, parameters.location, parameters.description !
Courier.InvalidArguments => GOTO translate];
END;
EXITS translate => ERROR Courier.Error[invalidArguments];
END; --SerializeParameters
StackPop: PROC[stack: CourierOps.StackHandle] RETURNS[LONG POINTER] =
BEGIN
<<
To pop an element is simple. Especially of the block isn't empty.
Just subtract the element length from the current value.
But, if the block is empty, pop another block and return the address
of the last element in the block or NIL if there is no next block.
>>
OPEN OPS: CourierOps; --only used on an INLINE
block: CourierOps.StackBlockHandle;
relative: INTEGER ← Inline.LowHalf[stack] MOD CourierOps.stackBlockLength;
RETURN[SELECT TRUE FROM
(relative # CourierOps.floor) => stack - SIZE[CourierOps.StackObject],
((block ← CourierOps.StackBlockPop[OPS.StackBase[stack]]) = NIL) => NIL,
ENDCASE => @block.element[CourierOps.stackObjectLimit - 1]];
END; --StackPop
StackPush: PROC[stack: CourierOps.StackHandle] RETURNS[LONG POINTER] =
BEGIN
OPEN OPS: CourierOps;
StackFull: PROC[stack: OPS.StackHandle] RETURNS[BOOLEAN] = INLINE
{RETURN[(Inline.LowHalf[stack] MOD OPS.stackBlockLength) = OPS.ceiling]};
SELECT TRUE FROM
(stack = NIL) =>
stack ← @CourierOps.StackBlockPush[OPS.StackBase[stack]].element[0];
(StackFull[stack ← stack + SIZE[CourierOps.StackObject]]) =>
stack ← @CourierOps.StackBlockPush[OPS.StackBase[stack]].element[0];
ENDCASE; --side effect of previous case arm will be returned
RETURN[stack];
END; --StackPush
Fetch: PUBLIC PROC[
ch: CourierInternal.ConnectionHandle, site: LONG POINTER,
description: Courier.Description] =
BEGIN
notes: CourierOps.NotesObject;
stack: CourierOps.FetchHandle ← NIL;
fetchSize: Courier.NoteSize =
BEGIN
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER];
stack ← StackPush[stack];
stack↑ ← [fetch[site, site + size]];
RETURN[site];
END; --fetchSize
fetchLongNumber: PROC[site: LONG POINTER TO LONG UNSPECIFIED] =
BEGIN
long: LONG CARDINAL;
fetchUnnoted[site];
long ← CourierInternal.ExchWords[site↑];
PutBlock[ch, [@long, 0, SIZE[LONG CARDINAL]*bpw]];
stack.left ← stack.left + SIZE[LONG CARDINAL];
END; --fetchLongNumber
fetchSpace: Courier.NoteSpace =
<<PROC [site: LONG POINTER, size: CARDINAL]
reads 'size' WORDs into 'space', consuming no portion of stack>>
BEGIN
PutBlock[ch, [site, 0, MIN[size, 77777B]*bpw]];
IF size > 77777B THEN
PutBlock[ch, [site + 77777B, 0, (size - 77777B)*bpw]];
END; --fetchSpace
fetchDeadSpace: Courier.NoteDeadSpace =
<<
PROC [site: LONG POINTER, size: CARDINAL]
consumes 'size' from the stack starting at 'site'
>>
BEGIN
fetchUnnoted[site]; --get any unnoted data in front of dead space
IF ~InRange[stack.left + size, stack.left, stack.right] THEN
ERROR Courier.Error[parameterInconsistency];
stack.left ← stack.left + size;
END; --fetchDeadSpace
fetchString: Courier.NoteString =
BEGIN
<<
PROC[site: LONG POINTER TO LONG STRING];
Beware of NIL strings and zero length strings.
Note: This may modify user's data on odd length strings.
>>
length: CARDINAL ← IF site↑ # NIL THEN site↑.length ELSE 0;
fetchUnnoted[site];
PutBlock[ch, [@length, 0, SIZE[CARDINAL]*bpw]];
IF length # 0 THEN
BEGIN
maxlength: CARDINAL = length + (length MOD bpw);
IF (length # maxlength) THEN site↑[length] ← 0C;
PutBlock[ch, [site↑ + SIZE[StringBody], 0, maxlength]];
END;
stack.left ← stack.left + SIZE[LONG STRING];
END; --fetchString
fetchArrayDescriptor: PROC[
site: LONG POINTER TO LongDescriptor, elementSize, upperBound: CARDINAL] =
BEGIN
<<
Outputs the length of the array as a 16 bit CARDINAL.
The old stack.left is updated by the length of the DESCRIPTOR
Creates a new stack entry IFF the array length # 0 and BASE # NIL.
>>
base: LONG POINTER ← BASE[site↑];
length: CARDINAL ← IF base = NIL THEN 0 ELSE LENGTH[site↑];
IF length > upperBound THEN ERROR Courier.InvalidArguments;
fetchUnnoted[site];
PutBlock[ch, [@length, 0, SIZE[CARDINAL]*bpw]];
stack.left ← stack.left + SIZE[LongDescriptor];
IF length # 0 THEN
BEGIN
stack ← StackPush[stack];
stack↑ ← [fetch[base, base + length*elementSize]];
END;
END; --fetchArrayDescriptor
fetchChoice: Courier.NoteChoice =
BEGIN
<<
PROC[site: LONG POINTER, size: CARDINAL, variant: LongDescriptor,
tag: LONG POINTER]
>>
fetchUnnoted[site]; --transmit up to leading edge
IF tag = NIL THEN tag ← site; --compatability feature
PutBlock[ch, [tag, 0, SIZE[Tag]*bpw]]; --the variant tag
IF tag↑ ~IN[0..variant.LENGTH) THEN ERROR Courier.InvalidArguments;
stack.left ← stack.left + size; --consume undescriminated record size
stack ← StackPush[stack];
stack↑ ← [fetch[tag + SIZE[Tag], site + variant[tag↑]]];
IF site # tag THEN
BEGIN --this is unnoted ahead of the tag
stack ← StackPush[stack];
stack↑ ← [fetch[site, tag]];
END;
END; --fetchChoice
fetchParameters: Courier.NoteParameters =
BEGIN
<<
PROC[site: LONG POINTER, description, Description];
Define "minor" portion of the parameter area.
>>
fetchMinorSize: Courier.NoteSize =
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
BEGIN
--new parameter area must be entirely contained in old one
IF ~InRange[site + size, stack.left, stack.right] THEN
ERROR Courier.Error[parameterInconsistency];
RETURN[site]; --of the minor, not the major description
END; --fetchMinorSize
IF (site # NIL) AND (description # NIL) THEN
BEGIN
fetchUnnoted[site]; --note up to new left hand side
notes.object.noteSize ← fetchMinorSize; --replace size routine in object
description[@notes.object]; --process the minor description
END;
END; --fetchParameters
fetchDisjointData: Courier.NoteDisjointData =
BEGIN
<<
PROC[site: LONG POINTER TO LONG POINTER, description, Description];
This does consume a portion of the "major" record even if it is NIL.
>>
fetchDisjointSize: Courier.NoteSize =
--PROC[size: CARDINAL] RETURNS[location: LONG POINTER]
BEGIN
IF size # 0 THEN
BEGIN
stack ← StackPush[stack];
stack↑ ← [fetch[site↑, site↑ + size]];
END;
RETURN[site↑]; --of the disjoint, not the major description
END; --fetchDisjointSize
fetchUnnoted[site]; --process data up to minor record left edge
stack.left ← stack.left + SIZE[LONG POINTER];
IF (site # NIL) AND (description # NIL) THEN
BEGIN
notes.object.noteSize ← fetchDisjointSize;
description[@notes.object]; --process the minor description
END;
END; --fetchDisjointData
<<
The block is completely disjoint (i.e., we consume no parameter area)
and we don't do an unnoted on entry. Its kinda like a NoteSpace. The
client is responsible for alignment and all that.
>>
fetchBlock: Courier.NoteBlock = {PutBlock[ch, LOOPHOLE[block]]};
fetchUnnoted: PROC[site: LONG POINTER] =
BEGIN
--Checks range of site against the stack, transmitting unnoted data
--when required, and pruning the stack when possible.
--Errors if stack is NIL on entry and on exit if stack = NIL and site # NIL.
IF stack = NIL THEN ERROR Courier.Error[parameterInconsistency];
UNTIL stack = NIL DO
IF stack.left = stack.right THEN
BEGIN
--This limit block is already used; pop it and try again.
stack ← StackPop[stack];
LOOP;
END;
IF site = stack.left THEN RETURN; --we're already there
IF InRange[site, stack.left + 1, stack.right - 1] THEN
BEGIN
--New site is within this limit block, but unnoted data is first.
--Assumption is that length to send is less than 16 bits.
PutBlock[ch, [stack.left, 0, CARDINAL[(site - stack.left)]*bpw]];
stack.left ← site;
RETURN;
END;
--Site is not in this block; assume remainder of data in the block
--unnoted; transmit it, then set left = right so it will be pruned.
--Assumption is that lenth to send is less than 16 bits.
PutBlock[ch, [stack.left, 0, CARDINAL[(stack.right - stack.left)]*bpw]];
stack.left ← stack.right;
ENDLOOP;
IF site # NIL THEN ERROR Courier.Error[parameterInconsistency];
END; --fetchUnnoted
notes.ch ← ch;
notes.object ← [
ch.object.zone, fetch, fetchSize, fetchLongNumber,
fetchLongNumber, fetchParameters, fetchChoice, fetchDeadSpace,
fetchString, fetchSpace, fetchArrayDescriptor, fetchDisjointData,
fetchBlock];
BEGIN
ENABLE UNWIND => Unstack[stack];
description[@notes.object];
fetchUnnoted[NIL]; --Flush remainder of data as if unnoted.
END;
END; --Fetch
Unstack: PROC[stack: CourierOps.StackHandle] =
BEGIN
--This is the routine called by the UNWIND catchers to free stacks.
UNTIL stack = NIL DO stack ← StackPop[stack]; ENDLOOP;
END; --Unstack
localSystemElement.net ← System.nullNetworkNumber;
[] ← LocalSystemElement[];
END..... -- of CourierImplC.mesa
-- MODIFICATION LOG:
17-Jun-85 18:27:05 AOF Post Yuba.
15-Oct-86 16:26:37 AOF Check for left == site first in Unnoted routines.
7-Jan-87 17:38:56 AOF MDS Relief tweeks (nullString => LONG STRING).
12-Mar-87 11:24:01 AOF Fix AR #10344 (checking tag value against variant DESC).