Some confusion in this example is that you need the routine xdr_wrapstring() to package the xdr_string() routine, because the implementation of xdr_array() passes only two parameters to the array element description routine. xdr_wrapstring() supplies the third parameter to xdr_string().
By now, the recursive nature of the XDR library should be obvious. A discussion follows of more constructed data types.
Opaque Data
In some protocols, handles are passed from a server to the 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, handles are opaque. The xdr_opaque() primitive is used for describing fixed-sized opaque bytes.
bool_t xdr_opaque(xdrs, p, len) XDR *xdrs; char *p; u_int len; |
The parameter p is the location of the bytes, len is the number of bytes in the opaque object. By definition, the actual data contained in the opaque object is not machine portable.
In the SunOS/SVR4 system is another routine for manipulating opaque data. This routine, the xdr_netobj, sends counted opaque data, much like xdr_opaque(). The following code example illustrates the syntax of xdr_netobj().
Example A-10 xdr_netobj Routine
struct netobj { u_int n_len; char *n_bytes; }; typedef struct netobj netobj; bool_t xdr_netobj(xdrs, np) XDR *xdrs; struct netobj *np; |
The xdr_netobj() routine is a filter primitive that translates between variable-length opaque data and its external representation. The parameter np is the address of the netobj structure containing both a length and a pointer to the opaque data. The length may be no more than MAX_NETOBJ_SZ bytes. This routine returns TRUE if it succeeds, FALSE otherwise.
Fixed-Length Arrays
The XDR library provides a primitive, xdr_vector(), for fixed-length arrays, shown in the following code example.
Example A-11 xdr_vector Routine
Discriminated Unions
The XDR library supports discriminated unions. A discriminated union is a C union and an enum_t value that selects an "arm" of the union.
struct xdr_discrim { enum_t value; bool_t (*proc)(); }; bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm) XDR *xdrs; enum_t *dscmp; char *unp; struct xdr_discrim *arms; bool_t (*defaultarm)(); /* may equal NULL */ |
First the routine translates the discriminant of the union located at *dscmp. The discriminant is always an enum_t. Next the union located at *unp is translated. The parameter arms is a pointer to an array of xdr_discrim structures. Each structure contains an ordered pair of [value,proc]. If the union's discriminant is equal to the associated value, then the proc is called to translate the union. The end of the xdr_discrim structure array is denoted by a routine of value NULL (0). If the discriminant is not found in the arms array, then the defaultarm() procedure is called if it is nonnull. Otherwise the routine returns FALSE.
Discriminated Union Example
Suppose the type of a union is integer, character pointer (a string), or a gnumbers structure. Also, assume the union and its current type are declared in a structure. The declaration is:
enum utype {INTEGER=1, STRING=2, GNUMBERS=3}; struct u_tag { enum utype utype; /* the union's discriminant */ union { int ival; char *pval; struct gnumbers gn; } uval; }; |
The following code example constructs an XDR procedure to deserialize the discriminated union.
Example A-12 XDR Discriminated Union
The routine xdr_gnumbers() was presented previously in "XDR Library". The default arm parameter to xdr_union(), the last parameter, is NULL in this example. Therefore, the value of the union's discriminant can legally take on only values listed in the u_tag_arms array. Example A-12 also demonstrates that the elements of the arm's array do not need to be sorted.
The values of the discriminant can be sparse, though in Example A-12 they are not. Make a practice of assigning 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.
Pointers
In C, putting pointers to another structure within a structure is often convenient. The xdr_reference() primitive makes it easy to serialize, deserialize, and free these referenced structures.
bool_t xdr_reference(xdrs, pp, size, proc) XDR *xdrs; char **pp; u_int ssize; bool_t (*proc)(); |
Parameter pp is the address of the pointer to the structure; parameter ssize is the size in bytes of the structure (use the C function sizeof() to obtain this value); and proc() is the XDR routine that describes the structure. When decoding data, storage is allocated if *pp is NULL.
A primitive xdr_struct() does not need to describe structures within structures because pointers are always sufficient.