diff options
Diffstat (limited to 'c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms')
-rw-r--r-- | c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms | 1966 |
1 files changed, 0 insertions, 1966 deletions
diff --git a/c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms b/c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms deleted file mode 100644 index 6c2d482dea..0000000000 --- a/c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms +++ /dev/null @@ -1,1966 +0,0 @@ -.\" -.\" 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 |