/*   buffdesc.h,  /atalk-ii/ins,  Garth Conboy,  03/29/92  */
/*   Copyright (c) 1992 by Pacer Software Inc., La Jolla, CA  */

/*   GC - Initial coding.
     GC - (05/20/92): Added support for opaque data descriptors.
     GC - (06/24/92): More major changes: a single BufferDescriptor chunk
                      may now contain both out-board and on-board data.
     GC - (06/30/92): Added holders for transmit completion info in the
                      BufferDescriptor.

     *** Make the PVCS source control system happy:
     $Header$
     $Log$
     ***

     Data structure and routine declarations for transmit buffer lists.

     In order to avoid buffer copies when prepending protocol and data
     link headers to outgoing datagrams we use a buffer chaining
     technique.  The most all send routines take "BufferDescriptor"s
     rather than "data buffer"s as arguments; this allows these routines to
     logically prepend headers without moving the buffers around.  These
     BufferDescriptors are then passed down to actual packet out rotuines
     in Depend.c.

     Each buffer chunk in a BufferDescriptor chain is capable of holding,
     describing or pointing-to two types of data: an out-board buffer
     of some type (either "char *" or opaque, as described later) and an
     on-board data buffer.  A single "chunk" can contain both, in which
     case the on-board data is prepended to the out-board data; that is,
     the on-board data is treated as a header for the out-board data.

     One core assumption is that there is some sort of "gather send"
     capability is provided by the underlying hardware. If no such "gather
     send" capability exists, multiple-chunk-buffer-descriptors will
     need to be coalesced into a single chunk before being passed down to
     the actual hardware send routines.

     This, above mentioned, coalescing, could be a real performance problem
     in a router that does not support gather send.  For this reason there
     is a "prependInPlace" flag kept in the BufferDescriptor.  If this flag
     is set, requests to AllocateHeader will NOT create a new link in the
     buffer chain, but WILL simply move the exisitng pointer BACKWARDS in
     memory and increment the known size of the existing chunk!  Thus, it is
     assumed that there exists preceding space sufficient to hold the
     requested header(s).  WATCH OUT FOR THIS!

     It is likely that the router will set the "prependInPlace" flag, because
     there is a slight performance win even if gather send is supported (less
     allocating and freeing, only a single chunk to transmit).  If this is
     being done, incoming packets MUST have sufficient space allocated
     preceding the Ddp datagram for the incoming header to be replaced with
     a new (and possibly longer) Ddp, 802.2 and data link headers.  Keep in
     mind Token Ring source routing... that can take alot of header space!

     One other note on "prependInPlace": each BufferDescriptor has a certain
     amount of on-board buffer space, if a new header (a call to
     AllocateHeader) can be fit into this space we will do it regardeless of
     the setting of the "prependInPlace" flag.
*/

/* We allow small buffers to be stored internal to the buffer descriptor
   to reduce memory thrashing for things such as Atp header & user bytes.
   This functionality can be disabled by defining MaxOnBoardBytes to be
   zero (0). */

#if Iam a WindowsNT
  #define MaxOnBoardBytes (MaximumHeaderLength + LongDdpHeaderLength + 4 + 4)
                                       /* Max link hdr + long Ddp hdr +
                                          Atp hdr + Atp user bytes */
#else
  #define MaxOnBoardBytes (4 + 4)      /* ATP header & user bytes */
#endif

/* The out-board data buffer described by each chunk in a BufferDescriptor
   chain can be of one of two types: either an actual data buffer (char *)
   or an opaque "data descriptor" in some system dependent format (void *).
   In the first case, the data may be a data buffer dynamically
   allocated by the BufferDescriptor routines, or it may be a "user buffer"
   that a BufferDescriptor was created to describe.

   In the second case, the buffer descriptor was simply created to describe a
   a system-dependent "user buffer" (or buffer chain, maybe) that the portable
   code will never actually touch... it will just be an entry in the buffer
   chain that is eventually passed down to the "depend()" routines for
   transmission.  "outBoardSize" will always reflect the actual (total) length
   of this opaque entity.

   The flag "opaqueData" is used to distinguish which of the two cases apply
   to each BufferDescriptor in a chain; if the flag is True, we have the
   second case; if the flag is False, we have the first case.

   If we need to build a BufferDescriptor for less than all of an incoming
   "opaque data descriptor" we will call a system dependent routine build
   one of these "opaque data descriptors" for the size that we need.  In this
   case, this newly created "opaque data descriptor" might need to be freed
   when the transmit completes; to signify this we use the
   "freeOpaqueDataDescriptor" flag.

   A note on the out-board data buffer (the first case, above):
   "outBoardAllocatedSize" always is the actual allocated size of the data
   buffer; "outBoardSize" is the "in use" size of the data buffer;
   "outBoardBuffer" always points to the start of the buffer; "outBoardData"
   points to the start of the "in use" data.  In the second case (above;
   opaque data) "outBoardAllcoatedSize" and "outBoardSize" will always be
   the same, and "outBoardBuffer" and "outBoardData" will always be the
   same (the start of the opaque thing or its system dependent descriptor).

   This same set of rules basically holds for the on-board data except that
   the "allocated" size is fixed (at MaxOnBoardBytes); "outBoardSize" is the
   actual "in use" size of the on-board data buffer; the "onBoardData"
   pointer fills the role of "outBoardData," pointing at the "in use" portion
   of the on-board data buffer: "onBoardBuffer."

   */

typedef struct buffDesc { struct buffDesc far *next;
                                                 /* Next chunk in list. */
                          int outBoardDataValid : 1,
                                                 /* Does this chunk contain
                                                    valid out-board data? */
                              onBoardDataValid : 1,
                                                 /* Does this chunk contain
                                                    valid on-board data? */
                              opaqueData : 1,    /* If outBoardDataValid,
                                                    is the data "opaque" or
                                                    is it a real "char *"
                                                    buffer?  See the above
                                                    comments. */
                              freeData : 1,      /* When this chunk is freed,
                                                    should the non-opaque out-
                                                    data buffer also be freed?
                                                    Valid only if outBoardData-
                                                    Valid and Not opaqueData. */
                              prependInPlace : 1,
                                                 /* See above comments.  Valid
                                                    only if outBoardDataValid
                                                    and Not opaqueData. */
                              freeOpaqueDataDescriptor : 1,
                                                 /* See above comments, valid
                                                    only if outBoardDataValid
                                                    and opaqueData is True. */
                              dummmyLastFlag : 1;
                          long outBoardSize;     /* Size, in bytes, of the
                                                    out-board data in this
                                                    chunk.  Valid only if
                                                    outBoardDataValid is
                                                    True. */
                          long outBoardAllocatedSize;
                                                 /* The size, in bytes, of the
                                                    space actually allocated for
                                                    for the out-board buffer;
                                                    will be great than or equal
                                                    to outBoardSize. */
                          long onBoardSize;      /* Size, in bytes, of the
                                                    on-board data in this
                                                    chunk.  Valid only if
                                                    onBoardDataValid is
                                                    True. */
                          char far *data;        /* The actual "write position"
                                                    within this chunk; where
                                                    the newest alloacted header
                                                    should be filled in.  This
                                                    should be set to either
                                                    outBoardData or onBoardData
                                                    depending on where the most
                                                    "recent" header was
                                                    allocated.  Note that we
                                                    will never directly write
                                                    into opaque data. */
                          char far *outBoardBuffer;
                                                 /* Data for this chunk: a
                                                    real "char *" buffer if
                                                    opaqueData is False;
                                                    an "opaque thing" if
                                                    opaqueData is True;
                                                    This pointer SHOULD NOT be
                                                    modified.  Only valid if
                                                    "outBoardDataValid" is
                                                    True.*/
                          char far *outBoardData;
                                                 /* The start of "in use"
                                                    portion of the out-board
                                                    data, generally it will be
                                                    the same as "outBoard-
                                                    Buffer," but if only a
                                                    subsequent portion is
                                                    used, it will point to the
                                                    start location.  This will
                                                    always be the same as
                                                    "outBoardBuffer" if
                                                    "opaqueData" is True.
                                                    Only valid if
                                                    "outBoardDataValid" is
                                                    True. */
                          char far *onBoardData; /* The start of the "in use"
                                                    part of "onBoardBuffer;"
                                                    only valid if
                                                    "onBoardDataValid" is
                                                    True. */
                          char onBoardBuffer[MaxOnBoardBytes];
                                                 /* On board data for smallish
                                                    chunks. */
                          TransmitCompleteHandler *transmitCompleteHandler;
                                                 /* If the higher levels want
                                                    transmit complete
                                                    notification, we fill in
                                                    the routine here. */
                          long unsigned userData;
                                                 /* User data for the above
                                                    call. */
                        } far *BufferDescriptor;

/* To futher reduce memory thrashing we keep a list of free buffer descriptors.
   These may, or may not, have a valid (non-on-board) data buffer along with
   them.  The maximum non-on-board data buffer that we will keep around is
   as follows.  It's large enough to hold the largest header that will need
   to be prepended to a Ddp datagram.

   If this size is smaller than MaxOnBoardBytes, this functionality will
   efectively be disabled. */

#define MaxKeptBufferBytes (MaximumHeaderLength + LongDdpHeaderLength)

/* The list of free buffer descriptrs is anchored here.  Note that if we free
   a buffer descriptor and the data is free-able and larger than
   MaxKeptBufferBytes, we will free the data and set the out-board size to
   zero.  The buffer descriptor itself could then be reused.  We will also
   do this when we free a buffer descriptor that describes memory that is
   not ours (i.e. a buffer descriptor for user suppiled buffering; the
   freeData bit was reset when the free was requested). */

#ifndef InitializeData
  extern
#endif
BufferDescriptor freeBufferDescriptorList;

#ifndef InitializeData
  extern
#endif
int freeBufferDescriptorCount;

/* We will keep maximum of the following number of buffer descriptors on the
   above list. */

#define MaxFreeBufferDescriptors 30

/* Allocate and attach a new header to an existing buffer chain.  The data
   will be marked as free-able. */

extern BufferDescriptor far AllocateHeader(BufferDescriptor chain,
                                           long size);

/* Attach an existing chunk of data to an exisitng buffer chain.  The data
   will be marked as non-free-able. */

extern BufferDescriptor far AttachHeader(BufferDescriptor chain,
                                         long size,
                                         char far *data,
                                         Boolean opaqueData);

/* Free a buffer chain and its assocciated free-able data. */

extern void far FreeBufferChain(BufferDescriptor chain);

/* Compute the actual, in use, size of a buffer chain. */

extern long far SizeOfBufferChain(BufferDescriptor chain);

/* Compute the Ddp checksum of a buffer chain. */

extern short unsigned far DdpChecksumBufferChain(BufferDescriptor chain,
                                                 long actualLength,
                                                 long leadingBytesToSkip);

/* Given a buffer descriptor, adjust the most recent header to reflect new
   "in use" pointers and sizes.  A negative adjustment cause less data to
   be considered "in use," a positive adjustment causes more data to be
   considered "in use." */

extern void far AdjustBufferDescriptor(BufferDescriptor descriptor,
                                       long adjustment);

/* Given a multi-chunk buffer chain, coalesce it into a single chunk
   chain and free the orignal. */

extern BufferDescriptor far CoalesceBufferChain(BufferDescriptor chain);

/* Create a copy of a buffer descriptor (basically the same as
   CoalesceBufferChain, but don't free the orignal). */

extern BufferDescriptor far CopyBufferChain(BufferDescriptor chain);

/* Given a buffer descriptor chain, create a new one describing a subset of
   the original.  This routine generally does not copy any data, so the
   original buffer descriptor may not be freed before the subset descriptor
   is freed. */

extern BufferDescriptor far SubsetBufferDescriptor(BufferDescriptor chain,
                                                   long offset, long size);

/* Move a specified amount of data from "out-board data" to a "char *"
   buffer.  This routine handles both "char *" and "opaque" out-board
   data. */

extern void far MoveOutBoardData(char far *target, BufferDescriptor chunk,
                                 long offset, long size);

/* Move opaque data to a "char *" buffer. */

extern void far MoveFromOpaque(char far *target, void far *opaque,
                               long offset, long size);

/* Move a "char *" buffer to opaque data. */

extern void far MoveToOpaque(void far *opaque, long offset,
                             char far *buffer, long size);

/* Move an opaque buffer to an opaque buffer.  Overlapping areas should
   work "correctly" this routine is used to "synch-up" Atp responses. */

extern void far MoveOpaqueToOpaque(void far *targetOpaque, long targetOffset,
                                  void far *sourceOpaque, long sourceOffset,
                                  long size);

/* The "strlen()" function for opaque data. */

extern size_t far StrlenOpaque(void far *opaque, long offset);

/* Make an opaque data descriptor for a specified "char *" buffer.  The
   last argument is returned as True if the returned descriptor is a "real
   thing" that must be freed when we're done with it (this refers to the
   descriptor, not the described data). */

extern void far *MakeOpaqueDataDescriptor(char far *buffer, long size,
                                          Boolean far
                                                 *freeOpaqueDataDescriptor);

/* Given an opaque data descriptor, create a new opaque data descriptor that
   describes a subset of the original -- leave the original unmodified. The
   last argument is returned as True if the returned descriptor is a "real
   thing" that must be freed when we're done with it (this refers to the
   descriptor, not the described data). */

extern void far * far SubsetOpaqueDataDescriptor(void far *master,
                                                 long offset,
                                                 long size,
                                                 Boolean far
                                                    *freeOpaqueDataDescriptor);

/* Free and opaque data descriptor; this frees the descriptor NOT the data.
   This routine will be used to free descriptors that were created by the
   stack when "subsetting" passed opaque data descriptors. */

extern void far FreeOpaqueDataDescriptor(void far *descriptor);

/* Allocate a new buffer chunk.  The data will be marked as free-able. */

#define NewBufferDescriptor(size) AllocateHeader(Empty, size)

/* Create a buffer chunk for an existing chunk of data.  The data will be
   marked as non-free-able. */

#define DescribeBuffer(size, data, opaque) AttachHeader(Empty, size,   \
                                                        data, opaque)