The XDR library package provides a primitive for handling arrays of arbitrary elements. The 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, xdr_array() requires parameters identical to those of 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.
bool_t xdr_array(xdrs, ap, lp, maxlength, elementsize, xdr_element) XDR *xdrs; char **ap; u_int *lp; u_int maxlength; u_int elementsize; bool_t (*xdr_element)();
The parameter ap is the address of the pointer to the array. If *ap is NULL when the array is being deserialized, XDR allocates an array of the appropriate size and sets *ap to that array. The element count of the array is obtained from *lp when the array is serialized; *lp is set to the array length when the array is deserialized. The parameter maxlength is the maximum number of elements that the array is allowed to have; elementsiz is the byte size of each element of the array (the C function sizeof() can be used to obtain this value). The xdr_element() routine is called to serialize, deserialize, or free each element of the array.
Before defining more constructed data types, three examples are presented.
A user on a networked machine can be identified by
The machine name.
The user's UID. See the getuid(2) man page.
The group numbers to which the user belongs. See the getgroups(2) man page.
struct netuser { char *nu_machinename; int nu_uid; u_int nu_glen; int *nu_gids; }; #define NLEN 255 /* machine names < 256 chars */ #define NGRPS 20 /* user can't be in > 20 groups */ 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)); }
You could implement a party of network users as an array of netuser structure. The declaration and its associated XDR routines are as shown in the following code example.
struct party { u_int p_len; struct netuser *p_nusers; }; #define PLEN 500 /* max number of users in a party */ 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)); }
You can combine the well-known parameters to main, argc and argv, into a structure. An array of these structures can make up a history of commands. The declarations and XDR routines might look like the following example.
struct cmd { u_int c_argc; char **c_argv; }; #define ALEN 1000 /* args cannot be > 1000 chars */ #define NARGC 100 /* commands cannot have > 100 args */ struct history { u_int h_len; struct cmd *h_cmds; }; #define NCMDS 75 /* history is no more than 75 commands */ bool_t xdr_wrapstring(xdrs, sp) XDR *xdrs; char **sp; { return(xdr_string(xdrs, sp, ALEN)); } 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_wrapstring)); } 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)); }
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.