PupBuffer.mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Hal Murray, June 2, 1986 11:18:46 am PDT
DIRECTORY
Basics USING [BITAND, bytesPerWord],
Endian USING [bytesPerFWord, bytesPerHWord, FWORD, HWORD],
CommBuffer USING [Overhead],
Pup USING [Address, Host, Net],
PupType USING [bytesInPupHeader, bytesOfPupOverhead, ErrorCode, HeaderWithoutChecksum, maxNewGatewayBytes, maxOldGatewayBytes, Type];
PupBuffer: CEDAR DEFINITIONS
IMPORTS Basics = {
BYTE: TYPE = [0..100H);
HWORD: TYPE = Endian.HWORD;
FWORD: TYPE = Endian.FWORD;
Buffer size constants
maxOldGatewayBytes: NAT = PupType.maxOldGatewayBytes;
maxNewGatewayBytes: NAT = PupType.maxNewGatewayBytes;
maxDataBytes: NAT = 1500-PupType.bytesOfPupOverhead;
This is what the type checker uses for things that live in our buffers. Gateways currently support 532. Making this bigger is the first step towards making things go much faster. "1500" is really Driver.dataBytesInBuffer, but we don't want that compilation dependency.
Beware: round down to maxXxxGatewayBytes if you are going through a gateway.
maxDataHWords: NAT = maxDataBytes/Endian.bytesPerHWord;
maxDataFWords: NAT = maxDataBytes/Endian.bytesPerFWord;
ByteAlloc: TYPE = [0..maxDataBytes);
HWordAlloc: TYPE = [0..maxDataHWords);
FWordAlloc: TYPE = [0..maxDataFWords);
ByteIndex: TYPE = [0..maxDataBytes];
HWordIndex: TYPE = [0..maxDataHWords];
FWordIndex: TYPE = [0..maxDataFWords];
Including these types here means we have to recompile a whole lot to experiment with bigger buffers. Using these types lets the Compiler avoid a lot of bounds checking.
StringIndex: TYPE = [0..maxDataBytes-stringOverheadBytes);
AbortIndex: TYPE = [0..maxDataBytes-abortOverheadBytes);
ErrorIndex: TYPE = [0..maxDataBytes-errorOverheadBytes);
Buffer layout
Buffer: TYPE = REF BufferObject;
Do not NEW your own BufferObjects.
PupBufferPointer: TYPE = LONG POINTER TO BufferObject; -- Needed by Stupid Debugger
BufferObject: TYPE = MACHINE DEPENDENT RECORD [
ovh: CommBuffer.Overhead,
Next is a copy of the Header defined in PupType. If we used that directly you would have to say b.pup.type instead of just b.type.
byteLength: HWORD, -- includes header and software checksum
hopCount: [0..16),
spares: [0..16),
type: PupType.Type,
id: FWORD,
dest, source: Pup.Address,
body: SELECT OVERLAID * FROM
bytes => [ byte: PACKED ARRAY ByteAlloc OF BYTE ],
chars => [ char: PACKED ARRAY ByteAlloc OF CHAR ],
hWords => [ hWord: ARRAY HWordAlloc OF HWORD ],
fWords => [ fWord: ARRAY FWordAlloc OF FWORD ],
string => [ string: RECORD [
length: HWORD,
maxLength: HWORD,
text: PACKED ARRAY StringIndex OF CHAR ] ],
rfc => [ address: Pup.Address ],
ack => [
maxBytesPerPup: HWORD,
maxPupsAhead: HWORD,
maxBytesAhead: HWORD],
abort => [ abort: RECORD [
code: HWORD,
text: PACKED ARRAY AbortIndex OF CHAR ] ],
error => [ error: RECORD [
header: PupType.HeaderWithoutChecksum,
code: PupType.ErrorCode,
options: HWORD,
text: PACKED ARRAY ErrorIndex OF CHAR ] ],
addresses => [ addresses: ARRAY AddressAlloc OF Pup.Address ],
echoStats => [ echoStats: EchoStatsResponse],
fileLookupReply => [ fileLookupReply: FileLookupReply ],
routing => [ routing: ARRAY RoutingEntryAlloc OF RoutingInfoResponse ],
time => [ time: TimeResponse ],
ENDCASE
software checksum (1 HWORD) comes after all the data
];
Software checksums
noChecksum: Endian.HWORD = 0FFFFH;
RoundUpForChecksum: PROC [bytes: NAT] RETURNS [NAT] = INLINE {
RETURN[Basics.BITAND[(bytes+1), 0FFFEH]]; };
Round up to include the garbage byte if this is an odd length packet.
WordsWithoutChecksum: PROC [bytes: NAT] RETURNS [words: NAT] = INLINE {
RETURN[(bytes-1)/Endian.bytesPerHWord]; };
Total words is (bytes+2-1)/2, but we want 1 less than that.
Length calculations for error/abort Pups (and string body)
stringOverheadBytes: NAT = 2*Endian.bytesPerHWord;
abortOverheadBytes: NAT = 1*Endian.bytesPerHWord;
errorOverheadBytes: NAT =
PupType.bytesInPupHeader +
SIZE[PupType.ErrorCode, Basics.bytesPerWord] +
1*Endian.bytesPerHWord;
Record layouts for bodys of Pups
This is a collection of all the record layouts used in raw Pups. Collecting so many things here adds to the recompilation problems if anything changes but it avoids LOOPHOLEs and collects these specifications as a documentation aid.
Bugs or oversights can be fixed with a LOOPHOLE. That's actually reasonably safe if you consistently include something like the following right after you get a buffer. (Beware of NIL faults.)
foo: LONG POINTER TO Record ← LOOPHOLE[@b.body];
The following sections are sorted alphabetically, but you have to guess which keyword to use.
Addresses - response from name server
bytesPerAddress: NAT = SIZE[Pup.Address, Basics.bytesPerWord];
maxAddresses: NAT = maxOldGatewayBytes/bytesPerAddress;
AddressAlloc: TYPE = [0..maxAddresses);
Echo server - statistics
echoStatsVersion: NAT = 1;
EchoStatsResponse: TYPE = MACHINE DEPENDENT RECORD [
version: HWORD,
pupsEchoed: FWORD ];
File Lookup
FileLookupReply: TYPE = MACHINE DEPENDENT RECORD [
version: HWORD,
createTime: FWORD,
length: FWORD ];
Routing info
bytesPerRoutingInfoResponse: NAT = SIZE[RoutingInfoResponse, Basics.bytesPerWord];
maxRoutingEntrys: NAT = maxOldGatewayBytes/bytesPerRoutingInfoResponse;
RoutingEntryAlloc: TYPE = [0..maxRoutingEntrys);
RoutingInfoResponse: TYPE = MACHINE DEPENDENT RECORD [
net: Pup.Net, viaNet: Pup.Net, viaHost: Pup.Host, hop: BYTE ];
Time server
TimeResponse: TYPE = MACHINE DEPENDENT RECORD [
time: FWORD,
direction: { west(0), east(1) },
zone: [0..127],
zoneMinutes: [0..255],
beginDST, endDST: HWORD ];
}.