XDR Stream Implementation
This section provides the abstract data types needed to implement new instances of XDR streams.
XDR Object
The structure in the following code example defines the interface to an XDR stream.
Example A-13 XDR Stream Interface Example
The x_op field is the current operation being performed on the stream. This field is important to the XDR primitives, but should not affect a stream's implementation. That is, a stream's implementation should not depend on this value. The fields x_private, x_base, and x_handy are private to the particular stream's implementation. The field x_public is for the XDR client and should never be used by the XDR stream implementations or the XDR primitives. x_getpostn(), x_setpostn(), and x_destroy() are macros for accessing operations.
The operation x_inline() has two parameters: an XDR *, and an unsigned integer, which is a byte count. The routine returns a pointer to a piece of the stream's internal buffer. The caller can then use the buffer segment for any purpose. The stream's interpretation is that the bytes in the buffer segment have been consumed. The routine can return NULL if it cannot return a buffer segment of the requested size.
Caution - The x_inline() routine is used to squeeze cycles, and the resulting buffer is not data portable. Do not use this feature.
The operations x_getbytes() and x_putbytes() routinely get and put sequences of bytes from or to the underlying stream. They return TRUE if they are successful, and FALSE otherwise. The routines have identical parameters (replace xxx with the same string in each case.)
bool_t xxxbytes(xdrs, buf, bytecount) XDR *xdrs; char *buf; u_int bytecount; |
The operations x_getint32() and x_putint32() receive and put int numbers from and to the data stream. These routines are responsible for translating the numbers between the machine representation and the (standard) external representation. The UNIX primitives htonl() and ntohl() can be helpful in accomplishing this objective. The higher-level XDR implementation assumes that signed and unsigned integers contain the same number of bits, and that nonnegative integers have the same bit representations as unsigned integers. The routines return TRUE if they succeed, and FALSE otherwise.
The x_getint() and x_putint() functions make use of these operations. They have identical parameters:
bool_t xxxint(xdrs, ip) XDR *xdrs; int32_t *ip; |
The long version of these operations (x_getlong() and x_putlong()) also call x_getint32() and x_putint32(), ensuring that a 4-byte quantity is operated on, no matter what machine the program is running on.
Implementors of new XDR streams must make an XDR structure with new operation routines available to clients, using some kind of create routine.
Advanced XDR Topics
This section describes techniques for passing data structures that are not covered in the preceding sections. Such structures include linked lists of arbitrary lengths. Unlike the simpler examples covered in the previous sections, the following examples are written using both the XDR C library routines and the XDR data description language. Appendix C, XDR Protocol Specification describes this language in detail.
Linked Lists
The "Pointer Example" presented a C data structure and its associated XDR routines for an individual's gross assets and liabilities. The following code example uses a linked list to duplicate the pointer example.
Example A-14 Linked List
struct gnumbers { int g_assets; int g_liabilities; }; bool_t xdr_gnumbers(xdrs, gp) XDR *xdrs; struct gnumbers *gp; { return(xdr_int(xdrs, &(gp->g_assets) && xdr_int(xdrs, &(gp->g_liabilities))); } |
Now assume that you want to implement a linked list of such information. A data structure could be constructed as follows.
struct gnumbers_node { struct gnumbers gn_numbers; struct gnumbers_node *gn_next; }; typedef struct gnumbers_node *gnumbers_list; |
Think of the head of the linked list as the data object. That is, the head is not merely a convenient shorthand for a structure. Similarly, the gn_next field is used to indicate whether the object has terminated. Unfortunately, if the object continues, the gn_next field is also the address of where it continues. The link addresses carry no useful information when the object is serialized.
The XDR data description of this linked list is described by the recursive declaration of gnumbers_list.
struct gnumbers { int g_assets; int g_liabilities; }; struct gnumbers_node { gnumbers gn_numbers; gnumbers_node *gn_next; }; |
In this description, the Boolean indicates more data follows. If the Boolean is FALSE, it is the last data field of the structure. If it is TRUE, it is followed by a gnumbers structure and, recursively, by a gnumbers_list. Note that the C declaration has no Boolean explicitly declared in it, though the gn_next field implicitly carries the information. The XDR data description has no pointer explicitly declared in it.
Hints for writing the XDR routines for a gnumbers_list follow easily from the preceding XDR description. Note how the primitive xdr_pointer() is used to implement the preceding XDR union.
Example A-15 xdr_pointer
The side effect of using XDR on a list with these routines is that the C stack grows linearly with respect to the number of nodes in the list. This growth is due to the recursion. The following example collapses the last two mutually recursive routines into a single, nonrecursive one.
Example A-16 Nonrecursive Stack in XDR