Go to main content

ONC+ RPC Developer's Guide

Exit Print View

Updated: March 2019

XDR Library

The XDR library solves data portability problems, and also enables you to write and read arbitrary C constructs in a consistent, specified, well-documented manner. Thus, using the library can be helpful even when the data is not shared among systems on a network.

The XDR library has filter routines for strings (null-terminated arrays of bytes), structures, unions, and arrays, to name a few. Using more primitive routines, you can write your own specific XDR routines to describe arbitrary data structures, including elements of arrays, arms of unions, or objects pointed at from other structures. The structures themselves might contain arrays of arbitrary elements, or pointers to other structures.

Look closely at Example 78, Writer Example (XDR Modified) and Example 79, Reader Example (XDR Modified). A family of XDR stream-creation routines has each member treat the stream of bits differently. In the example, data is manipulated using standard I/O routines, so you use xdrstdio_create(). The parameters to XDR stream-creation routines vary according to their function. In the example, xdrstdio_create() takes a pointer to an XDR structure that it initializes, a pointer to a FILE that the input or output is performed on, and the operation. The operation might be XDR_ENCODE for serializing in the writer program, or XDR_DECODE for deserializing in the reader program.

Note - RPC users never need to create XDR streams. The RPC system itself creates these streams, which are then passed to the users.

The xdr_int() primitive is characteristic of most XDR library primitives and all client XDR routines. First, the routine returns FALSE (0) if it fails, and TRUE (1) if it succeeds. Second, for each data type, xxx, there is an associated XDR routine of the form:

xdr_xxx(xdrs, xp)
   XDR *xdrs;
   xxx *xp;

In this case, xxx is int, and the corresponding XDR routine is a primitive, xdr_int(). The client could also define an arbitrary structure xxx, in which case the client would also supply the routine xdr_xxx(), describing each field by calling XDR routines of the appropriate type. In all cases the first parameter, xdrs, can be treated as an opaque handle and passed to the primitive routines.

An XDR routine is direction independent. That is, the same routine is called to serialize or deserialize data. This feature is critical to software engineering of portable data. The idea is to call the same routine for either operation. This practice almost guarantees that serialized data can also be deserialized.

This one routine is used by both producer and consumer of networked data. This concept is implemented by always passing the address of an object rather than the object itself. Only in the case of deserialization is the object modified. This feature is not shown in the example, but its value becomes obvious when nontrivial data structures are passed among systems. If needed, the user can obtain the direction of the XDR operation. For details, see the sectionOperation Directions.

A slightly more complicated example follows. Assume that a person's gross assets and liabilities are to be exchanged among processes. Also assume that these values are important enough to warrant their own data type.

struct gnumbers {
   int g_assets;
   int g_liabilities;

The corresponding XDR routine describing this structure is as follows.

bool_t                      /* TRUE is success, FALSE is failure */
xdr_gnumbers(xdrs, gp)
   XDR *xdrs;
   struct gnumbers *gp;
   if (xdr_int(xdrs, &gp->g_assets) &&
         xdr_int(xdrs, &gp->g_liabilities))

Note that the parameter xdrs is never inspected or modified. It is only passed on to the subcomponent routines. The routine must inspect the return value of each XDR routine call, and to halt immediately and return FALSE if the subroutine fails.

This example also shows that the type bool_t is declared as an integer that has only the values TRUE (1) and FALSE (0). This document uses the following definitions:

#define bool_t int
#define TRUE 1
#define FALSE 0

By observing these conventions, you can rewrite xdr_gnumbers() as follows:

xdr_gnumbers(xdrs, gp)
   XDR *xdrs;
   struct gnumbers *gp;
   return(xdr_int(xdrs, &gp->g_assets) &&
            xdr_int(xdrs, &gp->g_liabilities));

This document uses both coding styles.