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