summaryrefslogtreecommitdiffstats
path: root/c/src/exec/librpc/src/rpc/PSD.doc/xdr.nts.ms
diff options
context:
space:
mode:
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.ms1966
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