1.\" 2.\" Must use -- eqn -- with this one 3.\" 4.\" @(#)xdr.nts.ms 2.2 88/08/05 4.0 RPCSRC |
5.\" $FreeBSD: head/lib/libc/rpc/PSD.doc/xdr.nts.ms 108533 2003-01-01 18:49:04Z schweikh $ |
6.\" 7.EQ 8delim $$ 9.EN 10.de BT 11.if \\n%=1 .tl ''- % -'' 12.. 13.ND --- 4 unchanged lines hidden (view full) --- 18.if \n%=1 .bp 19.SH 20\&External Data Representation: Sun Technical Notes 21.IX XDR "Sun technical notes" 22.LP 23This chapter contains technical notes on Sun's implementation of the 24External Data Representation (XDR) standard, a set of library routines 25that allow a C programmer to describe arbitrary data structures in a |
26machinex-independent fashion. |
27For a formal specification of the XDR 28standard, see the 29.I "External Data Representation Standard: Protocol Specification". |
30XDR is the backbone of Sun's Remote Procedure Call package, in the 31sense that data for remote procedure calls is transmitted using the |
32standard. XDR library routines should be used to transmit data 33that is accessed (read or written) by more than one type of machine.\** 34.FS 35.IX XDR "system routines" 36For a compete specification of the system External Data Representation |
37routines, see the 38.I xdr(3N) |
39manual page. 40.FE 41.LP |
42This chapter contains a short tutorial overview of the XDR library |
43routines, a guide to accessing currently available XDR streams, and 44information on defining new streams and data types. XDR was designed |
45to work across different languages, operating systems, and machine |
46architectures. Most users (particularly RPC users) will only need 47the information in the 48.I "Number Filters", 49.I "Floating Point Filters", 50and 51.I "Enumeration Filters" |
52sections. |
53Programmers wishing to implement RPC and XDR on new machines 54will be interested in the rest of the chapter, as well as the 55.I "External Data Representaiton Standard: Protocol Specification", 56which will be their primary reference. 57.SH 58Note: 59.I |
60.I rpcgen |
61can be used to write XDR routines even in cases where no RPC calls are 62being made. 63.LP 64On Sun systems, 65C programs that want to use XDR routines 66must include the file 67.I <rpc/rpc.h> , 68which contains all the necessary interfaces to the XDR system. --- 52 unchanged lines hidden (view full) --- 121} 122.DE 123The two programs appear to be portable, because (a) they pass 124.I lint 125checking, and (b) they exhibit the same behavior when executed 126on two different hardware architectures, a Sun and a VAX. 127.LP 128Piping the output of the |
129.I writer |
130program to the |
131.I reader |
132program gives identical results on a Sun or a VAX. 133.DS 134.ft CW 135sun% \fBwriter | reader\fP 1360 1 2 3 4 5 6 7 137sun% 138 139 140vax% \fBwriter | reader\fP 1410 1 2 3 4 5 6 7 142vax% 143.DE |
144With the advent of local area networks and 4.2BSD came the concept |
145of \*Qnetwork pipes\*U \(em a process produces data on one machine, 146and a second process consumes data on another machine. 147A network pipe can be constructed with |
148.I writer |
149and 150.I reader . 151Here are the results if the first produces data on a Sun, 152and the second consumes data on a VAX. 153.DS 154.ft CW 155sun% \fBwriter | rsh vax reader\fP 1560 16777216 33554432 50331648 67108864 83886080 100663296 157117440512 158sun% 159.DE 160Identical results can be obtained by executing |
161.I writer |
162on the VAX and |
163.I reader |
164on the Sun. These results occur because the byte ordering 165of long integers differs between the VAX and the Sun, 166even though word size is the same. 167Note that $16777216$ is $2 sup 24$ \(em 168when four bytes are reversed, the 1 winds up in the 24th bit. 169.LP 170Whenever data is shared by two or more machine types, there is 171a need for portable data. Programs can be made data-portable by 172replacing the |
173.I read() |
174and |
175.I write() |
176calls with calls to an XDR library routine 177.I xdr_long() , 178a filter that knows the standard representation 179of a long integer in its external form. 180Here are the revised versions of 181.I writer : 182.ie t .DS 183.el .DS L --- 68 unchanged lines hidden (view full) --- 252may cause the size of a structure to vary from machine to machine. 253And pointers, which are very convenient to use, have no meaning 254outside the machine where they are defined. 255.LP 256.NH 1 257\&A Canonical Standard 258.IX XDR "canonical standard" 259.LP |
260XDR's approach to standardizing data representations is |
261.I canonical . 262That is, XDR defines a single byte order (Big Endian), a single 263floating-point representation (IEEE), and so on. Any program running on 264any machine can use XDR to create portable data by translating its 265local representation to the XDR standard representations; similarly, any 266program running on any machine can read portable data by translating the 267XDR standard representaions to its local equivalents. The single standard 268completely decouples programs that create or send portable data from those --- 4 unchanged lines hidden (view full) --- 273local representations of other machines are irrelevant. Conversely, to 274existing programs running on other machines, the local representations of 275the new machine are also irrelevant; such programs can immediately read 276portable data produced by the new machine because such data conforms to the 277canonical standards that they already understand. 278.LP 279There are strong precedents for XDR's canonical approach. For example, 280TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five |
281of the ISO model, are canonical protocols. The advantage of any canonical 282approach is simplicity; in the case of XDR, a single set of conversion 283routines is written once and is never touched again. The canonical approach 284has a disadvantage, but it is unimportant in real-world data transfer |
285applications. Suppose two Little-Endian machines are transferring integers |
286according to the XDR standard. The sending machine converts the integers |
287from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving 288machine performs the reverse conversion. Because both machines observe the 289same byte order, their conversions are unnecessary. The point, however, is 290not necessity, but cost as compared to the alternative. 291.LP 292The time spent converting to and from a canonical representation is |
293insignificant, especially in networking applications. Most of the time 294required to prepare a data structure for transfer is not spent in conversion 295but in traversing the elements of the data structure. To transmit a tree, |
296for example, each leaf must be visited and each element in a leaf record must 297be copied to a buffer and aligned there; storage for the leaf may have to be |
298deallocated as well. Similarly, to receive a tree, storage must be |
299allocated for each leaf, data must be moved from the buffer to the leaf and |
300properly aligned, and pointers must be constructed to link the leaves |
301together. Every machine pays the cost of traversing and copying data |
302structures whether or not conversion is required. In networking |
303applications, communications overhead\(emthe time required to move the data |
304down through the sender's protocol layers, across the network and up through |
305the receiver's protocol layers\(emdwarfs conversion overhead. 306.NH 1 307\&The XDR Library 308.IX "XDR" "library" 309.LP 310The XDR library not only solves data portability problems, it also |
311allows you to write and read arbitrary C constructs in a consistent, 312specified, well-documented manner. Thus, it can make sense to use the |
313library even when the data is not shared among machines on a network. 314.LP 315The XDR library has filter routines for 316strings (null-terminated arrays of bytes), 317structures, unions, and arrays, to name a few. 318Using more primitive routines, 319you can write your own specific XDR routines 320to describe arbitrary data structures, --- 7 unchanged lines hidden (view full) --- 328in which each member treats the stream of bits differently. 329In our example, data is manipulated using standard I/O routines, 330so we use 331.I xdrstdio_create (). 332.IX xdrstdio_create() "" "\fIxdrstdio_create()\fP" 333The parameters to XDR stream creation routines 334vary according to their function. 335In our example, |
336.I xdrstdio_create() |
337takes a pointer to an XDR structure that it initializes, 338a pointer to a |
339.I FILE |
340that the input or output is performed on, and the operation. 341The operation may be 342.I XDR_ENCODE 343for serializing in the |
344.I writer |
345program, or 346.I XDR_DECODE 347for deserializing in the |
348.I reader |
349program. 350.LP 351Note: RPC users never need to create XDR streams; 352the RPC system itself creates these streams, 353which are then passed to the users. 354.LP 355The |
356.I xdr_long() |
357.IX xdr_long() "" "\fIxdr_long()\fP" |
358primitive is characteristic of most XDR library |
359primitives and all client XDR routines. 360First, the routine returns |
361.I FALSE |
362(0) if it fails, and |
363.I TRUE |
364(1) if it succeeds. 365Second, for each data type, 366.I xxx , 367there is an associated XDR routine of the form: 368.DS 369.ft CW 370xdr_xxx(xdrs, xp) 371 XDR *xdrs; 372 xxx *xp; 373{ 374} 375.DE 376In our case, |
377.I xxx |
378is long, and the corresponding XDR routine is 379a primitive, 380.I xdr_long() . 381The client could also define an arbitrary structure |
382.I xxx |
383in which case the client would also supply the routine 384.I xdr_xxx (), 385describing each field by calling XDR routines 386of the appropriate type. 387In all cases the first parameter, |
388.I xdrs |
389can be treated as an opaque handle, 390and passed to the primitive routines. 391.LP 392XDR routines are direction independent; 393that is, the same routines are called to serialize or deserialize data. 394This feature is critical to software engineering of portable data. 395The idea is to call the same routine for either operation \(em 396this almost guarantees that serialized data can also be deserialized. 397One routine is used by both producer and consumer of networked data. 398This is implemented by always passing the address 399of an object rather than the object itself \(em 400only in the case of deserialization is the object modified. 401This feature is not shown in our trivial example, 402but its value becomes obvious when nontrivial data structures |
403are passed among machines. |
404If needed, the user can obtain the |
405direction of the XDR operation. |
406See the 407.I "XDR Operation Directions" 408section below for details. 409.LP 410Let's look at a slightly more complicated example. 411Assume that a person's gross assets and liabilities 412are to be exchanged among processes. 413Also assume that these values are important enough --- 17 unchanged lines hidden (view full) --- 431{ 432 if (xdr_long(xdrs, &gp->g_assets) && 433 xdr_long(xdrs, &gp->g_liabilities)) 434 return(TRUE); 435 return(FALSE); 436} 437.DE 438Note that the parameter |
439.I xdrs |
440is never inspected or modified; 441it is only passed on to the subcomponent routines. 442It is imperative to inspect the return value of each XDR routine call, 443and to give up immediately and return |
444.I FALSE |
445if the subroutine fails. 446.LP 447This example also shows that the type 448.I bool_t 449is declared as an integer whose only values are |
450.I TRUE |
451(1) and |
452.I FALSE |
453(0). This document uses the following definitions: 454.ie t .DS 455.el .DS L 456.ft CW 457#define bool_t int 458#define TRUE 1 459#define FALSE 0 460.DE 461.LP 462Keeping these conventions in mind, |
463.I xdr_gnumbers() |
464can be rewritten as follows: 465.ie t .DS 466.el .DS L 467.ft CW 468xdr_gnumbers(xdrs, gp) 469 XDR *xdrs; 470 struct gnumbers *gp; 471{ --- 63 unchanged lines hidden (view full) --- 535 u_short *sup; 536.DE 537The first parameter, 538.I xdrs , 539is an XDR stream handle. 540The second parameter is the address of the number 541that provides data to the stream or receives data from it. 542All routines return |
543.I TRUE |
544if they complete successfully, and |
545.I FALSE |
546otherwise. 547.NH 2 548\&Floating Point Filters 549.IX "XDR library" "floating point filters" 550.LP 551The XDR library also provides primitive routines 552for C's floating point types: 553.DS 554.ft CW 555bool_t xdr_float(xdrs, fp) 556 XDR *xdrs; 557 float *fp; 558.sp .5 559bool_t xdr_double(xdrs, dp) 560 XDR *xdrs; 561 double *dp; 562.DE 563The first parameter, |
564.I xdrs |
565is an XDR stream handle. 566The second parameter is the address 567of the floating point number that provides data to the stream 568or receives data from it. 569Both routines return |
570.I TRUE |
571if they complete successfully, and |
572.I FALSE |
573otherwise. 574.LP 575Note: Since the numbers are represented in IEEE floating point, 576routines may fail when decoding a valid IEEE representation 577into a machine-specific representation, or vice-versa. 578.NH 2 579\&Enumeration Filters 580.IX "XDR library" "enumeration filters" 581.LP 582The XDR library provides a primitive for generic enumerations. 583The primitive assumes that a C |
584.I enum |
585has the same representation inside the machine as a C integer. 586The boolean type is an important instance of the 587.I enum . 588The external representation of a boolean is always |
589.I TRUE 590(1) or 591.I FALSE |
592(0). 593.DS 594.ft CW 595#define bool_t int 596#define FALSE 0 597#define TRUE 1 598.sp .5 599#define enum_t int --- 5 unchanged lines hidden (view full) --- 605bool_t xdr_bool(xdrs, bp) 606 XDR *xdrs; 607 bool_t *bp; 608.DE 609The second parameters 610.I ep 611and 612.I bp |
613are addresses of the associated type that provides data to, or |
614receives data from, the stream 615.I xdrs . 616.NH 2 617\&No Data 618.IX "XDR library" "no data" 619.LP 620Occasionally, an XDR routine must be supplied to the RPC system, 621even when no data is passed or required. --- 47 unchanged lines hidden (view full) --- 669.DS 670.ft CW 671bool_t xdr_string(xdrs, sp, maxlength) 672 XDR *xdrs; 673 char **sp; 674 u_int maxlength; 675.DE 676The first parameter |
677.I xdrs |
678is the XDR stream handle. 679The second parameter |
680.I sp |
681is a pointer to a string (type 682.I "char **" . 683The third parameter |
684.I maxlength |
685specifies the maximum number of bytes allowed during encoding or decoding. 686its value is usually specified by a protocol. For example, a protocol 687specification may say that a file name may be no longer than 255 characters. 688.LP 689The routine returns |
690.I FALSE |
691if the number of characters exceeds 692.I maxlength , 693and |
694.I TRUE |
695if it doesn't. 696.SH 697Keep |
698.I maxlength |
699small. If it is too big you can blow the heap, since |
700.I xdr_string() |
701will call |
702.I malloc() |
703for space. 704.LP 705The behavior of |
706.I xdr_string() |
707.IX xdr_string() "" \fIxdr_string()\fP 708is similar to the behavior of other routines 709discussed in this section. The direction |
710.I XDR_ENCODE |
711is easiest to understand. The parameter |
712.I sp |
713points to a string of a certain length; 714if the string does not exceed 715.I maxlength , 716the bytes are serialized. 717.LP 718The effect of deserializing a string is subtle. 719First the length of the incoming string is determined; 720it must not exceed 721.I maxlength . 722Next |
723.I sp |
724is dereferenced; if the the value is 725.I NULL , 726then a string of the appropriate length is allocated and |
727.I *sp |
728is set to this string. 729If the original value of |
730.I *sp |
731is non-null, then the XDR package assumes 732that a target area has been allocated, 733which can hold strings no longer than 734.I maxlength . 735In either case, the string is decoded into the target area. 736The routine then appends a null character to the string. 737.LP 738In the |
739.I XDR_FREE |
740operation, the string is obtained by dereferencing 741.I sp . 742If the string is not 743.I NULL , 744it is freed and |
745.I *sp |
746is set to 747.I NULL . 748In this operation, |
749.I xdr_string() |
750ignores the |
751.I maxlength |
752parameter. 753.NH 3 754\&Byte Arrays 755.IX "XDR library" "byte arrays" 756.LP 757Often variable-length arrays of bytes are preferable to strings. |
758Byte arrays differ from strings in the following three ways: |
7591) the length of the array (the byte count) is explicitly 760located in an unsigned integer, 7612) the byte sequence is not terminated by a null character, and 7623) the external representation of the bytes is the same as their 763internal representation. 764The primitive |
765.I xdr_bytes() |
766.IX xdr_bytes() "" \fIxdr_bytes()\fP 767converts between the internal and external 768representations of byte arrays: 769.DS 770.ft CW 771bool_t xdr_bytes(xdrs, bpp, lp, maxlength) 772 XDR *xdrs; 773 char **bpp; 774 u_int *lp; 775 u_int maxlength; 776.DE 777The usage of the first, second and fourth parameters 778are identical to the first, second and third parameters of 779.I xdr_string (), 780respectively. 781The length of the byte area is obtained by dereferencing |
782.I lp |
783when serializing; |
784.I *lp |
785is set to the byte length when deserializing. 786.NH 3 787\&Arrays 788.IX "XDR library" "arrays" 789.LP 790The XDR library package provides a primitive 791for handling arrays of arbitrary elements. 792The |
793.I xdr_bytes() |
794routine treats a subset of generic arrays, 795in which the size of array elements is known to be 1, 796and the external description of each element is built-in. 797The generic array primitive, 798.I xdr_array() , 799.IX xdr_array() "" \fIxdr_array()\fP 800requires parameters identical to those of |
801.I xdr_bytes() |
802plus two more: 803the size of array elements, 804and an XDR routine to handle each of the elements. 805This routine is called to encode or decode 806each element of the array. 807.DS 808.ft CW 809bool_t 810xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element) 811 XDR *xdrs; 812 char **ap; 813 u_int *lp; 814 u_int maxlength; 815 u_int elementsiz; 816 bool_t (*xdr_element)(); 817.DE 818The parameter |
819.I ap |
820is the address of the pointer to the array. 821If |
822.I *ap |
823is |
824.I NULL |
825when the array is being deserialized, 826XDR allocates an array of the appropriate size and sets |
827.I *ap |
828to that array. 829The element count of the array is obtained from |
830.I *lp |
831when the array is serialized; |
832.I *lp 833is set to the array length when the array is deserialized. |
834The parameter |
835.I maxlength |
836is the maximum number of elements that the array is allowed to have; 837.I elementsiz 838is the byte size of each element of the array 839(the C function 840.I sizeof() 841can be used to obtain this value). 842The |
843.I xdr_element() |
844.IX xdr_element() "" \fIxdr_element()\fP 845routine is called to serialize, deserialize, or free 846each element of the array. 847.br 848.LP |
849Before defining more constructed data types, it is appropriate to |
850present three examples. 851.LP 852.I "Example A:" 853.br |
854A user on a networked machine can be identified by |
855(a) the machine name, such as 856.I krypton : 857see the |
858.I gethostname |
859man page; (b) the user's UID: see the |
860.I geteuid 861man page; and (c) the group numbers to which the user belongs: |
862see the |
863.I getgroups 864man page. A structure with this information and its associated |
865XDR routine could be coded like this: 866.ie t .DS 867.el .DS L 868.ft CW 869struct netuser { 870 char *nu_machinename; 871 int nu_uid; 872 u_int nu_glen; --- 4 unchanged lines hidden (view full) --- 877.sp .5 878bool_t 879xdr_netuser(xdrs, nup) 880 XDR *xdrs; 881 struct netuser *nup; 882{ 883 return(xdr_string(xdrs, &nup->nu_machinename, NLEN) && 884 xdr_int(xdrs, &nup->nu_uid) && |
885 xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen, |
886 NGRPS, sizeof (int), xdr_int)); 887} 888.DE 889.LP 890.I "Example B:" 891.br 892A party of network users could be implemented 893as an array of --- 74 unchanged lines hidden (view full) --- 968 XDR *xdrs; 969 struct history *hp; 970{ 971 return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS, 972 sizeof (struct cmd), xdr_cmd)); 973} 974.DE 975The most confusing part of this example is that the routine |
976.I xdr_wrap_string() |
977is needed to package the |
978.I xdr_string() |
979routine, because the implementation of |
980.I xdr_array() |
981only passes two parameters to the array element description routine; |
982.I xdr_wrap_string() |
983supplies the third parameter to 984.I xdr_string (). 985.LP 986By now the recursive nature of the XDR library should be obvious. 987Let's continue with more constructed data types. 988.NH 3 989\&Opaque Data 990.IX "XDR library" "opaque data" 991.LP 992In some protocols, handles are passed from a server to client. 993The client passes the handle back to the server at some later time. 994Handles are never inspected by clients; 995they are obtained and submitted. 996That is to say, handles are opaque. 997The |
998.I xdr_opaque() |
999.IX xdr_opaque() "" \fIxdr_opaque()\fP 1000primitive is used for describing fixed sized, opaque bytes. 1001.DS 1002.ft CW 1003bool_t xdr_opaque(xdrs, p, len) 1004 XDR *xdrs; 1005 char *p; 1006 u_int len; 1007.DE 1008The parameter |
1009.I p |
1010is the location of the bytes; 1011.I len 1012is the number of bytes in the opaque object. 1013By definition, the actual data 1014contained in the opaque object are not machine portable. 1015.NH 3 1016\&Fixed Sized Arrays 1017.IX "XDR library" "fixed sized arrays" --- 19 unchanged lines hidden (view full) --- 1037 struct netuser *nup; 1038{ 1039 int i; 1040.sp .5 1041 if (!xdr_string(xdrs, &nup->nu_machinename, NLEN)) 1042 return(FALSE); 1043 if (!xdr_int(xdrs, &nup->nu_uid)) 1044 return(FALSE); |
1045 if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int), |
1046 xdr_int)) { 1047 return(FALSE); 1048 } 1049 return(TRUE); 1050} 1051.DE 1052.NH 3 1053\&Discriminated Unions --- 12 unchanged lines hidden (view full) --- 1066.sp .5 1067bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm) 1068 XDR *xdrs; 1069 enum_t *dscmp; 1070 char *unp; 1071 struct xdr_discrim *arms; 1072 bool_t (*defaultarm)(); /* \fImay equal NULL\fP */ 1073.DE |
1074First the routine translates the discriminant of the union located at |
1075.I *dscmp . 1076The discriminant is always an 1077.I enum_t . 1078Next the union located at |
1079.I *unp |
1080is translated. 1081The parameter 1082.I arms 1083is a pointer to an array of 1084.I xdr_discrim |
1085structures. |
1086Each structure contains an ordered pair of 1087.I [value,proc] . 1088If the union's discriminant is equal to the associated 1089.I value , 1090then the 1091.I proc 1092is called to translate the union. 1093The end of the 1094.I xdr_discrim 1095structure array is denoted by a routine of value |
1096.I NULL |
1097(0). If the discriminant is not found in the 1098.I arms 1099array, then the 1100.I defaultarm 1101procedure is called if it is non-null; 1102otherwise the routine returns 1103.I FALSE . 1104.LP 1105.I "Example D:" 1106Suppose the type of a union may be integer, 1107character pointer (a string), or a |
1108.I gnumbers |
1109structure. 1110Also, assume the union and its current type 1111are declared in a structure. 1112The declaration is: 1113.ie t .DS 1114.el .DS L 1115.ft CW 1116enum utype { INTEGER=1, STRING=2, GNUMBERS=3 }; --- 25 unchanged lines hidden (view full) --- 1142 XDR *xdrs; 1143 struct u_tag *utp; 1144{ 1145 return(xdr_union(xdrs, &utp->utype, &utp->uval, 1146 u_tag_arms, NULL)); 1147} 1148.DE 1149The routine |
1150.I xdr_gnumbers() 1151was presented above in |
1152.I "The XDR Library" 1153section. |
1154.I xdr_wrap_string() |
1155was presented in example C. |
1156The default 1157.I arm |
1158parameter to |
1159.I xdr_union() |
1160(the last parameter) is |
1161.I NULL |
1162in this example. Therefore the value of the union's discriminant 1163may legally take on only values listed in the |
1164.I u_tag_arms |
1165array. This example also demonstrates that 1166the elements of the arm's array do not need to be sorted. 1167.LP 1168It is worth pointing out that the values of the discriminant 1169may be sparse, though in this example they are not. 1170It is always good 1171practice to assign explicitly integer values to each element of the 1172discriminant's type. 1173This practice both documents the external 1174representation of the discriminant and guarantees that different 1175C compilers emit identical discriminant values. 1176.LP 1177Exercise: Implement |
1178.I xdr_union() |
1179using the other primitives in this section. 1180.NH 3 1181\&Pointers 1182.IX "XDR library" "pointers" 1183.LP 1184In C it is often convenient to put pointers 1185to another structure within a structure. 1186The |
1187.I xdr_reference() |
1188.IX xdr_reference() "" \fIxdr_reference()\fP 1189primitive makes it easy to serialize, deserialize, and free 1190these referenced structures. 1191.DS 1192.ft CW 1193bool_t xdr_reference(xdrs, pp, size, proc) 1194 XDR *xdrs; 1195 char **pp; 1196 u_int ssize; 1197 bool_t (*proc)(); 1198.DE 1199.LP 1200Parameter |
1201.I pp |
1202is the address of 1203the pointer to the structure; 1204parameter 1205.I ssize 1206is the size in bytes of the structure (use the C function |
1207.I sizeof() |
1208to obtain this value); and 1209.I proc 1210is the XDR routine that describes the structure. 1211When decoding data, storage is allocated if |
1212.I *pp |
1213is 1214.I NULL . 1215.LP 1216There is no need for a primitive |
1217.I xdr_struct() |
1218to describe structures within structures, 1219because pointers are always sufficient. 1220.LP 1221Exercise: Implement |
1222.I xdr_reference() |
1223using 1224.I xdr_array (). 1225Warning: |
1226.I xdr_reference() |
1227and |
1228.I xdr_array() |
1229are NOT interchangeable external representations of data. 1230.LP 1231.I "Example E:" 1232Suppose there is a structure containing a person's name 1233and a pointer to a |
1234.I gnumbers |
1235structure containing the person's gross assets and liabilities. 1236The construct is: 1237.DS 1238.ft CW 1239struct pgn { 1240 char *name; 1241 struct gnumbers *gnp; 1242}; --- 9 unchanged lines hidden (view full) --- 1252 if (xdr_string(xdrs, &pp->name, NLEN) && 1253 xdr_reference(xdrs, &pp->gnp, 1254 sizeof(struct gnumbers), xdr_gnumbers)) 1255 return(TRUE); 1256 return(FALSE); 1257} 1258.DE 1259.IX "pointer semantics and XDR" |
1260.I "Pointer Semantics and XDR" |
1261.LP |
1262In many applications, C programmers attach double meaning to |
1263the values of a pointer. Typically the value |
1264.I NULL |
1265(or zero) means data is not needed, 1266yet some application-specific interpretation applies. 1267In essence, the C programmer is encoding 1268a discriminated union efficiently 1269by overloading the interpretation of the value of a pointer. 1270For instance, in example E a |
1271.I NULL |
1272pointer value for 1273.I gnp 1274could indicate that 1275the person's assets and liabilities are unknown. 1276That is, the pointer value encodes two things: 1277whether or not the data is known; 1278and if it is known, where it is located in memory. 1279Linked lists are an extreme example of the use 1280of application-specific pointer interpretation. 1281.LP 1282The primitive |
1283.I xdr_reference() |
1284.IX xdr_reference() "" \fIxdr_reference()\fP 1285cannot and does not attach any special 1286meaning to a null-value pointer during serialization. 1287That is, passing an address of a pointer whose value is |
1288.I NULL |
1289to |
1290.I xdr_reference() |
1291when serialing data will most likely cause a memory fault and, on the UNIX 1292system, a core dump. 1293.LP |
1294.I xdr_pointer() 1295correctly handles 1296.I NULL 1297pointers. For more information about its use, see |
1298the 1299.I "Linked Lists" 1300topics below. 1301.LP 1302.I Exercise: 1303After reading the section on 1304.I "Linked Lists" , 1305return here and extend example E so that |
1306it can correctly deal with 1307.I NULL |
1308pointer values. 1309.LP 1310.I Exercise: 1311Using the 1312.I xdr_union (), |
1313.I xdr_reference() |
1314and |
1315.I xdr_void() |
1316primitives, implement a generic pointer handling primitive 1317that implicitly deals with |
1318.I NULL |
1319pointers. That is, implement 1320.I xdr_pointer (). 1321.NH 2 1322\&Non-filter Primitives 1323.IX "XDR" "non-filter primitives" 1324.LP 1325XDR streams can be manipulated with 1326the primitives discussed in this section. --- 5 unchanged lines hidden (view full) --- 1332bool_t xdr_setpos(xdrs, pos) 1333 XDR *xdrs; 1334 u_int pos; 1335.sp .5 1336xdr_destroy(xdrs) 1337 XDR *xdrs; 1338.DE 1339The routine |
1340.I xdr_getpos() |
1341.IX xdr_getpos() "" \fIxdr_getpos()\fP 1342returns an unsigned integer 1343that describes the current position in the data stream. 1344Warning: In some XDR streams, the returned value of |
1345.I xdr_getpos() |
1346is meaningless; 1347the routine returns a \-1 in this case 1348(though \-1 should be a legitimate value). 1349.LP 1350The routine |
1351.I xdr_setpos() |
1352.IX xdr_setpos() "" \fIxdr_setpos()\fP 1353sets a stream position to 1354.I pos . 1355Warning: In some XDR streams, setting a position is impossible; 1356in such cases, |
1357.I xdr_setpos() |
1358will return 1359.I FALSE . 1360This routine will also fail if the requested position is out-of-bounds. 1361The definition of bounds varies from stream to stream. 1362.LP 1363The |
1364.I xdr_destroy() |
1365.IX xdr_destroy() "" \fIxdr_destroy()\fP 1366primitive destroys the XDR stream. 1367Usage of the stream 1368after calling this routine is undefined. 1369.NH 2 1370\&XDR Operation Directions 1371.IX XDR "operation directions" 1372.IX "direction of XDR operations" --- 25 unchanged lines hidden (view full) --- 1398standard I/O 1399.I FILE 1400streams, TCP/IP connections and UNIX files, and memory. 1401.NH 3 1402\&Standard I/O Streams 1403.IX "XDR" "standard I/O streams" 1404.LP 1405XDR streams can be interfaced to standard I/O using the |
1406.I xdrstdio_create() |
1407.IX xdrstdio_create() "" \fIxdrstdio_create()\fP 1408routine as follows: 1409.DS 1410.ft CW 1411#include <stdio.h> 1412#include <rpc/rpc.h> /* \fIxdr streams part of rpc\fP */ 1413.sp .5 1414void 1415xdrstdio_create(xdrs, fp, x_op) 1416 XDR *xdrs; 1417 FILE *fp; 1418 enum xdr_op x_op; 1419.DE 1420The routine |
1421.I xdrstdio_create() |
1422initializes an XDR stream pointed to by 1423.I xdrs . 1424The XDR stream interfaces to the standard I/O library. 1425Parameter 1426.I fp 1427is an open file, and 1428.I x_op 1429is an XDR direction. --- 10 unchanged lines hidden (view full) --- 1440void 1441xdrmem_create(xdrs, addr, len, x_op) 1442 XDR *xdrs; 1443 char *addr; 1444 u_int len; 1445 enum xdr_op x_op; 1446.DE 1447The routine |
1448.I xdrmem_create() |
1449.IX xdrmem_create() "" \fIxdrmem_create()\fP 1450initializes an XDR stream in local memory. 1451The memory is pointed to by parameter 1452.I addr ; 1453parameter 1454.I len 1455is the length in bytes of the memory. 1456The parameters 1457.I xdrs 1458and 1459.I x_op 1460are identical to the corresponding parameters of 1461.I xdrstdio_create (). 1462Currently, the UDP/IP implementation of RPC uses 1463.I xdrmem_create (). 1464Complete call or result messages are built in memory before calling the |
1465.I sendto() |
1466system routine. 1467.NH 3 1468\&Record (TCP/IP) Streams 1469.IX "XDR" "record (TCP/IP) streams" 1470.LP 1471A record stream is an XDR stream built on top of 1472a record marking standard that is built on top of the 1473UNIX file or 4.2 BSD connection interface. --- 4 unchanged lines hidden (view full) --- 1478xdrrec_create(xdrs, 1479 sendsize, recvsize, iohandle, readproc, writeproc) 1480 XDR *xdrs; 1481 u_int sendsize, recvsize; 1482 char *iohandle; 1483 int (*readproc)(), (*writeproc)(); 1484.DE 1485The routine |
1486.I xdrrec_create() |
1487provides an XDR stream interface that allows for a bidirectional, 1488arbitrarily long sequence of records. 1489The contents of the records are meant to be data in XDR form. 1490The stream's primary use is for interfacing RPC to TCP connections. 1491However, it can be used to stream data into or out of normal 1492UNIX files. 1493.LP 1494The parameter 1495.I xdrs 1496is similar to the corresponding parameter described above. 1497The stream does its own data buffering similar to that of standard I/O. 1498The parameters 1499.I sendsize 1500and 1501.I recvsize 1502determine the size in bytes of the output and input buffers, respectively; 1503if their values are zero (0), then predetermined defaults are used. 1504When a buffer needs to be filled or flushed, the routine |
1505.I readproc() |
1506or |
1507.I writeproc() |
1508is called, respectively. 1509The usage and behavior of these 1510routines are similar to the UNIX system calls |
1511.I read() |
1512and 1513.I write (). 1514However, 1515the first parameter to each of these routines is the opaque parameter 1516.I iohandle . 1517The other two parameters 1518.I buf "" 1519and 1520.I nbytes ) 1521and the results 1522(byte count) are identical to the system routines. 1523If |
1524.I xxx |
1525is |
1526.I readproc() |
1527or 1528.I writeproc (), 1529then it has the following form: 1530.DS 1531.ft CW 1532.ft I 1533/* 1534 * returns the actual number of bytes transferred. --- 23 unchanged lines hidden (view full) --- 1558xdrrec_skiprecord(xdrs) 1559 XDR *xdrs; 1560.sp .5 1561bool_t 1562xdrrec_eof(xdrs) 1563 XDR *xdrs; 1564.DE 1565The routine |
1566.I xdrrec_endofrecord() |
1567.IX xdrrec_endofrecord() "" \fIxdrrec_endofrecord()\fP 1568causes the current outgoing data to be marked as a record. 1569If the parameter 1570.I flushnow 1571is 1572.I TRUE , 1573then the stream's |
1574.I writeproc |
1575will be called; otherwise, |
1576.I writeproc |
1577will be called when the output buffer has been filled. 1578.LP 1579The routine |
1580.I xdrrec_skiprecord() |
1581.IX xdrrec_skiprecord() "" \fIxdrrec_skiprecord()\fP 1582causes an input stream's position to be moved past 1583the current record boundary and onto the 1584beginning of the next record in the stream. 1585.LP 1586If there is no more data in the stream's input buffer, 1587then the routine |
1588.I xdrrec_eof() |
1589.IX xdrrec_eof() "" \fIxdrrec_eof()\fP 1590returns 1591.I TRUE . 1592That is not to say that there is no more data 1593in the underlying file descriptor. 1594.NH 2 1595\&XDR Stream Implementation 1596.IX "XDR" "stream implementation" --- 56 unchanged lines hidden (view full) --- 1653takes two parameters: 1654an XDR *, and an unsigned integer, which is a byte count. 1655The routine returns a pointer to a piece of 1656the stream's internal buffer. 1657The caller can then use the buffer segment for any purpose. 1658From the stream's point of view, the bytes in the 1659buffer segment have been consumed or put. 1660The routine may return |
1661.I NULL |
1662if it cannot return a buffer segment of the requested size. 1663(The |
1664.I x_inline() |
1665routine is for cycle squeezers. 1666Use of the resulting buffer is not data-portable. |
1667Users are encouraged not to use this feature.) |
1668.LP 1669The operations 1670.I x_getbytes() 1671and 1672.I x_putbytes() 1673blindly get and put sequences of bytes 1674from or to the underlying stream; 1675they return |
1676.I TRUE |
1677if they are successful, and |
1678.I FALSE |
1679otherwise. The routines have identical parameters (replace 1680.I xxx ): 1681.DS 1682.ft CW 1683bool_t 1684xxxbytes(xdrs, buf, bytecount) 1685 XDR *xdrs; 1686 char *buf; --- 15 unchanged lines hidden (view full) --- 1702can be helpful in accomplishing this. 1703The higher-level XDR implementation assumes that 1704signed and unsigned long integers contain the same number of bits, 1705and that nonnegative integers 1706have the same bit representations as unsigned integers. 1707The routines return 1708.I TRUE 1709if they succeed, and |
1710.I FALSE |
1711otherwise. They have identical parameters: 1712.DS 1713.ft CW 1714bool_t 1715xxxlong(xdrs, lp) 1716 XDR *xdrs; 1717 long *lp; 1718.DE 1719Implementors of new XDR streams must make an XDR structure 1720(with new operation routines) available to clients, 1721using some kind of create routine. 1722.NH 1 1723\&Advanced Topics 1724.IX XDR "advanced topics" 1725.LP 1726This section describes techniques for passing data structures that 1727are not covered in the preceding sections. Such structures include 1728linked lists (of arbitrary lengths). Unlike the simpler examples 1729covered in the earlier sections, the following examples are written |
1730using both the XDR C library routines and the XDR data description 1731language. |
1732The 1733.I "External Data Representation Standard: Protocol Specification" |
1734describes this |
1735language in complete detail. 1736.NH 2 1737\&Linked Lists 1738.IX XDR "linked lists" 1739.LP 1740The last example in the 1741.I Pointers |
1742topic earlier in this chapter |
1743presented a C data structure and its associated XDR |
1744routines for an individual's gross assets and liabilities. |
1745The example is duplicated below: 1746.ie t .DS 1747.el .DS L 1748.ft CW 1749struct gnumbers { 1750 long g_assets; 1751 long g_liabilities; 1752}; --- 4 unchanged lines hidden (view full) --- 1757 struct gnumbers *gp; 1758{ 1759 if (xdr_long(xdrs, &(gp->g_assets))) 1760 return(xdr_long(xdrs, &(gp->g_liabilities))); 1761 return(FALSE); 1762} 1763.DE 1764.LP |
1765Now assume that we wish to implement a linked list of such information. |
1766A data structure could be constructed as follows: 1767.ie t .DS 1768.el .DS L 1769.ft CW 1770struct gnumbers_node { 1771 struct gnumbers gn_numbers; 1772 struct gnumbers_node *gn_next; 1773}; 1774.sp .5 1775typedef struct gnumbers_node *gnumbers_list; 1776.DE 1777.LP 1778The head of the linked list can be thought of as the data object; 1779that is, the head is not merely a convenient shorthand for a |
1780structure. Similarly the 1781.I gn_next 1782field is used to indicate whether or not the object has terminated. 1783Unfortunately, if the object continues, the 1784.I gn_next 1785field is also the address of where it continues. The link addresses |
1786carry no useful information when the object is serialized. 1787.LP |
1788The XDR data description of this linked list is described by the 1789recursive declaration of |
1790.I gnumbers_list : 1791.ie t .DS 1792.el .DS L 1793.ft CW 1794struct gnumbers { 1795 int g_assets; 1796 int g_liabilities; 1797}; 1798.sp .5 1799struct gnumbers_node { 1800 gnumbers gn_numbers; 1801 gnumbers_node *gn_next; 1802}; 1803.DE 1804.LP 1805In this description, the boolean indicates whether there is more data |
1806following it. If the boolean is |
1807.I FALSE , |
1808then it is the last data field of the structure. If it is |
1809.I TRUE , |
1810then it is followed by a gnumbers structure and (recursively) by a |
1811.I gnumbers_list . |
1812Note that the C declaration has no boolean explicitly declared in it 1813(though the 1814.I gn_next 1815field implicitly carries the information), while the XDR data |
1816description has no pointer explicitly declared in it. 1817.LP |
1818Hints for writing the XDR routines for a 1819.I gnumbers_list 1820follow easily from the XDR description above. Note how the primitive 1821.I xdr_pointer() |
1822is used to implement the XDR union above. 1823.ie t .DS 1824.el .DS L 1825.ft CW 1826bool_t 1827xdr_gnumbers_node(xdrs, gn) 1828 XDR *xdrs; 1829 gnumbers_node *gn; 1830{ 1831 return(xdr_gnumbers(xdrs, &gn->gn_numbers) && 1832 xdr_gnumbers_list(xdrs, &gp->gn_next)); 1833} 1834.sp .5 1835bool_t 1836xdr_gnumbers_list(xdrs, gnp) 1837 XDR *xdrs; 1838 gnumbers_list *gnp; 1839{ |
1840 return(xdr_pointer(xdrs, gnp, 1841 sizeof(struct gnumbers_node), |
1842 xdr_gnumbers_node)); 1843} 1844.DE 1845.LP 1846The unfortunate side effect of XDR'ing a list with these routines 1847is that the C stack grows linearly with respect to the number of 1848node in the list. This is due to the recursion. The following 1849routine collapses the above two mutually recursive into a single, --- 15 unchanged lines hidden (view full) --- 1865 return(FALSE); 1866 } 1867 if (! more_data) { 1868 break; 1869 } 1870 if (xdrs->x_op == XDR_FREE) { 1871 nextp = &(*gnp)->gn_next; 1872 } |
1873 if (!xdr_reference(xdrs, gnp, |
1874 sizeof(struct gnumbers_node), xdr_gnumbers)) { 1875 1876 return(FALSE); 1877 } |
1878 gnp = (xdrs->x_op == XDR_FREE) ? |
1879 nextp : &(*gnp)->gn_next; 1880 } 1881 *gnp = NULL; 1882 return(TRUE); 1883} 1884.DE 1885.LP 1886The first task is to find out whether there is more data or not, 1887so that this boolean information can be serialized. Notice that |
1888this statement is unnecessary in the 1889.I XDR_DECODE 1890case, since the value of more_data is not known until we |
1891deserialize it in the next statement. 1892.LP |
1893The next statement XDR's the more_data field of the XDR union. 1894Then if there is truly no more data, we set this last pointer to 1895.I NULL 1896to indicate the end of the list, and return 1897.I TRUE 1898because we are done. Note that setting the pointer to 1899.I NULL 1900is only important in the 1901.I XDR_DECODE 1902case, since it is already 1903.I NULL 1904in the 1905.I XDR_ENCODE 1906and 1907XDR_FREE |
1908cases. 1909.LP |
1910Next, if the direction is |
1911.I XDR_FREE , |
1912the value of 1913.I nextp 1914is set to indicate the location of the next pointer in the list. 1915We do this now because we need to dereference gnp to find the 1916location of the next item in the list, and after the next |
1917statement the storage pointed to by |
1918.I gnp |
1919will be freed up and no be longer valid. We can't do this for all |
1920directions though, because in the 1921.I XDR_DECODE 1922direction the value of 1923.I gnp |
1924won't be set until the next statement. 1925.LP |
1926Next, we XDR the data in the node using the primitive |
1927.I xdr_reference (). |
1928.I xdr_reference() 1929is like 1930.I xdr_pointer() |
1931which we used before, but it does not |
1932send over the boolean indicating whether there is more data. 1933We use it instead of 1934.I xdr_pointer() 1935because we have already XDR'd this information ourselves. Notice 1936that the xdr routine passed is not the same type as an element 1937in the list. The routine passed is |
1938.I xdr_gnumbers (), |
1939for XDR'ing gnumbers, but each element in the list is actually of 1940type |
1941.I gnumbers_node . |
1942We don't pass 1943.I xdr_gnumbers_node() 1944because it is recursive, and instead use 1945.I xdr_gnumbers() 1946which XDR's all of the non-recursive part. Note that this trick 1947will work only if the 1948.I gn_numbers 1949field is the first item in each element, so that their addresses 1950are identical when passed to |
1951.I xdr_reference (). 1952.LP |
1953Finally, we update 1954.I gnp 1955to point to the next item in the list. If the direction is |
1956.I XDR_FREE , |
1957we set it to the previously saved value, otherwise we can 1958dereference 1959.I gnp 1960to get the proper value. Though harder to understand than the |
1961recursive version, this non-recursive routine is far less likely 1962to blow the C stack. It will also run more efficiently since |
1963a lot of procedure call overhead has been removed. Most lists 1964are small though (in the hundreds of items or less) and the |
1965recursive version should be sufficient for them. 1966.EQ 1967delim off 1968.EN |