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