diff options
Diffstat (limited to 'lib/librpc/doc/xdr.nts.ms')
-rw-r--r-- | lib/librpc/doc/xdr.nts.ms | 1966 |
1 files changed, 1966 insertions, 0 deletions
diff --git a/lib/librpc/doc/xdr.nts.ms b/lib/librpc/doc/xdr.nts.ms new file mode 100644 index 0000000..6c2d482 --- /dev/null +++ b/lib/librpc/doc/xdr.nts.ms @@ -0,0 +1,1966 @@ +.\" +.\" Must use -- eqn -- with this one +.\" +.\" @(#)xdr.nts.ms 2.2 88/08/05 4.0 RPCSRC +.EQ +delim $$ +.EN +.de BT +.if \\n%=1 .tl ''- % -'' +.. +.ND +.\" prevent excess underlining in nroff +.if n .fp 2 R +.OH 'External Data Representation: Sun Technical Notes''Page %' +.EH 'Page %''External Data Representation: Sun Technical Notes' +.if \\n%=1 .bp +.SH +\&External Data Representation: Sun Technical Notes +.IX XDR "Sun technical notes" +.LP +This chapter contains technical notes on Sun's implementation of the +External Data Representation (XDR) standard, a set of library routines +that allow a C programmer to describe arbitrary data structures in a +machinex-independent fashion. +For a formal specification of the XDR +standard, see the +.I "External Data Representation Standard: Protocol Specification". +XDR is the backbone of Sun's Remote Procedure Call package, in the +sense that data for remote procedure calls is transmitted using the +standard. XDR library routines should be used to transmit data +that is accessed (read or written) by more than one type of machine.\** +.FS +.IX XDR "system routines" +For a compete specification of the system External Data Representation +routines, see the +.I xdr(3N) +manual page. +.FE +.LP +This chapter contains a short tutorial overview of the XDR library +routines, a guide to accessing currently available XDR streams, and +information on defining new streams and data types. XDR was designed +to work across different languages, operating systems, and machine +architectures. Most users (particularly RPC users) will only need +the information in the +.I "Number Filters", +.I "Floating Point Filters", +and +.I "Enumeration Filters" +sections. +Programmers wishing to implement RPC and XDR on new machines +will be interested in the rest of the chapter, as well as the +.I "External Data Representaiton Standard: Protocol Specification", +which will be their primary reference. +.SH +Note: +.I +.I rpcgen +can be used to write XDR routines even in cases where no RPC calls are +being made. +.LP +On Sun systems, +C programs that want to use XDR routines +must include the file +.I <rpc/rpc.h> , +which contains all the necessary interfaces to the XDR system. +Since the C library +.I libc.a +contains all the XDR routines, +compile as normal. +.DS +example% \fBcc\0\fIprogram\fP.c\fI +.DE +.ne 3i +.NH 0 +\&Justification +.IX XDR justification +.LP +Consider the following two programs, +.I writer : +.ie t .DS +.el .DS L +.ft CW +#include <stdio.h> +.sp.5 +main() /* \fIwriter.c\fP */ +{ + long i; +.sp.5 + for (i = 0; i < 8; i++) { + if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) { + fprintf(stderr, "failed!\en"); + exit(1); + } + } + exit(0); +} +.DE +and +.I reader : +.ie t .DS +.el .DS L +.ft CW +#include <stdio.h> +.sp.5 +main() /* \fIreader.c\fP */ +{ + long i, j; +.sp.5 + for (j = 0; j < 8; j++) { + if (fread((char *)&i, sizeof (i), 1, stdin) != 1) { + fprintf(stderr, "failed!\en"); + exit(1); + } + printf("%ld ", i); + } + printf("\en"); + exit(0); +} +.DE +The two programs appear to be portable, because (a) they pass +.I lint +checking, and (b) they exhibit the same behavior when executed +on two different hardware architectures, a Sun and a VAX. +.LP +Piping the output of the +.I writer +program to the +.I reader +program gives identical results on a Sun or a VAX. +.DS +.ft CW +sun% \fBwriter | reader\fP +0 1 2 3 4 5 6 7 +sun% + + +vax% \fBwriter | reader\fP +0 1 2 3 4 5 6 7 +vax% +.DE +With the advent of local area networks and 4.2BSD came the concept +of \*Qnetwork pipes\*U \(em a process produces data on one machine, +and a second process consumes data on another machine. +A network pipe can be constructed with +.I writer +and +.I reader . +Here are the results if the first produces data on a Sun, +and the second consumes data on a VAX. +.DS +.ft CW +sun% \fBwriter | rsh vax reader\fP +0 16777216 33554432 50331648 67108864 83886080 100663296 +117440512 +sun% +.DE +Identical results can be obtained by executing +.I writer +on the VAX and +.I reader +on the Sun. These results occur because the byte ordering +of long integers differs between the VAX and the Sun, +even though word size is the same. +Note that $16777216$ is $2 sup 24$ \(em +when four bytes are reversed, the 1 winds up in the 24th bit. +.LP +Whenever data is shared by two or more machine types, there is +a need for portable data. Programs can be made data-portable by +replacing the +.I read() +and +.I write() +calls with calls to an XDR library routine +.I xdr_long() , +a filter that knows the standard representation +of a long integer in its external form. +Here are the revised versions of +.I writer : +.ie t .DS +.el .DS L +.ft CW +#include <stdio.h> +#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */ +.sp.5 +main() /* \fIwriter.c\fP */ +{ + XDR xdrs; + long i; +.sp.5 + xdrstdio_create(&xdrs, stdout, XDR_ENCODE); + for (i = 0; i < 8; i++) { + if (!xdr_long(&xdrs, &i)) { + fprintf(stderr, "failed!\en"); + exit(1); + } + } + exit(0); +} +.DE +and +.I reader : +.ie t .DS +.el .DS L +.ft CW +#include <stdio.h> +#include <rpc/rpc.h> /* \fIxdr is a sub-library of rpc\fP */ +.sp.5 +main() /* \fIreader.c\fP */ +{ + XDR xdrs; + long i, j; +.sp.5 + xdrstdio_create(&xdrs, stdin, XDR_DECODE); + for (j = 0; j < 8; j++) { + if (!xdr_long(&xdrs, &i)) { + fprintf(stderr, "failed!\en"); + exit(1); + } + printf("%ld ", i); + } + printf("\en"); + exit(0); +} +.DE +The new programs were executed on a Sun, +on a VAX, and from a Sun to a VAX; +the results are shown below. +.DS +.ft CW +sun% \fBwriter | reader\fP +0 1 2 3 4 5 6 7 +sun% + +vax% \fBwriter | reader\fP +0 1 2 3 4 5 6 7 +vax% + +sun% \fBwriter | rsh vax reader\fP +0 1 2 3 4 5 6 7 +sun% +.DE +.SH +Note: +.I +.IX XDR "portable data" +Integers are just the tip of the portable-data iceberg. Arbitrary +data structures present portability problems, particularly with +respect to alignment and pointers. Alignment on word boundaries +may cause the size of a structure to vary from machine to machine. +And pointers, which are very convenient to use, have no meaning +outside the machine where they are defined. +.LP +.NH 1 +\&A Canonical Standard +.IX XDR "canonical standard" +.LP +XDR's approach to standardizing data representations is +.I canonical . +That is, XDR defines a single byte order (Big Endian), a single +floating-point representation (IEEE), and so on. Any program running on +any machine can use XDR to create portable data by translating its +local representation to the XDR standard representations; similarly, any +program running on any machine can read portable data by translating the +XDR standard representaions to its local equivalents. The single standard +completely decouples programs that create or send portable data from those +that use or receive portable data. The advent of a new machine or a new +language has no effect upon the community of existing portable data creators +and users. A new machine joins this community by being \*Qtaught\*U how to +convert the standard representations and its local representations; the +local representations of other machines are irrelevant. Conversely, to +existing programs running on other machines, the local representations of +the new machine are also irrelevant; such programs can immediately read +portable data produced by the new machine because such data conforms to the +canonical standards that they already understand. +.LP +There are strong precedents for XDR's canonical approach. For example, +TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five +of the ISO model, are canonical protocols. The advantage of any canonical +approach is simplicity; in the case of XDR, a single set of conversion +routines is written once and is never touched again. The canonical approach +has a disadvantage, but it is unimportant in real-world data transfer +applications. Suppose two Little-Endian machines are transferring integers +according to the XDR standard. The sending machine converts the integers +from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving +machine performs the reverse conversion. Because both machines observe the +same byte order, their conversions are unnecessary. The point, however, is +not necessity, but cost as compared to the alternative. +.LP +The time spent converting to and from a canonical representation is +insignificant, especially in networking applications. Most of the time +required to prepare a data structure for transfer is not spent in conversion +but in traversing the elements of the data structure. To transmit a tree, +for example, each leaf must be visited and each element in a leaf record must +be copied to a buffer and aligned there; storage for the leaf may have to be +deallocated as well. Similarly, to receive a tree, storage must be +allocated for each leaf, data must be moved from the buffer to the leaf and +properly aligned, and pointers must be constructed to link the leaves +together. Every machine pays the cost of traversing and copying data +structures whether or not conversion is required. In networking +applications, communications overhead\(emthe time required to move the data +down through the sender's protocol layers, across the network and up through +the receiver's protocol layers\(emdwarfs conversion overhead. +.NH 1 +\&The XDR Library +.IX "XDR" "library" +.LP +The XDR library not only solves data portability problems, it also +allows you to write and read arbitrary C constructs in a consistent, +specified, well-documented manner. Thus, it can make sense to use the +library even when the data is not shared among machines on a network. +.LP +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 may contain arrays of arbitrary elements, +or pointers to other structures. +.LP +Let's examine the two programs more closely. +There is a family of XDR stream creation routines +in which each member treats the stream of bits differently. +In our example, data is manipulated using standard I/O routines, +so we use +.I xdrstdio_create (). +.IX xdrstdio_create() "" "\fIxdrstdio_create()\fP" +The parameters to XDR stream creation routines +vary according to their function. +In our example, +.I xdrstdio_create() +takes a pointer to an XDR structure that it initializes, +a pointer to a +.I FILE +that the input or output is performed on, and the operation. +The operation may be +.I XDR_ENCODE +for serializing in the +.I writer +program, or +.I XDR_DECODE +for deserializing in the +.I reader +program. +.LP +Note: RPC users never need to create XDR streams; +the RPC system itself creates these streams, +which are then passed to the users. +.LP +The +.I xdr_long() +.IX xdr_long() "" "\fIxdr_long()\fP" +primitive is characteristic of most XDR library +primitives and all client XDR routines. +First, the routine returns +.I FALSE +(0) if it fails, and +.I TRUE +(1) if it succeeds. +Second, for each data type, +.I xxx , +there is an associated XDR routine of the form: +.DS +.ft CW +xdr_xxx(xdrs, xp) + XDR *xdrs; + xxx *xp; +{ +} +.DE +In our case, +.I xxx +is long, and the corresponding XDR routine is +a primitive, +.I xdr_long() . +The client could also define an arbitrary structure +.I xxx +in which case the client would also supply the routine +.I xdr_xxx (), +describing each field by calling XDR routines +of the appropriate type. +In all cases the first parameter, +.I xdrs +can be treated as an opaque handle, +and passed to the primitive routines. +.LP +XDR routines are direction independent; +that is, the same routines are 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 \(em +this almost guarantees that serialized data can also be deserialized. +One routine is used by both producer and consumer of networked data. +This is implemented by always passing the address +of an object rather than the object itself \(em +only in the case of deserialization is the object modified. +This feature is not shown in our trivial example, +but its value becomes obvious when nontrivial data structures +are passed among machines. +If needed, the user can obtain the +direction of the XDR operation. +See the +.I "XDR Operation Directions" +section below for details. +.LP +Let's look at a slightly more complicated example. +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: +.ie t .DS +.el .DS L +.ft CW +struct gnumbers { + long g_assets; + long g_liabilities; +}; +.DE +The corresponding XDR routine describing this structure would be: +.ie t .DS +.el .DS L +.ft CW +bool_t /* \fITRUE is success, FALSE is failure\fP */ +xdr_gnumbers(xdrs, gp) + XDR *xdrs; + struct gnumbers *gp; +{ + if (xdr_long(xdrs, &gp->g_assets) && + xdr_long(xdrs, &gp->g_liabilities)) + return(TRUE); + return(FALSE); +} +.DE +Note that the parameter +.I xdrs +is never inspected or modified; +it is only passed on to the subcomponent routines. +It is imperative to inspect the return value of each XDR routine call, +and to give up immediately and return +.I FALSE +if the subroutine fails. +.LP +This example also shows that the type +.I bool_t +is declared as an integer whose only values are +.I TRUE +(1) and +.I FALSE +(0). This document uses the following definitions: +.ie t .DS +.el .DS L +.ft CW +#define bool_t int +#define TRUE 1 +#define FALSE 0 +.DE +.LP +Keeping these conventions in mind, +.I xdr_gnumbers() +can be rewritten as follows: +.ie t .DS +.el .DS L +.ft CW +xdr_gnumbers(xdrs, gp) + XDR *xdrs; + struct gnumbers *gp; +{ + return(xdr_long(xdrs, &gp->g_assets) && + xdr_long(xdrs, &gp->g_liabilities)); +} +.DE +This document uses both coding styles. +.NH 1 +\&XDR Library Primitives +.IX "library primitives for XDR" +.IX XDR "library primitives" +.LP +This section gives a synopsis of each XDR primitive. +It starts with basic data types and moves on to constructed data types. +Finally, XDR utilities are discussed. +The interface to these primitives +and utilities is defined in the include file +.I <rpc/xdr.h> , +automatically included by +.I <rpc/rpc.h> . +.NH 2 +\&Number Filters +.IX "XDR library" "number filters" +.LP +The XDR library provides primitives to translate between numbers +and their corresponding external representations. +Primitives cover the set of numbers in: +.DS +.ft CW +[signed, unsigned] * [short, int, long] +.DE +.ne 2i +Specifically, the eight primitives are: +.DS +.ft CW +bool_t xdr_char(xdrs, cp) + XDR *xdrs; + char *cp; +.sp.5 +bool_t xdr_u_char(xdrs, ucp) + XDR *xdrs; + unsigned char *ucp; +.sp.5 +bool_t xdr_int(xdrs, ip) + XDR *xdrs; + int *ip; +.sp.5 +bool_t xdr_u_int(xdrs, up) + XDR *xdrs; + unsigned *up; +.sp.5 +bool_t xdr_long(xdrs, lip) + XDR *xdrs; + long *lip; +.sp.5 +bool_t xdr_u_long(xdrs, lup) + XDR *xdrs; + u_long *lup; +.sp.5 +bool_t xdr_short(xdrs, sip) + XDR *xdrs; + short *sip; +.sp.5 +bool_t xdr_u_short(xdrs, sup) + XDR *xdrs; + u_short *sup; +.DE +The first parameter, +.I xdrs , +is an XDR stream handle. +The second parameter is the address of the number +that provides data to the stream or receives data from it. +All routines return +.I TRUE +if they complete successfully, and +.I FALSE +otherwise. +.NH 2 +\&Floating Point Filters +.IX "XDR library" "floating point filters" +.LP +The XDR library also provides primitive routines +for C's floating point types: +.DS +.ft CW +bool_t xdr_float(xdrs, fp) + XDR *xdrs; + float *fp; +.sp.5 +bool_t xdr_double(xdrs, dp) + XDR *xdrs; + double *dp; +.DE +The first parameter, +.I xdrs +is an XDR stream handle. +The second parameter is the address +of the floating point number that provides data to the stream +or receives data from it. +Both routines return +.I TRUE +if they complete successfully, and +.I FALSE +otherwise. +.LP +Note: Since the numbers are represented in IEEE floating point, +routines may fail when decoding a valid IEEE representation +into a machine-specific representation, or vice-versa. +.NH 2 +\&Enumeration Filters +.IX "XDR library" "enumeration filters" +.LP +The XDR library provides a primitive for generic enumerations. +The primitive assumes that a C +.I enum +has the same representation inside the machine as a C integer. +The boolean type is an important instance of the +.I enum . +The external representation of a boolean is always +.I TRUE +(1) or +.I FALSE +(0). +.DS +.ft CW +#define bool_t int +#define FALSE 0 +#define TRUE 1 +.sp.5 +#define enum_t int +.sp.5 +bool_t xdr_enum(xdrs, ep) + XDR *xdrs; + enum_t *ep; +.sp.5 +bool_t xdr_bool(xdrs, bp) + XDR *xdrs; + bool_t *bp; +.DE +The second parameters +.I ep +and +.I bp +are addresses of the associated type that provides data to, or +receives data from, the stream +.I xdrs . +.NH 2 +\&No Data +.IX "XDR library" "no data" +.LP +Occasionally, an XDR routine must be supplied to the RPC system, +even when no data is passed or required. +The library provides such a routine: +.DS +.ft CW +bool_t xdr_void(); /* \fIalways returns TRUE\fP */ +.DE +.NH 2 +\&Constructed Data Type Filters +.IX "XDR library" "constructed data type filters" +.LP +Constructed or compound data type primitives +require more parameters and perform more complicated functions +then the primitives discussed above. +This section includes primitives for +strings, arrays, unions, and pointers to structures. +.LP +Constructed data type primitives may use memory management. +In many cases, memory is allocated when deserializing data with +.I XDR_DECODE +Therefore, the XDR package must provide means to deallocate memory. +This is done by an XDR operation, +.I XDR_FREE +To review, the three XDR directional operations are +.I XDR_ENCODE , +.I XDR_DECODE +and +.I XDR_FREE . +.NH 3 +\&Strings +.IX "XDR library" "strings" +.LP +In C, a string is defined as a sequence of bytes +terminated by a null byte, +which is not considered when calculating string length. +However, when a string is passed or manipulated, +a pointer to it is employed. +Therefore, the XDR library defines a string to be a +.I "char *" +and not a sequence of characters. +The external representation of a string is drastically different +from its internal representation. +Externally, strings are represented as +sequences of ASCII characters, +while internally, they are represented with character pointers. +Conversion between the two representations +is accomplished with the routine +.I xdr_string (): +.IX xdr_string() "" \fIxdr_string()\fP +.DS +.ft CW +bool_t xdr_string(xdrs, sp, maxlength) + XDR *xdrs; + char **sp; + u_int maxlength; +.DE +The first parameter +.I xdrs +is the XDR stream handle. +The second parameter +.I sp +is a pointer to a string (type +.I "char **" . +The third parameter +.I maxlength +specifies the maximum number of bytes allowed during encoding or decoding. +its value is usually specified by a protocol. For example, a protocol +specification may say that a file name may be no longer than 255 characters. +.LP +The routine returns +.I FALSE +if the number of characters exceeds +.I maxlength , +and +.I TRUE +if it doesn't. +.SH +Keep +.I maxlength +small. If it is too big you can blow the heap, since +.I xdr_string() +will call +.I malloc() +for space. +.LP +The behavior of +.I xdr_string() +.IX xdr_string() "" \fIxdr_string()\fP +is similar to the behavior of other routines +discussed in this section. The direction +.I XDR_ENCODE +is easiest to understand. The parameter +.I sp +points to a string of a certain length; +if the string does not exceed +.I maxlength , +the bytes are serialized. +.LP +The effect of deserializing a string is subtle. +First the length of the incoming string is determined; +it must not exceed +.I maxlength . +Next +.I sp +is dereferenced; if the the value is +.I NULL , +then a string of the appropriate length is allocated and +.I *sp +is set to this string. +If the original value of +.I *sp +is non-null, then the XDR package assumes +that a target area has been allocated, +which can hold strings no longer than +.I maxlength . +In either case, the string is decoded into the target area. +The routine then appends a null character to the string. +.LP +In the +.I XDR_FREE +operation, the string is obtained by dereferencing +.I sp . +If the string is not +.I NULL , +it is freed and +.I *sp +is set to +.I NULL . +In this operation, +.I xdr_string() +ignores the +.I maxlength +parameter. +.NH 3 +\&Byte Arrays +.IX "XDR library" "byte arrays" +.LP +Often variable-length arrays of bytes are preferable to strings. +Byte arrays differ from strings in the following three ways: +1) the length of the array (the byte count) is explicitly +located in an unsigned integer, +2) the byte sequence is not terminated by a null character, and +3) the external representation of the bytes is the same as their +internal representation. +The primitive +.I xdr_bytes() +.IX xdr_bytes() "" \fIxdr_bytes()\fP +converts between the internal and external +representations of byte arrays: +.DS +.ft CW +bool_t xdr_bytes(xdrs, bpp, lp, maxlength) + XDR *xdrs; + char **bpp; + u_int *lp; + u_int maxlength; +.DE +The usage of the first, second and fourth parameters +are identical to the first, second and third parameters of +.I xdr_string (), +respectively. +The length of the byte area is obtained by dereferencing +.I lp +when serializing; +.I *lp +is set to the byte length when deserializing. +.NH 3 +\&Arrays +.IX "XDR library" "arrays" +.LP +The XDR library package provides a primitive +for handling arrays of arbitrary elements. +The +.I xdr_bytes() +routine treats a subset of generic arrays, +in which the size of array elements is known to be 1, +and the external description of each element is built-in. +The generic array primitive, +.I xdr_array() , +.IX xdr_array() "" \fIxdr_array()\fP +requires parameters identical to those of +.I xdr_bytes() +plus two more: +the size of array elements, +and an XDR routine to handle each of the elements. +This routine is called to encode or decode +each element of the array. +.DS +.ft CW +bool_t +xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element) + XDR *xdrs; + char **ap; + u_int *lp; + u_int maxlength; + u_int elementsiz; + bool_t (*xdr_element)(); +.DE +The parameter +.I ap +is the address of the pointer to the array. +If +.I *ap +is +.I NULL +when the array is being deserialized, +XDR allocates an array of the appropriate size and sets +.I *ap +to that array. +The element count of the array is obtained from +.I *lp +when the array is serialized; +.I *lp +is set to the array length when the array is deserialized. +The parameter +.I maxlength +is the maximum number of elements that the array is allowed to have; +.I elementsiz +is the byte size of each element of the array +(the C function +.I sizeof() +can be used to obtain this value). +The +.I xdr_element() +.IX xdr_element() "" \fIxdr_element()\fP +routine is called to serialize, deserialize, or free +each element of the array. +.br +.LP +Before defining more constructed data types, it is appropriate to +present three examples. +.LP +.I "Example A:" +.br +A user on a networked machine can be identified by +(a) the machine name, such as +.I krypton : +see the +.I gethostname +man page; (b) the user's UID: see the +.I geteuid +man page; and (c) the group numbers to which the user belongs: +see the +.I getgroups +man page. A structure with this information and its associated +XDR routine could be coded like this: +.ie t .DS +.el .DS L +.ft CW +struct netuser { + char *nu_machinename; + int nu_uid; + u_int nu_glen; + int *nu_gids; +}; +#define NLEN 255 /* \fImachine names < 256 chars\fP */ +#define NGRPS 20 /* \fIuser can't be in > 20 groups\fP */ +.sp.5 +bool_t +xdr_netuser(xdrs, nup) + XDR *xdrs; + struct netuser *nup; +{ + return(xdr_string(xdrs, &nup->nu_machinename, NLEN) && + xdr_int(xdrs, &nup->nu_uid) && + xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen, + NGRPS, sizeof (int), xdr_int)); +} +.DE +.LP +.I "Example B:" +.br +A party of network users could be implemented +as an array of +.I netuser +structure. +The declaration and its associated XDR routines +are as follows: +.ie t .DS +.el .DS L +.ft CW +struct party { + u_int p_len; + struct netuser *p_nusers; +}; +#define PLEN 500 /* \fImax number of users in a party\fP */ +.sp.5 +bool_t +xdr_party(xdrs, pp) + XDR *xdrs; + struct party *pp; +{ + return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN, + sizeof (struct netuser), xdr_netuser)); +} +.DE +.LP +.I "Example C:" +.br +The well-known parameters to +.I main , +.I argc +and +.I argv +can be combined into a structure. +An array of these structures can make up a history of commands. +The declarations and XDR routines might look like: +.ie t .DS +.el .DS L +.ft CW +struct cmd { + u_int c_argc; + char **c_argv; +}; +#define ALEN 1000 /* \fIargs cannot be > 1000 chars\fP */ +#define NARGC 100 /* \fIcommands cannot have > 100 args\fP */ + +struct history { + u_int h_len; + struct cmd *h_cmds; +}; +#define NCMDS 75 /* \fIhistory is no more than 75 commands\fP */ + +bool_t +xdr_wrap_string(xdrs, sp) + XDR *xdrs; + char **sp; +{ + return(xdr_string(xdrs, sp, ALEN)); +} +.DE +.ie t .DS +.el .DS L +.ft CW +bool_t +xdr_cmd(xdrs, cp) + XDR *xdrs; + struct cmd *cp; +{ + return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC, + sizeof (char *), xdr_wrap_string)); +} +.DE +.ie t .DS +.el .DS L +.ft CW +bool_t +xdr_history(xdrs, hp) + XDR *xdrs; + struct history *hp; +{ + return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS, + sizeof (struct cmd), xdr_cmd)); +} +.DE +The most confusing part of this example is that the routine +.I xdr_wrap_string() +is needed to package the +.I xdr_string() +routine, because the implementation of +.I xdr_array() +only passes two parameters to the array element description routine; +.I xdr_wrap_string() +supplies the third parameter to +.I xdr_string (). +.LP +By now the recursive nature of the XDR library should be obvious. +Let's continue with more constructed data types. +.NH 3 +\&Opaque Data +.IX "XDR library" "opaque data" +.LP +In some protocols, handles are passed from a server to client. +The client passes the handle back to the server at some later time. +Handles are never inspected by clients; +they are obtained and submitted. +That is to say, handles are opaque. +The +.I xdr_opaque() +.IX xdr_opaque() "" \fIxdr_opaque()\fP +primitive is used for describing fixed sized, opaque bytes. +.DS +.ft CW +bool_t xdr_opaque(xdrs, p, len) + XDR *xdrs; + char *p; + u_int len; +.DE +The parameter +.I p +is the location of the bytes; +.I len +is the number of bytes in the opaque object. +By definition, the actual data +contained in the opaque object are not machine portable. +.NH 3 +\&Fixed Sized Arrays +.IX "XDR library" "fixed sized arrays" +.LP +The XDR library provides a primitive, +.I xdr_vector (), +for fixed-length arrays. +.ie t .DS +.el .DS L +.ft CW +#define NLEN 255 /* \fImachine names must be < 256 chars\fP */ +#define NGRPS 20 /* \fIuser belongs to exactly 20 groups\fP */ +.sp.5 +struct netuser { + char *nu_machinename; + int nu_uid; + int nu_gids[NGRPS]; +}; +.sp.5 +bool_t +xdr_netuser(xdrs, nup) + XDR *xdrs; + struct netuser *nup; +{ + int i; +.sp.5 + if (!xdr_string(xdrs, &nup->nu_machinename, NLEN)) + return(FALSE); + if (!xdr_int(xdrs, &nup->nu_uid)) + return(FALSE); + if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int), + xdr_int)) { + return(FALSE); + } + return(TRUE); +} +.DE +.NH 3 +\&Discriminated Unions +.IX "XDR library" "discriminated unions" +.LP +The XDR library supports discriminated unions. +A discriminated union is a C union and an +.I enum_t +value that selects an \*Qarm\*U of the union. +.DS +.ft CW +struct xdr_discrim { + enum_t value; + bool_t (*proc)(); +}; +.sp.5 +bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm) + XDR *xdrs; + enum_t *dscmp; + char *unp; + struct xdr_discrim *arms; + bool_t (*defaultarm)(); /* \fImay equal NULL\fP */ +.DE +First the routine translates the discriminant of the union located at +.I *dscmp . +The discriminant is always an +.I enum_t . +Next the union located at +.I *unp +is translated. +The parameter +.I arms +is a pointer to an array of +.I xdr_discrim +structures. +Each structure contains an ordered pair of +.I [value,proc] . +If the union's discriminant is equal to the associated +.I value , +then the +.I proc +is called to translate the union. +The end of the +.I xdr_discrim +structure array is denoted by a routine of value +.I NULL +(0). If the discriminant is not found in the +.I arms +array, then the +.I defaultarm +procedure is called if it is non-null; +otherwise the routine returns +.I FALSE . +.LP +.I "Example D:" +Suppose the type of a union may be integer, +character pointer (a string), or a +.I gnumbers +structure. +Also, assume the union and its current type +are declared in a structure. +The declaration is: +.ie t .DS +.el .DS L +.ft CW +enum utype { INTEGER=1, STRING=2, GNUMBERS=3 }; +.sp.5 +struct u_tag { + enum utype utype; /* \fIthe union's discriminant\fP */ + union { + int ival; + char *pval; + struct gnumbers gn; + } uval; +}; +.DE +The following constructs and XDR procedure (de)serialize +the discriminated union: +.ie t .DS +.el .DS L +.ft CW +struct xdr_discrim u_tag_arms[4] = { + { INTEGER, xdr_int }, + { GNUMBERS, xdr_gnumbers } + { STRING, xdr_wrap_string }, + { __dontcare__, NULL } + /* \fIalways terminate arms with a NULL xdr_proc\fP */ +} +.sp.5 +bool_t +xdr_u_tag(xdrs, utp) + XDR *xdrs; + struct u_tag *utp; +{ + return(xdr_union(xdrs, &utp->utype, &utp->uval, + u_tag_arms, NULL)); +} +.DE +The routine +.I xdr_gnumbers() +was presented above in +.I "The XDR Library" +section. +.I xdr_wrap_string() +was presented in example C. +The default +.I arm +parameter to +.I xdr_union() +(the last parameter) is +.I NULL +in this example. Therefore the value of the union's discriminant +may legally take on only values listed in the +.I u_tag_arms +array. This example also demonstrates that +the elements of the arm's array do not need to be sorted. +.LP +It is worth pointing out that the values of the discriminant +may be sparse, though in this example they are not. +It is always good +practice to assign explicitly integer values to each element of the +discriminant's type. +This practice both documents the external +representation of the discriminant and guarantees that different +C compilers emit identical discriminant values. +.LP +Exercise: Implement +.I xdr_union() +using the other primitives in this section. +.NH 3 +\&Pointers +.IX "XDR library" "pointers" +.LP +In C it is often convenient to put pointers +to another structure within a structure. +The +.I xdr_reference() +.IX xdr_reference() "" \fIxdr_reference()\fP +primitive makes it easy to serialize, deserialize, and free +these referenced structures. +.DS +.ft CW +bool_t xdr_reference(xdrs, pp, size, proc) + XDR *xdrs; + char **pp; + u_int ssize; + bool_t (*proc)(); +.DE +.LP +Parameter +.I pp +is the address of +the pointer to the structure; +parameter +.I ssize +is the size in bytes of the structure (use the C function +.I sizeof() +to obtain this value); and +.I proc +is the XDR routine that describes the structure. +When decoding data, storage is allocated if +.I *pp +is +.I NULL . +.LP +There is no need for a primitive +.I xdr_struct() +to describe structures within structures, +because pointers are always sufficient. +.LP +Exercise: Implement +.I xdr_reference() +using +.I xdr_array (). +Warning: +.I xdr_reference() +and +.I xdr_array() +are NOT interchangeable external representations of data. +.LP +.I "Example E:" +Suppose there is a structure containing a person's name +and a pointer to a +.I gnumbers +structure containing the person's gross assets and liabilities. +The construct is: +.DS +.ft CW +struct pgn { + char *name; + struct gnumbers *gnp; +}; +.DE +The corresponding XDR routine for this structure is: +.DS +.ft CW +bool_t +xdr_pgn(xdrs, pp) + XDR *xdrs; + struct pgn *pp; +{ + if (xdr_string(xdrs, &pp->name, NLEN) && + xdr_reference(xdrs, &pp->gnp, + sizeof(struct gnumbers), xdr_gnumbers)) + return(TRUE); + return(FALSE); +} +.DE +.IX "pointer semantics and XDR" +.I "Pointer Semantics and XDR" +.LP +In many applications, C programmers attach double meaning to +the values of a pointer. Typically the value +.I NULL +(or zero) means data is not needed, +yet some application-specific interpretation applies. +In essence, the C programmer is encoding +a discriminated union efficiently +by overloading the interpretation of the value of a pointer. +For instance, in example E a +.I NULL +pointer value for +.I gnp +could indicate that +the person's assets and liabilities are unknown. +That is, the pointer value encodes two things: +whether or not the data is known; +and if it is known, where it is located in memory. +Linked lists are an extreme example of the use +of application-specific pointer interpretation. +.LP +The primitive +.I xdr_reference() +.IX xdr_reference() "" \fIxdr_reference()\fP +cannot and does not attach any special +meaning to a null-value pointer during serialization. +That is, passing an address of a pointer whose value is +.I NULL +to +.I xdr_reference() +when serialing data will most likely cause a memory fault and, on the UNIX +system, a core dump. +.LP +.I xdr_pointer() +correctly handles +.I NULL +pointers. For more information about its use, see +the +.I "Linked Lists" +topics below. +.LP +.I Exercise: +After reading the section on +.I "Linked Lists" , +return here and extend example E so that +it can correctly deal with +.I NULL +pointer values. +.LP +.I Exercise: +Using the +.I xdr_union (), +.I xdr_reference() +and +.I xdr_void() +primitives, implement a generic pointer handling primitive +that implicitly deals with +.I NULL +pointers. That is, implement +.I xdr_pointer (). +.NH 2 +\&Non-filter Primitives +.IX "XDR" "non-filter primitives" +.LP +XDR streams can be manipulated with +the primitives discussed in this section. +.DS +.ft CW +u_int xdr_getpos(xdrs) + XDR *xdrs; +.sp.5 +bool_t xdr_setpos(xdrs, pos) + XDR *xdrs; + u_int pos; +.sp.5 +xdr_destroy(xdrs) + XDR *xdrs; +.DE +The routine +.I xdr_getpos() +.IX xdr_getpos() "" \fIxdr_getpos()\fP +returns an unsigned integer +that describes the current position in the data stream. +Warning: In some XDR streams, the returned value of +.I xdr_getpos() +is meaningless; +the routine returns a \-1 in this case +(though \-1 should be a legitimate value). +.LP +The routine +.I xdr_setpos() +.IX xdr_setpos() "" \fIxdr_setpos()\fP +sets a stream position to +.I pos . +Warning: In some XDR streams, setting a position is impossible; +in such cases, +.I xdr_setpos() +will return +.I FALSE . +This routine will also fail if the requested position is out-of-bounds. +The definition of bounds varies from stream to stream. +.LP +The +.I xdr_destroy() +.IX xdr_destroy() "" \fIxdr_destroy()\fP +primitive destroys the XDR stream. +Usage of the stream +after calling this routine is undefined. +.NH 2 +\&XDR Operation Directions +.IX XDR "operation directions" +.IX "direction of XDR operations" +.LP +At times you may wish to optimize XDR routines by taking +advantage of the direction of the operation \(em +.I XDR_ENCODE +.I XDR_DECODE +or +.I XDR_FREE +The value +.I xdrs->x_op +always contains the direction of the XDR operation. +Programmers are not encouraged to take advantage of this information. +Therefore, no example is presented here. However, an example in the +.I "Linked Lists" +topic below, demonstrates the usefulness of the +.I xdrs->x_op +field. +.NH 2 +\&XDR Stream Access +.IX "XDR" "stream access" +.LP +An XDR stream is obtained by calling the appropriate creation routine. +These creation routines take arguments that are tailored to the +specific properties of the stream. +.LP +Streams currently exist for (de)serialization of data to or from +standard I/O +.I FILE +streams, TCP/IP connections and UNIX files, and memory. +.NH 3 +\&Standard I/O Streams +.IX "XDR" "standard I/O streams" +.LP +XDR streams can be interfaced to standard I/O using the +.I xdrstdio_create() +.IX xdrstdio_create() "" \fIxdrstdio_create()\fP +routine as follows: +.DS +.ft CW +#include <stdio.h> +#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */ +.sp.5 +void +xdrstdio_create(xdrs, fp, x_op) + XDR *xdrs; + FILE *fp; + enum xdr_op x_op; +.DE +The routine +.I xdrstdio_create() +initializes an XDR stream pointed to by +.I xdrs . +The XDR stream interfaces to the standard I/O library. +Parameter +.I fp +is an open file, and +.I x_op +is an XDR direction. +.NH 3 +\&Memory Streams +.IX "XDR" "memory streams" +.LP +Memory streams allow the streaming of data into or out of +a specified area of memory: +.DS +.ft CW +#include <rpc/rpc.h> +.sp.5 +void +xdrmem_create(xdrs, addr, len, x_op) + XDR *xdrs; + char *addr; + u_int len; + enum xdr_op x_op; +.DE +The routine +.I xdrmem_create() +.IX xdrmem_create() "" \fIxdrmem_create()\fP +initializes an XDR stream in local memory. +The memory is pointed to by parameter +.I addr ; +parameter +.I len +is the length in bytes of the memory. +The parameters +.I xdrs +and +.I x_op +are identical to the corresponding parameters of +.I xdrstdio_create (). +Currently, the UDP/IP implementation of RPC uses +.I xdrmem_create (). +Complete call or result messages are built in memory before calling the +.I sendto() +system routine. +.NH 3 +\&Record (TCP/IP) Streams +.IX "XDR" "record (TCP/IP) streams" +.LP +A record stream is an XDR stream built on top of +a record marking standard that is built on top of the +UNIX file or 4.2 BSD connection interface. +.DS +.ft CW +#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */ +.sp.5 +xdrrec_create(xdrs, + sendsize, recvsize, iohandle, readproc, writeproc) + XDR *xdrs; + u_int sendsize, recvsize; + char *iohandle; + int (*readproc)(), (*writeproc)(); +.DE +The routine +.I xdrrec_create() +provides an XDR stream interface that allows for a bidirectional, +arbitrarily long sequence of records. +The contents of the records are meant to be data in XDR form. +The stream's primary use is for interfacing RPC to TCP connections. +However, it can be used to stream data into or out of normal +UNIX files. +.LP +The parameter +.I xdrs +is similar to the corresponding parameter described above. +The stream does its own data buffering similar to that of standard I/O. +The parameters +.I sendsize +and +.I recvsize +determine the size in bytes of the output and input buffers, respectively; +if their values are zero (0), then predetermined defaults are used. +When a buffer needs to be filled or flushed, the routine +.I readproc() +or +.I writeproc() +is called, respectively. +The usage and behavior of these +routines are similar to the UNIX system calls +.I read() +and +.I write (). +However, +the first parameter to each of these routines is the opaque parameter +.I iohandle . +The other two parameters +.I buf "" +and +.I nbytes ) +and the results +(byte count) are identical to the system routines. +If +.I xxx +is +.I readproc() +or +.I writeproc (), +then it has the following form: +.DS +.ft CW +.ft I +/* + * returns the actual number of bytes transferred. + * -1 is an error + */ +.ft CW +int +xxx(iohandle, buf, len) + char *iohandle; + char *buf; + int nbytes; +.DE +The XDR stream provides means for delimiting records in the byte stream. +The implementation details of delimiting records in a stream are +discussed in the +.I "Advanced Topics" +topic below. +The primitives that are specific to record streams are as follows: +.DS +.ft CW +bool_t +xdrrec_endofrecord(xdrs, flushnow) + XDR *xdrs; + bool_t flushnow; +.sp.5 +bool_t +xdrrec_skiprecord(xdrs) + XDR *xdrs; +.sp.5 +bool_t +xdrrec_eof(xdrs) + XDR *xdrs; +.DE +The routine +.I xdrrec_endofrecord() +.IX xdrrec_endofrecord() "" \fIxdrrec_endofrecord()\fP +causes the current outgoing data to be marked as a record. +If the parameter +.I flushnow +is +.I TRUE , +then the stream's +.I writeproc +will be called; otherwise, +.I writeproc +will be called when the output buffer has been filled. +.LP +The routine +.I xdrrec_skiprecord() +.IX xdrrec_skiprecord() "" \fIxdrrec_skiprecord()\fP +causes an input stream's position to be moved past +the current record boundary and onto the +beginning of the next record in the stream. +.LP +If there is no more data in the stream's input buffer, +then the routine +.I xdrrec_eof() +.IX xdrrec_eof() "" \fIxdrrec_eof()\fP +returns +.I TRUE . +That is not to say that there is no more data +in the underlying file descriptor. +.NH 2 +\&XDR Stream Implementation +.IX "XDR" "stream implementation" +.IX "stream implementation in XDR" +.LP +This section provides the abstract data types needed +to implement new instances of XDR streams. +.NH 3 +\&The XDR Object +.IX "XDR" "object" +.LP +The following structure defines the interface to an XDR stream: +.ie t .DS +.el .DS L +.ft CW +enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 }; +.sp.5 +typedef struct { + enum xdr_op x_op; /* \fIoperation; fast added param\fP */ + struct xdr_ops { + bool_t (*x_getlong)(); /* \fIget long from stream\fP */ + bool_t (*x_putlong)(); /* \fIput long to stream\fP */ + bool_t (*x_getbytes)(); /* \fIget bytes from stream\fP */ + bool_t (*x_putbytes)(); /* \fIput bytes to stream\fP */ + u_int (*x_getpostn)(); /* \fIreturn stream offset\fP */ + bool_t (*x_setpostn)(); /* \fIreposition offset\fP */ + caddr_t (*x_inline)(); /* \fIptr to buffered data\fP */ + VOID (*x_destroy)(); /* \fIfree private area\fP */ + } *x_ops; + caddr_t x_public; /* \fIusers' data\fP */ + caddr_t x_private; /* \fIpointer to private data\fP */ + caddr_t x_base; /* \fIprivate for position info\fP */ + int x_handy; /* \fIextra private word\fP */ +} XDR; +.DE +The +.I 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 +.I x_private , +.I x_base , +and +.I x_handy +are private to the particular +stream's implementation. +The field +.I x_public +is for the XDR client and should never be used by +the XDR stream implementations or the XDR primitives. +.I x_getpostn() , +.I x_setpostn() +and +.I x_destroy() +are macros for accessing operations. The operation +.I x_inline() +takes 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. +From the stream's point of view, the bytes in the +buffer segment have been consumed or put. +The routine may return +.I NULL +if it cannot return a buffer segment of the requested size. +(The +.I x_inline() +routine is for cycle squeezers. +Use of the resulting buffer is not data-portable. +Users are encouraged not to use this feature.) +.LP +The operations +.I x_getbytes() +and +.I x_putbytes() +blindly get and put sequences of bytes +from or to the underlying stream; +they return +.I TRUE +if they are successful, and +.I FALSE +otherwise. The routines have identical parameters (replace +.I xxx ): +.DS +.ft CW +bool_t +xxxbytes(xdrs, buf, bytecount) + XDR *xdrs; + char *buf; + u_int bytecount; +.DE +The operations +.I x_getlong() +and +.I x_putlong() +receive and put +long numbers from and to the data stream. +It is the responsibility of these routines +to translate the numbers between the machine representation +and the (standard) external representation. +The UNIX primitives +.I htonl() +and +.I ntohl() +can be helpful in accomplishing this. +The higher-level XDR implementation assumes that +signed and unsigned long integers contain the same number of bits, +and that nonnegative integers +have the same bit representations as unsigned integers. +The routines return +.I TRUE +if they succeed, and +.I FALSE +otherwise. They have identical parameters: +.DS +.ft CW +bool_t +xxxlong(xdrs, lp) + XDR *xdrs; + long *lp; +.DE +Implementors of new XDR streams must make an XDR structure +(with new operation routines) available to clients, +using some kind of create routine. +.NH 1 +\&Advanced Topics +.IX XDR "advanced topics" +.LP +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 earlier sections, the following examples are written +using both the XDR C library routines and the XDR data description +language. +The +.I "External Data Representation Standard: Protocol Specification" +describes this +language in complete detail. +.NH 2 +\&Linked Lists +.IX XDR "linked lists" +.LP +The last example in the +.I Pointers +topic earlier in this chapter +presented a C data structure and its associated XDR +routines for a individual's gross assets and liabilities. +The example is duplicated below: +.ie t .DS +.el .DS L +.ft CW +struct gnumbers { + long g_assets; + long g_liabilities; +}; +.sp.5 +bool_t +xdr_gnumbers(xdrs, gp) + XDR *xdrs; + struct gnumbers *gp; +{ + if (xdr_long(xdrs, &(gp->g_assets))) + return(xdr_long(xdrs, &(gp->g_liabilities))); + return(FALSE); +} +.DE +.LP +Now assume that we wish to implement a linked list of such information. +A data structure could be constructed as follows: +.ie t .DS +.el .DS L +.ft CW +struct gnumbers_node { + struct gnumbers gn_numbers; + struct gnumbers_node *gn_next; +}; +.sp .5 +typedef struct gnumbers_node *gnumbers_list; +.DE +.LP +The head of the linked list can be thought of as the data object; +that is, the head is not merely a convenient shorthand for a +structure. Similarly the +.I gn_next +field is used to indicate whether or not the object has terminated. +Unfortunately, if the object continues, the +.I gn_next +field is also the address of where it continues. The link addresses +carry no useful information when the object is serialized. +.LP +The XDR data description of this linked list is described by the +recursive declaration of +.I gnumbers_list : +.ie t .DS +.el .DS L +.ft CW +struct gnumbers { + int g_assets; + int g_liabilities; +}; +.sp .5 +struct gnumbers_node { + gnumbers gn_numbers; + gnumbers_node *gn_next; +}; +.DE +.LP +In this description, the boolean indicates whether there is more data +following it. If the boolean is +.I FALSE , +then it is the last data field of the structure. If it is +.I TRUE , +then it is followed by a gnumbers structure and (recursively) by a +.I gnumbers_list . +Note that the C declaration has no boolean explicitly declared in it +(though the +.I gn_next +field implicitly carries the information), while the XDR data +description has no pointer explicitly declared in it. +.LP +Hints for writing the XDR routines for a +.I gnumbers_list +follow easily from the XDR description above. Note how the primitive +.I xdr_pointer() +is used to implement the XDR union above. +.ie t .DS +.el .DS L +.ft CW +bool_t +xdr_gnumbers_node(xdrs, gn) + XDR *xdrs; + gnumbers_node *gn; +{ + return(xdr_gnumbers(xdrs, &gn->gn_numbers) && + xdr_gnumbers_list(xdrs, &gp->gn_next)); +} +.sp .5 +bool_t +xdr_gnumbers_list(xdrs, gnp) + XDR *xdrs; + gnumbers_list *gnp; +{ + return(xdr_pointer(xdrs, gnp, + sizeof(struct gnumbers_node), + xdr_gnumbers_node)); +} +.DE +.LP +The unfortunate side effect of XDR'ing a list with these routines +is that the C stack grows linearly with respect to the number of +node in the list. This is due to the recursion. The following +routine collapses the above two mutually recursive into a single, +non-recursive one. +.ie t .DS +.el .DS L +.ft CW +bool_t +xdr_gnumbers_list(xdrs, gnp) + XDR *xdrs; + gnumbers_list *gnp; +{ + bool_t more_data; + gnumbers_list *nextp; +.sp .5 + for (;;) { + more_data = (*gnp != NULL); + if (!xdr_bool(xdrs, &more_data)) { + return(FALSE); + } + if (! more_data) { + break; + } + if (xdrs->x_op == XDR_FREE) { + nextp = &(*gnp)->gn_next; + } + if (!xdr_reference(xdrs, gnp, + sizeof(struct gnumbers_node), xdr_gnumbers)) { + + return(FALSE); + } + gnp = (xdrs->x_op == XDR_FREE) ? + nextp : &(*gnp)->gn_next; + } + *gnp = NULL; + return(TRUE); +} +.DE +.LP +The first task is to find out whether there is more data or not, +so that this boolean information can be serialized. Notice that +this statement is unnecessary in the +.I XDR_DECODE +case, since the value of more_data is not known until we +deserialize it in the next statement. +.LP +The next statement XDR's the more_data field of the XDR union. +Then if there is truly no more data, we set this last pointer to +.I NULL +to indicate the end of the list, and return +.I TRUE +because we are done. Note that setting the pointer to +.I NULL +is only important in the +.I XDR_DECODE +case, since it is already +.I NULL +in the +.I XDR_ENCODE +and +XDR_FREE +cases. +.LP +Next, if the direction is +.I XDR_FREE , +the value of +.I nextp +is set to indicate the location of the next pointer in the list. +We do this now because we need to dereference gnp to find the +location of the next item in the list, and after the next +statement the storage pointed to by +.I gnp +will be freed up and no be longer valid. We can't do this for all +directions though, because in the +.I XDR_DECODE +direction the value of +.I gnp +won't be set until the next statement. +.LP +Next, we XDR the data in the node using the primitive +.I xdr_reference (). +.I xdr_reference() +is like +.I xdr_pointer() +which we used before, but it does not +send over the boolean indicating whether there is more data. +We use it instead of +.I xdr_pointer() +because we have already XDR'd this information ourselves. Notice +that the xdr routine passed is not the same type as an element +in the list. The routine passed is +.I xdr_gnumbers (), +for XDR'ing gnumbers, but each element in the list is actually of +type +.I gnumbers_node . +We don't pass +.I xdr_gnumbers_node() +because it is recursive, and instead use +.I xdr_gnumbers() +which XDR's all of the non-recursive part. Note that this trick +will work only if the +.I gn_numbers +field is the first item in each element, so that their addresses +are identical when passed to +.I xdr_reference (). +.LP +Finally, we update +.I gnp +to point to the next item in the list. If the direction is +.I XDR_FREE , +we set it to the previously saved value, otherwise we can +dereference +.I gnp +to get the proper value. Though harder to understand than the +recursive version, this non-recursive routine is far less likely +to blow the C stack. It will also run more efficiently since +a lot of procedure call overhead has been removed. Most lists +are small though (in the hundreds of items or less) and the +recursive version should be sufficient for them. +.EQ +delim off +.EN |