1 |
30 |
unneback |
.\"
|
2 |
|
|
.\" Must use -- eqn -- with this one
|
3 |
|
|
.\"
|
4 |
|
|
.\" @(#)xdr.nts.ms 2.2 88/08/05 4.0 RPCSRC
|
5 |
|
|
.EQ
|
6 |
|
|
delim $$
|
7 |
|
|
.EN
|
8 |
|
|
.de BT
|
9 |
|
|
.if \\n%=1 .tl ''- % -''
|
10 |
|
|
..
|
11 |
|
|
.ND
|
12 |
|
|
.\" prevent excess underlining in nroff
|
13 |
|
|
.if n .fp 2 R
|
14 |
|
|
.OH 'External Data Representation: Sun Technical Notes''Page %'
|
15 |
|
|
.EH 'Page %''External Data Representation: Sun Technical Notes'
|
16 |
|
|
.if \\n%=1 .bp
|
17 |
|
|
.SH
|
18 |
|
|
\&External Data Representation: Sun Technical Notes
|
19 |
|
|
.IX XDR "Sun technical notes"
|
20 |
|
|
.LP
|
21 |
|
|
This chapter contains technical notes on Sun's implementation of the
|
22 |
|
|
External Data Representation (XDR) standard, a set of library routines
|
23 |
|
|
that allow a C programmer to describe arbitrary data structures in a
|
24 |
|
|
machinex-independent fashion.
|
25 |
|
|
For a formal specification of the XDR
|
26 |
|
|
standard, see the
|
27 |
|
|
.I "External Data Representation Standard: Protocol Specification".
|
28 |
|
|
XDR is the backbone of Sun's Remote Procedure Call package, in the
|
29 |
|
|
sense that data for remote procedure calls is transmitted using the
|
30 |
|
|
standard. XDR library routines should be used to transmit data
|
31 |
|
|
that is accessed (read or written) by more than one type of machine.\**
|
32 |
|
|
.FS
|
33 |
|
|
.IX XDR "system routines"
|
34 |
|
|
For a compete specification of the system External Data Representation
|
35 |
|
|
routines, see the
|
36 |
|
|
.I xdr(3N)
|
37 |
|
|
manual page.
|
38 |
|
|
.FE
|
39 |
|
|
.LP
|
40 |
|
|
This chapter contains a short tutorial overview of the XDR library
|
41 |
|
|
routines, a guide to accessing currently available XDR streams, and
|
42 |
|
|
information on defining new streams and data types. XDR was designed
|
43 |
|
|
to work across different languages, operating systems, and machine
|
44 |
|
|
architectures. Most users (particularly RPC users) will only need
|
45 |
|
|
the information in the
|
46 |
|
|
.I "Number Filters",
|
47 |
|
|
.I "Floating Point Filters",
|
48 |
|
|
and
|
49 |
|
|
.I "Enumeration Filters"
|
50 |
|
|
sections.
|
51 |
|
|
Programmers wishing to implement RPC and XDR on new machines
|
52 |
|
|
will be interested in the rest of the chapter, as well as the
|
53 |
|
|
.I "External Data Representaiton Standard: Protocol Specification",
|
54 |
|
|
which will be their primary reference.
|
55 |
|
|
.SH
|
56 |
|
|
Note:
|
57 |
|
|
.I
|
58 |
|
|
.I rpcgen
|
59 |
|
|
can be used to write XDR routines even in cases where no RPC calls are
|
60 |
|
|
being made.
|
61 |
|
|
.LP
|
62 |
|
|
On Sun systems,
|
63 |
|
|
C programs that want to use XDR routines
|
64 |
|
|
must include the file
|
65 |
|
|
.I ,
|
66 |
|
|
which contains all the necessary interfaces to the XDR system.
|
67 |
|
|
Since the C library
|
68 |
|
|
.I libc.a
|
69 |
|
|
contains all the XDR routines,
|
70 |
|
|
compile as normal.
|
71 |
|
|
.DS
|
72 |
|
|
example% \fBcc\0\fIprogram\fP.c\fI
|
73 |
|
|
.DE
|
74 |
|
|
.ne 3i
|
75 |
|
|
.NH 0
|
76 |
|
|
\&Justification
|
77 |
|
|
.IX XDR justification
|
78 |
|
|
.LP
|
79 |
|
|
Consider the following two programs,
|
80 |
|
|
.I writer :
|
81 |
|
|
.ie t .DS
|
82 |
|
|
.el .DS L
|
83 |
|
|
.ft CW
|
84 |
|
|
#include
|
85 |
|
|
.sp.5
|
86 |
|
|
main() /* \fIwriter.c\fP */
|
87 |
|
|
{
|
88 |
|
|
long i;
|
89 |
|
|
.sp.5
|
90 |
|
|
for (i = 0; i < 8; i++) {
|
91 |
|
|
if (fwrite((char *)&i, sizeof(i), 1, stdout) != 1) {
|
92 |
|
|
fprintf(stderr, "failed!\en");
|
93 |
|
|
exit(1);
|
94 |
|
|
}
|
95 |
|
|
}
|
96 |
|
|
exit(0);
|
97 |
|
|
}
|
98 |
|
|
.DE
|
99 |
|
|
and
|
100 |
|
|
.I reader :
|
101 |
|
|
.ie t .DS
|
102 |
|
|
.el .DS L
|
103 |
|
|
.ft CW
|
104 |
|
|
#include
|
105 |
|
|
.sp.5
|
106 |
|
|
main() /* \fIreader.c\fP */
|
107 |
|
|
{
|
108 |
|
|
long i, j;
|
109 |
|
|
.sp.5
|
110 |
|
|
for (j = 0; j < 8; j++) {
|
111 |
|
|
if (fread((char *)&i, sizeof (i), 1, stdin) != 1) {
|
112 |
|
|
fprintf(stderr, "failed!\en");
|
113 |
|
|
exit(1);
|
114 |
|
|
}
|
115 |
|
|
printf("%ld ", i);
|
116 |
|
|
}
|
117 |
|
|
printf("\en");
|
118 |
|
|
exit(0);
|
119 |
|
|
}
|
120 |
|
|
.DE
|
121 |
|
|
The two programs appear to be portable, because (a) they pass
|
122 |
|
|
.I lint
|
123 |
|
|
checking, and (b) they exhibit the same behavior when executed
|
124 |
|
|
on two different hardware architectures, a Sun and a VAX.
|
125 |
|
|
.LP
|
126 |
|
|
Piping the output of the
|
127 |
|
|
.I writer
|
128 |
|
|
program to the
|
129 |
|
|
.I reader
|
130 |
|
|
program gives identical results on a Sun or a VAX.
|
131 |
|
|
.DS
|
132 |
|
|
.ft CW
|
133 |
|
|
sun% \fBwriter | reader\fP
|
134 |
|
|
|
135 |
|
|
sun%
|
136 |
|
|
|
137 |
|
|
|
138 |
|
|
vax% \fBwriter | reader\fP
|
139 |
|
|
|
140 |
|
|
vax%
|
141 |
|
|
.DE
|
142 |
|
|
With the advent of local area networks and 4.2BSD came the concept
|
143 |
|
|
of \*Qnetwork pipes\*U \(em a process produces data on one machine,
|
144 |
|
|
and a second process consumes data on another machine.
|
145 |
|
|
A network pipe can be constructed with
|
146 |
|
|
.I writer
|
147 |
|
|
and
|
148 |
|
|
.I reader .
|
149 |
|
|
Here are the results if the first produces data on a Sun,
|
150 |
|
|
and the second consumes data on a VAX.
|
151 |
|
|
.DS
|
152 |
|
|
.ft CW
|
153 |
|
|
sun% \fBwriter | rsh vax reader\fP
|
154 |
|
|
|
155 |
|
|
117440512
|
156 |
|
|
sun%
|
157 |
|
|
.DE
|
158 |
|
|
Identical results can be obtained by executing
|
159 |
|
|
.I writer
|
160 |
|
|
on the VAX and
|
161 |
|
|
.I reader
|
162 |
|
|
on the Sun. These results occur because the byte ordering
|
163 |
|
|
of long integers differs between the VAX and the Sun,
|
164 |
|
|
even though word size is the same.
|
165 |
|
|
Note that $16777216$ is $2 sup 24$ \(em
|
166 |
|
|
when four bytes are reversed, the 1 winds up in the 24th bit.
|
167 |
|
|
.LP
|
168 |
|
|
Whenever data is shared by two or more machine types, there is
|
169 |
|
|
a need for portable data. Programs can be made data-portable by
|
170 |
|
|
replacing the
|
171 |
|
|
.I read()
|
172 |
|
|
and
|
173 |
|
|
.I write()
|
174 |
|
|
calls with calls to an XDR library routine
|
175 |
|
|
.I xdr_long() ,
|
176 |
|
|
a filter that knows the standard representation
|
177 |
|
|
of a long integer in its external form.
|
178 |
|
|
Here are the revised versions of
|
179 |
|
|
.I writer :
|
180 |
|
|
.ie t .DS
|
181 |
|
|
.el .DS L
|
182 |
|
|
.ft CW
|
183 |
|
|
#include
|
184 |
|
|
#include /* \fIxdr is a sub-library of rpc\fP */
|
185 |
|
|
.sp.5
|
186 |
|
|
main() /* \fIwriter.c\fP */
|
187 |
|
|
{
|
188 |
|
|
XDR xdrs;
|
189 |
|
|
long i;
|
190 |
|
|
.sp.5
|
191 |
|
|
xdrstdio_create(&xdrs, stdout, XDR_ENCODE);
|
192 |
|
|
for (i = 0; i < 8; i++) {
|
193 |
|
|
if (!xdr_long(&xdrs, &i)) {
|
194 |
|
|
fprintf(stderr, "failed!\en");
|
195 |
|
|
exit(1);
|
196 |
|
|
}
|
197 |
|
|
}
|
198 |
|
|
exit(0);
|
199 |
|
|
}
|
200 |
|
|
.DE
|
201 |
|
|
and
|
202 |
|
|
.I reader :
|
203 |
|
|
.ie t .DS
|
204 |
|
|
.el .DS L
|
205 |
|
|
.ft CW
|
206 |
|
|
#include
|
207 |
|
|
#include /* \fIxdr is a sub-library of rpc\fP */
|
208 |
|
|
.sp.5
|
209 |
|
|
main() /* \fIreader.c\fP */
|
210 |
|
|
{
|
211 |
|
|
XDR xdrs;
|
212 |
|
|
long i, j;
|
213 |
|
|
.sp.5
|
214 |
|
|
xdrstdio_create(&xdrs, stdin, XDR_DECODE);
|
215 |
|
|
for (j = 0; j < 8; j++) {
|
216 |
|
|
if (!xdr_long(&xdrs, &i)) {
|
217 |
|
|
fprintf(stderr, "failed!\en");
|
218 |
|
|
exit(1);
|
219 |
|
|
}
|
220 |
|
|
printf("%ld ", i);
|
221 |
|
|
}
|
222 |
|
|
printf("\en");
|
223 |
|
|
exit(0);
|
224 |
|
|
}
|
225 |
|
|
.DE
|
226 |
|
|
The new programs were executed on a Sun,
|
227 |
|
|
on a VAX, and from a Sun to a VAX;
|
228 |
|
|
the results are shown below.
|
229 |
|
|
.DS
|
230 |
|
|
.ft CW
|
231 |
|
|
sun% \fBwriter | reader\fP
|
232 |
|
|
|
233 |
|
|
sun%
|
234 |
|
|
|
235 |
|
|
vax% \fBwriter | reader\fP
|
236 |
|
|
|
237 |
|
|
vax%
|
238 |
|
|
|
239 |
|
|
sun% \fBwriter | rsh vax reader\fP
|
240 |
|
|
|
241 |
|
|
sun%
|
242 |
|
|
.DE
|
243 |
|
|
.SH
|
244 |
|
|
Note:
|
245 |
|
|
.I
|
246 |
|
|
.IX XDR "portable data"
|
247 |
|
|
Integers are just the tip of the portable-data iceberg. Arbitrary
|
248 |
|
|
data structures present portability problems, particularly with
|
249 |
|
|
respect to alignment and pointers. Alignment on word boundaries
|
250 |
|
|
may cause the size of a structure to vary from machine to machine.
|
251 |
|
|
And pointers, which are very convenient to use, have no meaning
|
252 |
|
|
outside the machine where they are defined.
|
253 |
|
|
.LP
|
254 |
|
|
.NH 1
|
255 |
|
|
\&A Canonical Standard
|
256 |
|
|
.IX XDR "canonical standard"
|
257 |
|
|
.LP
|
258 |
|
|
XDR's approach to standardizing data representations is
|
259 |
|
|
.I canonical .
|
260 |
|
|
That is, XDR defines a single byte order (Big Endian), a single
|
261 |
|
|
floating-point representation (IEEE), and so on. Any program running on
|
262 |
|
|
any machine can use XDR to create portable data by translating its
|
263 |
|
|
local representation to the XDR standard representations; similarly, any
|
264 |
|
|
program running on any machine can read portable data by translating the
|
265 |
|
|
XDR standard representaions to its local equivalents. The single standard
|
266 |
|
|
completely decouples programs that create or send portable data from those
|
267 |
|
|
that use or receive portable data. The advent of a new machine or a new
|
268 |
|
|
language has no effect upon the community of existing portable data creators
|
269 |
|
|
and users. A new machine joins this community by being \*Qtaught\*U how to
|
270 |
|
|
convert the standard representations and its local representations; the
|
271 |
|
|
local representations of other machines are irrelevant. Conversely, to
|
272 |
|
|
existing programs running on other machines, the local representations of
|
273 |
|
|
the new machine are also irrelevant; such programs can immediately read
|
274 |
|
|
portable data produced by the new machine because such data conforms to the
|
275 |
|
|
canonical standards that they already understand.
|
276 |
|
|
.LP
|
277 |
|
|
There are strong precedents for XDR's canonical approach. For example,
|
278 |
|
|
TCP/IP, UDP/IP, XNS, Ethernet, and, indeed, all protocols below layer five
|
279 |
|
|
of the ISO model, are canonical protocols. The advantage of any canonical
|
280 |
|
|
approach is simplicity; in the case of XDR, a single set of conversion
|
281 |
|
|
routines is written once and is never touched again. The canonical approach
|
282 |
|
|
has a disadvantage, but it is unimportant in real-world data transfer
|
283 |
|
|
applications. Suppose two Little-Endian machines are transferring integers
|
284 |
|
|
according to the XDR standard. The sending machine converts the integers
|
285 |
|
|
from Little-Endian byte order to XDR (Big-Endian) byte order; the receiving
|
286 |
|
|
machine performs the reverse conversion. Because both machines observe the
|
287 |
|
|
same byte order, their conversions are unnecessary. The point, however, is
|
288 |
|
|
not necessity, but cost as compared to the alternative.
|
289 |
|
|
.LP
|
290 |
|
|
The time spent converting to and from a canonical representation is
|
291 |
|
|
insignificant, especially in networking applications. Most of the time
|
292 |
|
|
required to prepare a data structure for transfer is not spent in conversion
|
293 |
|
|
but in traversing the elements of the data structure. To transmit a tree,
|
294 |
|
|
for example, each leaf must be visited and each element in a leaf record must
|
295 |
|
|
be copied to a buffer and aligned there; storage for the leaf may have to be
|
296 |
|
|
deallocated as well. Similarly, to receive a tree, storage must be
|
297 |
|
|
allocated for each leaf, data must be moved from the buffer to the leaf and
|
298 |
|
|
properly aligned, and pointers must be constructed to link the leaves
|
299 |
|
|
together. Every machine pays the cost of traversing and copying data
|
300 |
|
|
structures whether or not conversion is required. In networking
|
301 |
|
|
applications, communications overhead\(emthe time required to move the data
|
302 |
|
|
down through the sender's protocol layers, across the network and up through
|
303 |
|
|
the receiver's protocol layers\(emdwarfs conversion overhead.
|
304 |
|
|
.NH 1
|
305 |
|
|
\&The XDR Library
|
306 |
|
|
.IX "XDR" "library"
|
307 |
|
|
.LP
|
308 |
|
|
The XDR library not only solves data portability problems, it also
|
309 |
|
|
allows you to write and read arbitrary C constructs in a consistent,
|
310 |
|
|
specified, well-documented manner. Thus, it can make sense to use the
|
311 |
|
|
library even when the data is not shared among machines on a network.
|
312 |
|
|
.LP
|
313 |
|
|
The XDR library has filter routines for
|
314 |
|
|
strings (null-terminated arrays of bytes),
|
315 |
|
|
structures, unions, and arrays, to name a few.
|
316 |
|
|
Using more primitive routines,
|
317 |
|
|
you can write your own specific XDR routines
|
318 |
|
|
to describe arbitrary data structures,
|
319 |
|
|
including elements of arrays, arms of unions,
|
320 |
|
|
or objects pointed at from other structures.
|
321 |
|
|
The structures themselves may contain arrays of arbitrary elements,
|
322 |
|
|
or pointers to other structures.
|
323 |
|
|
.LP
|
324 |
|
|
Let's examine the two programs more closely.
|
325 |
|
|
There is a family of XDR stream creation routines
|
326 |
|
|
in which each member treats the stream of bits differently.
|
327 |
|
|
In our example, data is manipulated using standard I/O routines,
|
328 |
|
|
so we use
|
329 |
|
|
.I xdrstdio_create ().
|
330 |
|
|
.IX xdrstdio_create() "" "\fIxdrstdio_create()\fP"
|
331 |
|
|
The parameters to XDR stream creation routines
|
332 |
|
|
vary according to their function.
|
333 |
|
|
In our example,
|
334 |
|
|
.I xdrstdio_create()
|
335 |
|
|
takes a pointer to an XDR structure that it initializes,
|
336 |
|
|
a pointer to a
|
337 |
|
|
.I FILE
|
338 |
|
|
that the input or output is performed on, and the operation.
|
339 |
|
|
The operation may be
|
340 |
|
|
.I XDR_ENCODE
|
341 |
|
|
for serializing in the
|
342 |
|
|
.I writer
|
343 |
|
|
program, or
|
344 |
|
|
.I XDR_DECODE
|
345 |
|
|
for deserializing in the
|
346 |
|
|
.I reader
|
347 |
|
|
program.
|
348 |
|
|
.LP
|
349 |
|
|
Note: RPC users never need to create XDR streams;
|
350 |
|
|
the RPC system itself creates these streams,
|
351 |
|
|
which are then passed to the users.
|
352 |
|
|
.LP
|
353 |
|
|
The
|
354 |
|
|
.I xdr_long()
|
355 |
|
|
.IX xdr_long() "" "\fIxdr_long()\fP"
|
356 |
|
|
primitive is characteristic of most XDR library
|
357 |
|
|
primitives and all client XDR routines.
|
358 |
|
|
First, the routine returns
|
359 |
|
|
.I FALSE
|
360 |
|
|
(0) if it fails, and
|
361 |
|
|
.I TRUE
|
362 |
|
|
(1) if it succeeds.
|
363 |
|
|
Second, for each data type,
|
364 |
|
|
.I xxx ,
|
365 |
|
|
there is an associated XDR routine of the form:
|
366 |
|
|
.DS
|
367 |
|
|
.ft CW
|
368 |
|
|
xdr_xxx(xdrs, xp)
|
369 |
|
|
XDR *xdrs;
|
370 |
|
|
xxx *xp;
|
371 |
|
|
{
|
372 |
|
|
}
|
373 |
|
|
.DE
|
374 |
|
|
In our case,
|
375 |
|
|
.I xxx
|
376 |
|
|
is long, and the corresponding XDR routine is
|
377 |
|
|
a primitive,
|
378 |
|
|
.I xdr_long() .
|
379 |
|
|
The client could also define an arbitrary structure
|
380 |
|
|
.I xxx
|
381 |
|
|
in which case the client would also supply the routine
|
382 |
|
|
.I xdr_xxx (),
|
383 |
|
|
describing each field by calling XDR routines
|
384 |
|
|
of the appropriate type.
|
385 |
|
|
In all cases the first parameter,
|
386 |
|
|
.I xdrs
|
387 |
|
|
can be treated as an opaque handle,
|
388 |
|
|
and passed to the primitive routines.
|
389 |
|
|
.LP
|
390 |
|
|
XDR routines are direction independent;
|
391 |
|
|
that is, the same routines are called to serialize or deserialize data.
|
392 |
|
|
This feature is critical to software engineering of portable data.
|
393 |
|
|
The idea is to call the same routine for either operation \(em
|
394 |
|
|
this almost guarantees that serialized data can also be deserialized.
|
395 |
|
|
One routine is used by both producer and consumer of networked data.
|
396 |
|
|
This is implemented by always passing the address
|
397 |
|
|
of an object rather than the object itself \(em
|
398 |
|
|
only in the case of deserialization is the object modified.
|
399 |
|
|
This feature is not shown in our trivial example,
|
400 |
|
|
but its value becomes obvious when nontrivial data structures
|
401 |
|
|
are passed among machines.
|
402 |
|
|
If needed, the user can obtain the
|
403 |
|
|
direction of the XDR operation.
|
404 |
|
|
See the
|
405 |
|
|
.I "XDR Operation Directions"
|
406 |
|
|
section below for details.
|
407 |
|
|
.LP
|
408 |
|
|
Let's look at a slightly more complicated example.
|
409 |
|
|
Assume that a person's gross assets and liabilities
|
410 |
|
|
are to be exchanged among processes.
|
411 |
|
|
Also assume that these values are important enough
|
412 |
|
|
to warrant their own data type:
|
413 |
|
|
.ie t .DS
|
414 |
|
|
.el .DS L
|
415 |
|
|
.ft CW
|
416 |
|
|
struct gnumbers {
|
417 |
|
|
long g_assets;
|
418 |
|
|
long g_liabilities;
|
419 |
|
|
};
|
420 |
|
|
.DE
|
421 |
|
|
The corresponding XDR routine describing this structure would be:
|
422 |
|
|
.ie t .DS
|
423 |
|
|
.el .DS L
|
424 |
|
|
.ft CW
|
425 |
|
|
bool_t /* \fITRUE is success, FALSE is failure\fP */
|
426 |
|
|
xdr_gnumbers(xdrs, gp)
|
427 |
|
|
XDR *xdrs;
|
428 |
|
|
struct gnumbers *gp;
|
429 |
|
|
{
|
430 |
|
|
if (xdr_long(xdrs, &gp->g_assets) &&
|
431 |
|
|
xdr_long(xdrs, &gp->g_liabilities))
|
432 |
|
|
return(TRUE);
|
433 |
|
|
return(FALSE);
|
434 |
|
|
}
|
435 |
|
|
.DE
|
436 |
|
|
Note that the parameter
|
437 |
|
|
.I xdrs
|
438 |
|
|
is never inspected or modified;
|
439 |
|
|
it is only passed on to the subcomponent routines.
|
440 |
|
|
It is imperative to inspect the return value of each XDR routine call,
|
441 |
|
|
and to give up immediately and return
|
442 |
|
|
.I FALSE
|
443 |
|
|
if the subroutine fails.
|
444 |
|
|
.LP
|
445 |
|
|
This example also shows that the type
|
446 |
|
|
.I bool_t
|
447 |
|
|
is declared as an integer whose only values are
|
448 |
|
|
.I TRUE
|
449 |
|
|
(1) and
|
450 |
|
|
.I FALSE
|
451 |
|
|
(0). This document uses the following definitions:
|
452 |
|
|
.ie t .DS
|
453 |
|
|
.el .DS L
|
454 |
|
|
.ft CW
|
455 |
|
|
#define bool_t int
|
456 |
|
|
#define TRUE 1
|
457 |
|
|
#define FALSE 0
|
458 |
|
|
.DE
|
459 |
|
|
.LP
|
460 |
|
|
Keeping these conventions in mind,
|
461 |
|
|
.I xdr_gnumbers()
|
462 |
|
|
can be rewritten as follows:
|
463 |
|
|
.ie t .DS
|
464 |
|
|
.el .DS L
|
465 |
|
|
.ft CW
|
466 |
|
|
xdr_gnumbers(xdrs, gp)
|
467 |
|
|
XDR *xdrs;
|
468 |
|
|
struct gnumbers *gp;
|
469 |
|
|
{
|
470 |
|
|
return(xdr_long(xdrs, &gp->g_assets) &&
|
471 |
|
|
xdr_long(xdrs, &gp->g_liabilities));
|
472 |
|
|
}
|
473 |
|
|
.DE
|
474 |
|
|
This document uses both coding styles.
|
475 |
|
|
.NH 1
|
476 |
|
|
\&XDR Library Primitives
|
477 |
|
|
.IX "library primitives for XDR"
|
478 |
|
|
.IX XDR "library primitives"
|
479 |
|
|
.LP
|
480 |
|
|
This section gives a synopsis of each XDR primitive.
|
481 |
|
|
It starts with basic data types and moves on to constructed data types.
|
482 |
|
|
Finally, XDR utilities are discussed.
|
483 |
|
|
The interface to these primitives
|
484 |
|
|
and utilities is defined in the include file
|
485 |
|
|
.I ,
|
486 |
|
|
automatically included by
|
487 |
|
|
.I .
|
488 |
|
|
.NH 2
|
489 |
|
|
\&Number Filters
|
490 |
|
|
.IX "XDR library" "number filters"
|
491 |
|
|
.LP
|
492 |
|
|
The XDR library provides primitives to translate between numbers
|
493 |
|
|
and their corresponding external representations.
|
494 |
|
|
Primitives cover the set of numbers in:
|
495 |
|
|
.DS
|
496 |
|
|
.ft CW
|
497 |
|
|
[signed, unsigned] * [short, int, long]
|
498 |
|
|
.DE
|
499 |
|
|
.ne 2i
|
500 |
|
|
Specifically, the eight primitives are:
|
501 |
|
|
.DS
|
502 |
|
|
.ft CW
|
503 |
|
|
bool_t xdr_char(xdrs, cp)
|
504 |
|
|
XDR *xdrs;
|
505 |
|
|
char *cp;
|
506 |
|
|
.sp.5
|
507 |
|
|
bool_t xdr_u_char(xdrs, ucp)
|
508 |
|
|
XDR *xdrs;
|
509 |
|
|
unsigned char *ucp;
|
510 |
|
|
.sp.5
|
511 |
|
|
bool_t xdr_int(xdrs, ip)
|
512 |
|
|
XDR *xdrs;
|
513 |
|
|
int *ip;
|
514 |
|
|
.sp.5
|
515 |
|
|
bool_t xdr_u_int(xdrs, up)
|
516 |
|
|
XDR *xdrs;
|
517 |
|
|
unsigned *up;
|
518 |
|
|
.sp.5
|
519 |
|
|
bool_t xdr_long(xdrs, lip)
|
520 |
|
|
XDR *xdrs;
|
521 |
|
|
long *lip;
|
522 |
|
|
.sp.5
|
523 |
|
|
bool_t xdr_u_long(xdrs, lup)
|
524 |
|
|
XDR *xdrs;
|
525 |
|
|
u_long *lup;
|
526 |
|
|
.sp.5
|
527 |
|
|
bool_t xdr_short(xdrs, sip)
|
528 |
|
|
XDR *xdrs;
|
529 |
|
|
short *sip;
|
530 |
|
|
.sp.5
|
531 |
|
|
bool_t xdr_u_short(xdrs, sup)
|
532 |
|
|
XDR *xdrs;
|
533 |
|
|
u_short *sup;
|
534 |
|
|
.DE
|
535 |
|
|
The first parameter,
|
536 |
|
|
.I xdrs ,
|
537 |
|
|
is an XDR stream handle.
|
538 |
|
|
The second parameter is the address of the number
|
539 |
|
|
that provides data to the stream or receives data from it.
|
540 |
|
|
All routines return
|
541 |
|
|
.I TRUE
|
542 |
|
|
if they complete successfully, and
|
543 |
|
|
.I FALSE
|
544 |
|
|
otherwise.
|
545 |
|
|
.NH 2
|
546 |
|
|
\&Floating Point Filters
|
547 |
|
|
.IX "XDR library" "floating point filters"
|
548 |
|
|
.LP
|
549 |
|
|
The XDR library also provides primitive routines
|
550 |
|
|
for C's floating point types:
|
551 |
|
|
.DS
|
552 |
|
|
.ft CW
|
553 |
|
|
bool_t xdr_float(xdrs, fp)
|
554 |
|
|
XDR *xdrs;
|
555 |
|
|
float *fp;
|
556 |
|
|
.sp.5
|
557 |
|
|
bool_t xdr_double(xdrs, dp)
|
558 |
|
|
XDR *xdrs;
|
559 |
|
|
double *dp;
|
560 |
|
|
.DE
|
561 |
|
|
The first parameter,
|
562 |
|
|
.I xdrs
|
563 |
|
|
is an XDR stream handle.
|
564 |
|
|
The second parameter is the address
|
565 |
|
|
of the floating point number that provides data to the stream
|
566 |
|
|
or receives data from it.
|
567 |
|
|
Both routines return
|
568 |
|
|
.I TRUE
|
569 |
|
|
if they complete successfully, and
|
570 |
|
|
.I FALSE
|
571 |
|
|
otherwise.
|
572 |
|
|
.LP
|
573 |
|
|
Note: Since the numbers are represented in IEEE floating point,
|
574 |
|
|
routines may fail when decoding a valid IEEE representation
|
575 |
|
|
into a machine-specific representation, or vice-versa.
|
576 |
|
|
.NH 2
|
577 |
|
|
\&Enumeration Filters
|
578 |
|
|
.IX "XDR library" "enumeration filters"
|
579 |
|
|
.LP
|
580 |
|
|
The XDR library provides a primitive for generic enumerations.
|
581 |
|
|
The primitive assumes that a C
|
582 |
|
|
.I enum
|
583 |
|
|
has the same representation inside the machine as a C integer.
|
584 |
|
|
The boolean type is an important instance of the
|
585 |
|
|
.I enum .
|
586 |
|
|
The external representation of a boolean is always
|
587 |
|
|
.I TRUE
|
588 |
|
|
(1) or
|
589 |
|
|
.I FALSE
|
590 |
|
|
(0).
|
591 |
|
|
.DS
|
592 |
|
|
.ft CW
|
593 |
|
|
#define bool_t int
|
594 |
|
|
#define FALSE 0
|
595 |
|
|
#define TRUE 1
|
596 |
|
|
.sp.5
|
597 |
|
|
#define enum_t int
|
598 |
|
|
.sp.5
|
599 |
|
|
bool_t xdr_enum(xdrs, ep)
|
600 |
|
|
XDR *xdrs;
|
601 |
|
|
enum_t *ep;
|
602 |
|
|
.sp.5
|
603 |
|
|
bool_t xdr_bool(xdrs, bp)
|
604 |
|
|
XDR *xdrs;
|
605 |
|
|
bool_t *bp;
|
606 |
|
|
.DE
|
607 |
|
|
The second parameters
|
608 |
|
|
.I ep
|
609 |
|
|
and
|
610 |
|
|
.I bp
|
611 |
|
|
are addresses of the associated type that provides data to, or
|
612 |
|
|
receives data from, the stream
|
613 |
|
|
.I xdrs .
|
614 |
|
|
.NH 2
|
615 |
|
|
\&No Data
|
616 |
|
|
.IX "XDR library" "no data"
|
617 |
|
|
.LP
|
618 |
|
|
Occasionally, an XDR routine must be supplied to the RPC system,
|
619 |
|
|
even when no data is passed or required.
|
620 |
|
|
The library provides such a routine:
|
621 |
|
|
.DS
|
622 |
|
|
.ft CW
|
623 |
|
|
bool_t xdr_void(); /* \fIalways returns TRUE\fP */
|
624 |
|
|
.DE
|
625 |
|
|
.NH 2
|
626 |
|
|
\&Constructed Data Type Filters
|
627 |
|
|
.IX "XDR library" "constructed data type filters"
|
628 |
|
|
.LP
|
629 |
|
|
Constructed or compound data type primitives
|
630 |
|
|
require more parameters and perform more complicated functions
|
631 |
|
|
then the primitives discussed above.
|
632 |
|
|
This section includes primitives for
|
633 |
|
|
strings, arrays, unions, and pointers to structures.
|
634 |
|
|
.LP
|
635 |
|
|
Constructed data type primitives may use memory management.
|
636 |
|
|
In many cases, memory is allocated when deserializing data with
|
637 |
|
|
.I XDR_DECODE
|
638 |
|
|
Therefore, the XDR package must provide means to deallocate memory.
|
639 |
|
|
This is done by an XDR operation,
|
640 |
|
|
.I XDR_FREE
|
641 |
|
|
To review, the three XDR directional operations are
|
642 |
|
|
.I XDR_ENCODE ,
|
643 |
|
|
.I XDR_DECODE
|
644 |
|
|
and
|
645 |
|
|
.I XDR_FREE .
|
646 |
|
|
.NH 3
|
647 |
|
|
\&Strings
|
648 |
|
|
.IX "XDR library" "strings"
|
649 |
|
|
.LP
|
650 |
|
|
In C, a string is defined as a sequence of bytes
|
651 |
|
|
terminated by a null byte,
|
652 |
|
|
which is not considered when calculating string length.
|
653 |
|
|
However, when a string is passed or manipulated,
|
654 |
|
|
a pointer to it is employed.
|
655 |
|
|
Therefore, the XDR library defines a string to be a
|
656 |
|
|
.I "char *"
|
657 |
|
|
and not a sequence of characters.
|
658 |
|
|
The external representation of a string is drastically different
|
659 |
|
|
from its internal representation.
|
660 |
|
|
Externally, strings are represented as
|
661 |
|
|
sequences of ASCII characters,
|
662 |
|
|
while internally, they are represented with character pointers.
|
663 |
|
|
Conversion between the two representations
|
664 |
|
|
is accomplished with the routine
|
665 |
|
|
.I xdr_string ():
|
666 |
|
|
.IX xdr_string() "" \fIxdr_string()\fP
|
667 |
|
|
.DS
|
668 |
|
|
.ft CW
|
669 |
|
|
bool_t xdr_string(xdrs, sp, maxlength)
|
670 |
|
|
XDR *xdrs;
|
671 |
|
|
char **sp;
|
672 |
|
|
u_int maxlength;
|
673 |
|
|
.DE
|
674 |
|
|
The first parameter
|
675 |
|
|
.I xdrs
|
676 |
|
|
is the XDR stream handle.
|
677 |
|
|
The second parameter
|
678 |
|
|
.I sp
|
679 |
|
|
is a pointer to a string (type
|
680 |
|
|
.I "char **" .
|
681 |
|
|
The third parameter
|
682 |
|
|
.I maxlength
|
683 |
|
|
specifies the maximum number of bytes allowed during encoding or decoding.
|
684 |
|
|
its value is usually specified by a protocol. For example, a protocol
|
685 |
|
|
specification may say that a file name may be no longer than 255 characters.
|
686 |
|
|
.LP
|
687 |
|
|
The routine returns
|
688 |
|
|
.I FALSE
|
689 |
|
|
if the number of characters exceeds
|
690 |
|
|
.I maxlength ,
|
691 |
|
|
and
|
692 |
|
|
.I TRUE
|
693 |
|
|
if it doesn't.
|
694 |
|
|
.SH
|
695 |
|
|
Keep
|
696 |
|
|
.I maxlength
|
697 |
|
|
small. If it is too big you can blow the heap, since
|
698 |
|
|
.I xdr_string()
|
699 |
|
|
will call
|
700 |
|
|
.I malloc()
|
701 |
|
|
for space.
|
702 |
|
|
.LP
|
703 |
|
|
The behavior of
|
704 |
|
|
.I xdr_string()
|
705 |
|
|
.IX xdr_string() "" \fIxdr_string()\fP
|
706 |
|
|
is similar to the behavior of other routines
|
707 |
|
|
discussed in this section. The direction
|
708 |
|
|
.I XDR_ENCODE
|
709 |
|
|
is easiest to understand. The parameter
|
710 |
|
|
.I sp
|
711 |
|
|
points to a string of a certain length;
|
712 |
|
|
if the string does not exceed
|
713 |
|
|
.I maxlength ,
|
714 |
|
|
the bytes are serialized.
|
715 |
|
|
.LP
|
716 |
|
|
The effect of deserializing a string is subtle.
|
717 |
|
|
First the length of the incoming string is determined;
|
718 |
|
|
it must not exceed
|
719 |
|
|
.I maxlength .
|
720 |
|
|
Next
|
721 |
|
|
.I sp
|
722 |
|
|
is dereferenced; if the the value is
|
723 |
|
|
.I NULL ,
|
724 |
|
|
then a string of the appropriate length is allocated and
|
725 |
|
|
.I *sp
|
726 |
|
|
is set to this string.
|
727 |
|
|
If the original value of
|
728 |
|
|
.I *sp
|
729 |
|
|
is non-null, then the XDR package assumes
|
730 |
|
|
that a target area has been allocated,
|
731 |
|
|
which can hold strings no longer than
|
732 |
|
|
.I maxlength .
|
733 |
|
|
In either case, the string is decoded into the target area.
|
734 |
|
|
The routine then appends a null character to the string.
|
735 |
|
|
.LP
|
736 |
|
|
In the
|
737 |
|
|
.I XDR_FREE
|
738 |
|
|
operation, the string is obtained by dereferencing
|
739 |
|
|
.I sp .
|
740 |
|
|
If the string is not
|
741 |
|
|
.I NULL ,
|
742 |
|
|
it is freed and
|
743 |
|
|
.I *sp
|
744 |
|
|
is set to
|
745 |
|
|
.I NULL .
|
746 |
|
|
In this operation,
|
747 |
|
|
.I xdr_string()
|
748 |
|
|
ignores the
|
749 |
|
|
.I maxlength
|
750 |
|
|
parameter.
|
751 |
|
|
.NH 3
|
752 |
|
|
\&Byte Arrays
|
753 |
|
|
.IX "XDR library" "byte arrays"
|
754 |
|
|
.LP
|
755 |
|
|
Often variable-length arrays of bytes are preferable to strings.
|
756 |
|
|
Byte arrays differ from strings in the following three ways:
|
757 |
|
|
1) the length of the array (the byte count) is explicitly
|
758 |
|
|
located in an unsigned integer,
|
759 |
|
|
2) the byte sequence is not terminated by a null character, and
|
760 |
|
|
3) the external representation of the bytes is the same as their
|
761 |
|
|
internal representation.
|
762 |
|
|
The primitive
|
763 |
|
|
.I xdr_bytes()
|
764 |
|
|
.IX xdr_bytes() "" \fIxdr_bytes()\fP
|
765 |
|
|
converts between the internal and external
|
766 |
|
|
representations of byte arrays:
|
767 |
|
|
.DS
|
768 |
|
|
.ft CW
|
769 |
|
|
bool_t xdr_bytes(xdrs, bpp, lp, maxlength)
|
770 |
|
|
XDR *xdrs;
|
771 |
|
|
char **bpp;
|
772 |
|
|
u_int *lp;
|
773 |
|
|
u_int maxlength;
|
774 |
|
|
.DE
|
775 |
|
|
The usage of the first, second and fourth parameters
|
776 |
|
|
are identical to the first, second and third parameters of
|
777 |
|
|
.I xdr_string (),
|
778 |
|
|
respectively.
|
779 |
|
|
The length of the byte area is obtained by dereferencing
|
780 |
|
|
.I lp
|
781 |
|
|
when serializing;
|
782 |
|
|
.I *lp
|
783 |
|
|
is set to the byte length when deserializing.
|
784 |
|
|
.NH 3
|
785 |
|
|
\&Arrays
|
786 |
|
|
.IX "XDR library" "arrays"
|
787 |
|
|
.LP
|
788 |
|
|
The XDR library package provides a primitive
|
789 |
|
|
for handling arrays of arbitrary elements.
|
790 |
|
|
The
|
791 |
|
|
.I xdr_bytes()
|
792 |
|
|
routine treats a subset of generic arrays,
|
793 |
|
|
in which the size of array elements is known to be 1,
|
794 |
|
|
and the external description of each element is built-in.
|
795 |
|
|
The generic array primitive,
|
796 |
|
|
.I xdr_array() ,
|
797 |
|
|
.IX xdr_array() "" \fIxdr_array()\fP
|
798 |
|
|
requires parameters identical to those of
|
799 |
|
|
.I xdr_bytes()
|
800 |
|
|
plus two more:
|
801 |
|
|
the size of array elements,
|
802 |
|
|
and an XDR routine to handle each of the elements.
|
803 |
|
|
This routine is called to encode or decode
|
804 |
|
|
each element of the array.
|
805 |
|
|
.DS
|
806 |
|
|
.ft CW
|
807 |
|
|
bool_t
|
808 |
|
|
xdr_array(xdrs, ap, lp, maxlength, elementsiz, xdr_element)
|
809 |
|
|
XDR *xdrs;
|
810 |
|
|
char **ap;
|
811 |
|
|
u_int *lp;
|
812 |
|
|
u_int maxlength;
|
813 |
|
|
u_int elementsiz;
|
814 |
|
|
bool_t (*xdr_element)();
|
815 |
|
|
.DE
|
816 |
|
|
The parameter
|
817 |
|
|
.I ap
|
818 |
|
|
is the address of the pointer to the array.
|
819 |
|
|
If
|
820 |
|
|
.I *ap
|
821 |
|
|
is
|
822 |
|
|
.I NULL
|
823 |
|
|
when the array is being deserialized,
|
824 |
|
|
XDR allocates an array of the appropriate size and sets
|
825 |
|
|
.I *ap
|
826 |
|
|
to that array.
|
827 |
|
|
The element count of the array is obtained from
|
828 |
|
|
.I *lp
|
829 |
|
|
when the array is serialized;
|
830 |
|
|
.I *lp
|
831 |
|
|
is set to the array length when the array is deserialized.
|
832 |
|
|
The parameter
|
833 |
|
|
.I maxlength
|
834 |
|
|
is the maximum number of elements that the array is allowed to have;
|
835 |
|
|
.I elementsiz
|
836 |
|
|
is the byte size of each element of the array
|
837 |
|
|
(the C function
|
838 |
|
|
.I sizeof()
|
839 |
|
|
can be used to obtain this value).
|
840 |
|
|
The
|
841 |
|
|
.I xdr_element()
|
842 |
|
|
.IX xdr_element() "" \fIxdr_element()\fP
|
843 |
|
|
routine is called to serialize, deserialize, or free
|
844 |
|
|
each element of the array.
|
845 |
|
|
.br
|
846 |
|
|
.LP
|
847 |
|
|
Before defining more constructed data types, it is appropriate to
|
848 |
|
|
present three examples.
|
849 |
|
|
.LP
|
850 |
|
|
.I "Example A:"
|
851 |
|
|
.br
|
852 |
|
|
A user on a networked machine can be identified by
|
853 |
|
|
(a) the machine name, such as
|
854 |
|
|
.I krypton :
|
855 |
|
|
see the
|
856 |
|
|
.I gethostname
|
857 |
|
|
man page; (b) the user's UID: see the
|
858 |
|
|
.I geteuid
|
859 |
|
|
man page; and (c) the group numbers to which the user belongs:
|
860 |
|
|
see the
|
861 |
|
|
.I getgroups
|
862 |
|
|
man page. A structure with this information and its associated
|
863 |
|
|
XDR routine could be coded like this:
|
864 |
|
|
.ie t .DS
|
865 |
|
|
.el .DS L
|
866 |
|
|
.ft CW
|
867 |
|
|
struct netuser {
|
868 |
|
|
char *nu_machinename;
|
869 |
|
|
int nu_uid;
|
870 |
|
|
u_int nu_glen;
|
871 |
|
|
int *nu_gids;
|
872 |
|
|
};
|
873 |
|
|
#define NLEN 255 /* \fImachine names < 256 chars\fP */
|
874 |
|
|
#define NGRPS 20 /* \fIuser can't be in > 20 groups\fP */
|
875 |
|
|
.sp.5
|
876 |
|
|
bool_t
|
877 |
|
|
xdr_netuser(xdrs, nup)
|
878 |
|
|
XDR *xdrs;
|
879 |
|
|
struct netuser *nup;
|
880 |
|
|
{
|
881 |
|
|
return(xdr_string(xdrs, &nup->nu_machinename, NLEN) &&
|
882 |
|
|
xdr_int(xdrs, &nup->nu_uid) &&
|
883 |
|
|
xdr_array(xdrs, &nup->nu_gids, &nup->nu_glen,
|
884 |
|
|
NGRPS, sizeof (int), xdr_int));
|
885 |
|
|
}
|
886 |
|
|
.DE
|
887 |
|
|
.LP
|
888 |
|
|
.I "Example B:"
|
889 |
|
|
.br
|
890 |
|
|
A party of network users could be implemented
|
891 |
|
|
as an array of
|
892 |
|
|
.I netuser
|
893 |
|
|
structure.
|
894 |
|
|
The declaration and its associated XDR routines
|
895 |
|
|
are as follows:
|
896 |
|
|
.ie t .DS
|
897 |
|
|
.el .DS L
|
898 |
|
|
.ft CW
|
899 |
|
|
struct party {
|
900 |
|
|
u_int p_len;
|
901 |
|
|
struct netuser *p_nusers;
|
902 |
|
|
};
|
903 |
|
|
#define PLEN 500 /* \fImax number of users in a party\fP */
|
904 |
|
|
.sp.5
|
905 |
|
|
bool_t
|
906 |
|
|
xdr_party(xdrs, pp)
|
907 |
|
|
XDR *xdrs;
|
908 |
|
|
struct party *pp;
|
909 |
|
|
{
|
910 |
|
|
return(xdr_array(xdrs, &pp->p_nusers, &pp->p_len, PLEN,
|
911 |
|
|
sizeof (struct netuser), xdr_netuser));
|
912 |
|
|
}
|
913 |
|
|
.DE
|
914 |
|
|
.LP
|
915 |
|
|
.I "Example C:"
|
916 |
|
|
.br
|
917 |
|
|
The well-known parameters to
|
918 |
|
|
.I main ,
|
919 |
|
|
.I argc
|
920 |
|
|
and
|
921 |
|
|
.I argv
|
922 |
|
|
can be combined into a structure.
|
923 |
|
|
An array of these structures can make up a history of commands.
|
924 |
|
|
The declarations and XDR routines might look like:
|
925 |
|
|
.ie t .DS
|
926 |
|
|
.el .DS L
|
927 |
|
|
.ft CW
|
928 |
|
|
struct cmd {
|
929 |
|
|
u_int c_argc;
|
930 |
|
|
char **c_argv;
|
931 |
|
|
};
|
932 |
|
|
#define ALEN 1000 /* \fIargs cannot be > 1000 chars\fP */
|
933 |
|
|
#define NARGC 100 /* \fIcommands cannot have > 100 args\fP */
|
934 |
|
|
|
935 |
|
|
struct history {
|
936 |
|
|
u_int h_len;
|
937 |
|
|
struct cmd *h_cmds;
|
938 |
|
|
};
|
939 |
|
|
#define NCMDS 75 /* \fIhistory is no more than 75 commands\fP */
|
940 |
|
|
|
941 |
|
|
bool_t
|
942 |
|
|
xdr_wrap_string(xdrs, sp)
|
943 |
|
|
XDR *xdrs;
|
944 |
|
|
char **sp;
|
945 |
|
|
{
|
946 |
|
|
return(xdr_string(xdrs, sp, ALEN));
|
947 |
|
|
}
|
948 |
|
|
.DE
|
949 |
|
|
.ie t .DS
|
950 |
|
|
.el .DS L
|
951 |
|
|
.ft CW
|
952 |
|
|
bool_t
|
953 |
|
|
xdr_cmd(xdrs, cp)
|
954 |
|
|
XDR *xdrs;
|
955 |
|
|
struct cmd *cp;
|
956 |
|
|
{
|
957 |
|
|
return(xdr_array(xdrs, &cp->c_argv, &cp->c_argc, NARGC,
|
958 |
|
|
sizeof (char *), xdr_wrap_string));
|
959 |
|
|
}
|
960 |
|
|
.DE
|
961 |
|
|
.ie t .DS
|
962 |
|
|
.el .DS L
|
963 |
|
|
.ft CW
|
964 |
|
|
bool_t
|
965 |
|
|
xdr_history(xdrs, hp)
|
966 |
|
|
XDR *xdrs;
|
967 |
|
|
struct history *hp;
|
968 |
|
|
{
|
969 |
|
|
return(xdr_array(xdrs, &hp->h_cmds, &hp->h_len, NCMDS,
|
970 |
|
|
sizeof (struct cmd), xdr_cmd));
|
971 |
|
|
}
|
972 |
|
|
.DE
|
973 |
|
|
The most confusing part of this example is that the routine
|
974 |
|
|
.I xdr_wrap_string()
|
975 |
|
|
is needed to package the
|
976 |
|
|
.I xdr_string()
|
977 |
|
|
routine, because the implementation of
|
978 |
|
|
.I xdr_array()
|
979 |
|
|
only passes two parameters to the array element description routine;
|
980 |
|
|
.I xdr_wrap_string()
|
981 |
|
|
supplies the third parameter to
|
982 |
|
|
.I xdr_string ().
|
983 |
|
|
.LP
|
984 |
|
|
By now the recursive nature of the XDR library should be obvious.
|
985 |
|
|
Let's continue with more constructed data types.
|
986 |
|
|
.NH 3
|
987 |
|
|
\&Opaque Data
|
988 |
|
|
.IX "XDR library" "opaque data"
|
989 |
|
|
.LP
|
990 |
|
|
In some protocols, handles are passed from a server to client.
|
991 |
|
|
The client passes the handle back to the server at some later time.
|
992 |
|
|
Handles are never inspected by clients;
|
993 |
|
|
they are obtained and submitted.
|
994 |
|
|
That is to say, handles are opaque.
|
995 |
|
|
The
|
996 |
|
|
.I xdr_opaque()
|
997 |
|
|
.IX xdr_opaque() "" \fIxdr_opaque()\fP
|
998 |
|
|
primitive is used for describing fixed sized, opaque bytes.
|
999 |
|
|
.DS
|
1000 |
|
|
.ft CW
|
1001 |
|
|
bool_t xdr_opaque(xdrs, p, len)
|
1002 |
|
|
XDR *xdrs;
|
1003 |
|
|
char *p;
|
1004 |
|
|
u_int len;
|
1005 |
|
|
.DE
|
1006 |
|
|
The parameter
|
1007 |
|
|
.I p
|
1008 |
|
|
is the location of the bytes;
|
1009 |
|
|
.I len
|
1010 |
|
|
is the number of bytes in the opaque object.
|
1011 |
|
|
By definition, the actual data
|
1012 |
|
|
contained in the opaque object are not machine portable.
|
1013 |
|
|
.NH 3
|
1014 |
|
|
\&Fixed Sized Arrays
|
1015 |
|
|
.IX "XDR library" "fixed sized arrays"
|
1016 |
|
|
.LP
|
1017 |
|
|
The XDR library provides a primitive,
|
1018 |
|
|
.I xdr_vector (),
|
1019 |
|
|
for fixed-length arrays.
|
1020 |
|
|
.ie t .DS
|
1021 |
|
|
.el .DS L
|
1022 |
|
|
.ft CW
|
1023 |
|
|
#define NLEN 255 /* \fImachine names must be < 256 chars\fP */
|
1024 |
|
|
#define NGRPS 20 /* \fIuser belongs to exactly 20 groups\fP */
|
1025 |
|
|
.sp.5
|
1026 |
|
|
struct netuser {
|
1027 |
|
|
char *nu_machinename;
|
1028 |
|
|
int nu_uid;
|
1029 |
|
|
int nu_gids[NGRPS];
|
1030 |
|
|
};
|
1031 |
|
|
.sp.5
|
1032 |
|
|
bool_t
|
1033 |
|
|
xdr_netuser(xdrs, nup)
|
1034 |
|
|
XDR *xdrs;
|
1035 |
|
|
struct netuser *nup;
|
1036 |
|
|
{
|
1037 |
|
|
int i;
|
1038 |
|
|
.sp.5
|
1039 |
|
|
if (!xdr_string(xdrs, &nup->nu_machinename, NLEN))
|
1040 |
|
|
return(FALSE);
|
1041 |
|
|
if (!xdr_int(xdrs, &nup->nu_uid))
|
1042 |
|
|
return(FALSE);
|
1043 |
|
|
if (!xdr_vector(xdrs, nup->nu_gids, NGRPS, sizeof(int),
|
1044 |
|
|
xdr_int)) {
|
1045 |
|
|
return(FALSE);
|
1046 |
|
|
}
|
1047 |
|
|
return(TRUE);
|
1048 |
|
|
}
|
1049 |
|
|
.DE
|
1050 |
|
|
.NH 3
|
1051 |
|
|
\&Discriminated Unions
|
1052 |
|
|
.IX "XDR library" "discriminated unions"
|
1053 |
|
|
.LP
|
1054 |
|
|
The XDR library supports discriminated unions.
|
1055 |
|
|
A discriminated union is a C union and an
|
1056 |
|
|
.I enum_t
|
1057 |
|
|
value that selects an \*Qarm\*U of the union.
|
1058 |
|
|
.DS
|
1059 |
|
|
.ft CW
|
1060 |
|
|
struct xdr_discrim {
|
1061 |
|
|
enum_t value;
|
1062 |
|
|
bool_t (*proc)();
|
1063 |
|
|
};
|
1064 |
|
|
.sp.5
|
1065 |
|
|
bool_t xdr_union(xdrs, dscmp, unp, arms, defaultarm)
|
1066 |
|
|
XDR *xdrs;
|
1067 |
|
|
enum_t *dscmp;
|
1068 |
|
|
char *unp;
|
1069 |
|
|
struct xdr_discrim *arms;
|
1070 |
|
|
bool_t (*defaultarm)(); /* \fImay equal NULL\fP */
|
1071 |
|
|
.DE
|
1072 |
|
|
First the routine translates the discriminant of the union located at
|
1073 |
|
|
.I *dscmp .
|
1074 |
|
|
The discriminant is always an
|
1075 |
|
|
.I enum_t .
|
1076 |
|
|
Next the union located at
|
1077 |
|
|
.I *unp
|
1078 |
|
|
is translated.
|
1079 |
|
|
The parameter
|
1080 |
|
|
.I arms
|
1081 |
|
|
is a pointer to an array of
|
1082 |
|
|
.I xdr_discrim
|
1083 |
|
|
structures.
|
1084 |
|
|
Each structure contains an ordered pair of
|
1085 |
|
|
.I [value,proc] .
|
1086 |
|
|
If the union's discriminant is equal to the associated
|
1087 |
|
|
.I value ,
|
1088 |
|
|
then the
|
1089 |
|
|
.I proc
|
1090 |
|
|
is called to translate the union.
|
1091 |
|
|
The end of the
|
1092 |
|
|
.I xdr_discrim
|
1093 |
|
|
structure array is denoted by a routine of value
|
1094 |
|
|
.I NULL
|
1095 |
|
|
(0). If the discriminant is not found in the
|
1096 |
|
|
.I arms
|
1097 |
|
|
array, then the
|
1098 |
|
|
.I defaultarm
|
1099 |
|
|
procedure is called if it is non-null;
|
1100 |
|
|
otherwise the routine returns
|
1101 |
|
|
.I FALSE .
|
1102 |
|
|
.LP
|
1103 |
|
|
.I "Example D:"
|
1104 |
|
|
Suppose the type of a union may be integer,
|
1105 |
|
|
character pointer (a string), or a
|
1106 |
|
|
.I gnumbers
|
1107 |
|
|
structure.
|
1108 |
|
|
Also, assume the union and its current type
|
1109 |
|
|
are declared in a structure.
|
1110 |
|
|
The declaration is:
|
1111 |
|
|
.ie t .DS
|
1112 |
|
|
.el .DS L
|
1113 |
|
|
.ft CW
|
1114 |
|
|
enum utype { INTEGER=1, STRING=2, GNUMBERS=3 };
|
1115 |
|
|
.sp.5
|
1116 |
|
|
struct u_tag {
|
1117 |
|
|
enum utype utype; /* \fIthe union's discriminant\fP */
|
1118 |
|
|
union {
|
1119 |
|
|
int ival;
|
1120 |
|
|
char *pval;
|
1121 |
|
|
struct gnumbers gn;
|
1122 |
|
|
} uval;
|
1123 |
|
|
};
|
1124 |
|
|
.DE
|
1125 |
|
|
The following constructs and XDR procedure (de)serialize
|
1126 |
|
|
the discriminated union:
|
1127 |
|
|
.ie t .DS
|
1128 |
|
|
.el .DS L
|
1129 |
|
|
.ft CW
|
1130 |
|
|
struct xdr_discrim u_tag_arms[4] = {
|
1131 |
|
|
{ INTEGER, xdr_int },
|
1132 |
|
|
{ GNUMBERS, xdr_gnumbers }
|
1133 |
|
|
{ STRING, xdr_wrap_string },
|
1134 |
|
|
{ __dontcare__, NULL }
|
1135 |
|
|
/* \fIalways terminate arms with a NULL xdr_proc\fP */
|
1136 |
|
|
}
|
1137 |
|
|
.sp.5
|
1138 |
|
|
bool_t
|
1139 |
|
|
xdr_u_tag(xdrs, utp)
|
1140 |
|
|
XDR *xdrs;
|
1141 |
|
|
struct u_tag *utp;
|
1142 |
|
|
{
|
1143 |
|
|
return(xdr_union(xdrs, &utp->utype, &utp->uval,
|
1144 |
|
|
u_tag_arms, NULL));
|
1145 |
|
|
}
|
1146 |
|
|
.DE
|
1147 |
|
|
The routine
|
1148 |
|
|
.I xdr_gnumbers()
|
1149 |
|
|
was presented above in
|
1150 |
|
|
.I "The XDR Library"
|
1151 |
|
|
section.
|
1152 |
|
|
.I xdr_wrap_string()
|
1153 |
|
|
was presented in example C.
|
1154 |
|
|
The default
|
1155 |
|
|
.I arm
|
1156 |
|
|
parameter to
|
1157 |
|
|
.I xdr_union()
|
1158 |
|
|
(the last parameter) is
|
1159 |
|
|
.I NULL
|
1160 |
|
|
in this example. Therefore the value of the union's discriminant
|
1161 |
|
|
may legally take on only values listed in the
|
1162 |
|
|
.I u_tag_arms
|
1163 |
|
|
array. This example also demonstrates that
|
1164 |
|
|
the elements of the arm's array do not need to be sorted.
|
1165 |
|
|
.LP
|
1166 |
|
|
It is worth pointing out that the values of the discriminant
|
1167 |
|
|
may be sparse, though in this example they are not.
|
1168 |
|
|
It is always good
|
1169 |
|
|
practice to assign explicitly integer values to each element of the
|
1170 |
|
|
discriminant's type.
|
1171 |
|
|
This practice both documents the external
|
1172 |
|
|
representation of the discriminant and guarantees that different
|
1173 |
|
|
C compilers emit identical discriminant values.
|
1174 |
|
|
.LP
|
1175 |
|
|
Exercise: Implement
|
1176 |
|
|
.I xdr_union()
|
1177 |
|
|
using the other primitives in this section.
|
1178 |
|
|
.NH 3
|
1179 |
|
|
\&Pointers
|
1180 |
|
|
.IX "XDR library" "pointers"
|
1181 |
|
|
.LP
|
1182 |
|
|
In C it is often convenient to put pointers
|
1183 |
|
|
to another structure within a structure.
|
1184 |
|
|
The
|
1185 |
|
|
.I xdr_reference()
|
1186 |
|
|
.IX xdr_reference() "" \fIxdr_reference()\fP
|
1187 |
|
|
primitive makes it easy to serialize, deserialize, and free
|
1188 |
|
|
these referenced structures.
|
1189 |
|
|
.DS
|
1190 |
|
|
.ft CW
|
1191 |
|
|
bool_t xdr_reference(xdrs, pp, size, proc)
|
1192 |
|
|
XDR *xdrs;
|
1193 |
|
|
char **pp;
|
1194 |
|
|
u_int ssize;
|
1195 |
|
|
bool_t (*proc)();
|
1196 |
|
|
.DE
|
1197 |
|
|
.LP
|
1198 |
|
|
Parameter
|
1199 |
|
|
.I pp
|
1200 |
|
|
is the address of
|
1201 |
|
|
the pointer to the structure;
|
1202 |
|
|
parameter
|
1203 |
|
|
.I ssize
|
1204 |
|
|
is the size in bytes of the structure (use the C function
|
1205 |
|
|
.I sizeof()
|
1206 |
|
|
to obtain this value); and
|
1207 |
|
|
.I proc
|
1208 |
|
|
is the XDR routine that describes the structure.
|
1209 |
|
|
When decoding data, storage is allocated if
|
1210 |
|
|
.I *pp
|
1211 |
|
|
is
|
1212 |
|
|
.I NULL .
|
1213 |
|
|
.LP
|
1214 |
|
|
There is no need for a primitive
|
1215 |
|
|
.I xdr_struct()
|
1216 |
|
|
to describe structures within structures,
|
1217 |
|
|
because pointers are always sufficient.
|
1218 |
|
|
.LP
|
1219 |
|
|
Exercise: Implement
|
1220 |
|
|
.I xdr_reference()
|
1221 |
|
|
using
|
1222 |
|
|
.I xdr_array ().
|
1223 |
|
|
Warning:
|
1224 |
|
|
.I xdr_reference()
|
1225 |
|
|
and
|
1226 |
|
|
.I xdr_array()
|
1227 |
|
|
are NOT interchangeable external representations of data.
|
1228 |
|
|
.LP
|
1229 |
|
|
.I "Example E:"
|
1230 |
|
|
Suppose there is a structure containing a person's name
|
1231 |
|
|
and a pointer to a
|
1232 |
|
|
.I gnumbers
|
1233 |
|
|
structure containing the person's gross assets and liabilities.
|
1234 |
|
|
The construct is:
|
1235 |
|
|
.DS
|
1236 |
|
|
.ft CW
|
1237 |
|
|
struct pgn {
|
1238 |
|
|
char *name;
|
1239 |
|
|
struct gnumbers *gnp;
|
1240 |
|
|
};
|
1241 |
|
|
.DE
|
1242 |
|
|
The corresponding XDR routine for this structure is:
|
1243 |
|
|
.DS
|
1244 |
|
|
.ft CW
|
1245 |
|
|
bool_t
|
1246 |
|
|
xdr_pgn(xdrs, pp)
|
1247 |
|
|
XDR *xdrs;
|
1248 |
|
|
struct pgn *pp;
|
1249 |
|
|
{
|
1250 |
|
|
if (xdr_string(xdrs, &pp->name, NLEN) &&
|
1251 |
|
|
xdr_reference(xdrs, &pp->gnp,
|
1252 |
|
|
sizeof(struct gnumbers), xdr_gnumbers))
|
1253 |
|
|
return(TRUE);
|
1254 |
|
|
return(FALSE);
|
1255 |
|
|
}
|
1256 |
|
|
.DE
|
1257 |
|
|
.IX "pointer semantics and XDR"
|
1258 |
|
|
.I "Pointer Semantics and XDR"
|
1259 |
|
|
.LP
|
1260 |
|
|
In many applications, C programmers attach double meaning to
|
1261 |
|
|
the values of a pointer. Typically the value
|
1262 |
|
|
.I NULL
|
1263 |
|
|
(or zero) means data is not needed,
|
1264 |
|
|
yet some application-specific interpretation applies.
|
1265 |
|
|
In essence, the C programmer is encoding
|
1266 |
|
|
a discriminated union efficiently
|
1267 |
|
|
by overloading the interpretation of the value of a pointer.
|
1268 |
|
|
For instance, in example E a
|
1269 |
|
|
.I NULL
|
1270 |
|
|
pointer value for
|
1271 |
|
|
.I gnp
|
1272 |
|
|
could indicate that
|
1273 |
|
|
the person's assets and liabilities are unknown.
|
1274 |
|
|
That is, the pointer value encodes two things:
|
1275 |
|
|
whether or not the data is known;
|
1276 |
|
|
and if it is known, where it is located in memory.
|
1277 |
|
|
Linked lists are an extreme example of the use
|
1278 |
|
|
of application-specific pointer interpretation.
|
1279 |
|
|
.LP
|
1280 |
|
|
The primitive
|
1281 |
|
|
.I xdr_reference()
|
1282 |
|
|
.IX xdr_reference() "" \fIxdr_reference()\fP
|
1283 |
|
|
cannot and does not attach any special
|
1284 |
|
|
meaning to a null-value pointer during serialization.
|
1285 |
|
|
That is, passing an address of a pointer whose value is
|
1286 |
|
|
.I NULL
|
1287 |
|
|
to
|
1288 |
|
|
.I xdr_reference()
|
1289 |
|
|
when serialing data will most likely cause a memory fault and, on the UNIX
|
1290 |
|
|
system, a core dump.
|
1291 |
|
|
.LP
|
1292 |
|
|
.I xdr_pointer()
|
1293 |
|
|
correctly handles
|
1294 |
|
|
.I NULL
|
1295 |
|
|
pointers. For more information about its use, see
|
1296 |
|
|
the
|
1297 |
|
|
.I "Linked Lists"
|
1298 |
|
|
topics below.
|
1299 |
|
|
.LP
|
1300 |
|
|
.I Exercise:
|
1301 |
|
|
After reading the section on
|
1302 |
|
|
.I "Linked Lists" ,
|
1303 |
|
|
return here and extend example E so that
|
1304 |
|
|
it can correctly deal with
|
1305 |
|
|
.I NULL
|
1306 |
|
|
pointer values.
|
1307 |
|
|
.LP
|
1308 |
|
|
.I Exercise:
|
1309 |
|
|
Using the
|
1310 |
|
|
.I xdr_union (),
|
1311 |
|
|
.I xdr_reference()
|
1312 |
|
|
and
|
1313 |
|
|
.I xdr_void()
|
1314 |
|
|
primitives, implement a generic pointer handling primitive
|
1315 |
|
|
that implicitly deals with
|
1316 |
|
|
.I NULL
|
1317 |
|
|
pointers. That is, implement
|
1318 |
|
|
.I xdr_pointer ().
|
1319 |
|
|
.NH 2
|
1320 |
|
|
\&Non-filter Primitives
|
1321 |
|
|
.IX "XDR" "non-filter primitives"
|
1322 |
|
|
.LP
|
1323 |
|
|
XDR streams can be manipulated with
|
1324 |
|
|
the primitives discussed in this section.
|
1325 |
|
|
.DS
|
1326 |
|
|
.ft CW
|
1327 |
|
|
u_int xdr_getpos(xdrs)
|
1328 |
|
|
XDR *xdrs;
|
1329 |
|
|
.sp.5
|
1330 |
|
|
bool_t xdr_setpos(xdrs, pos)
|
1331 |
|
|
XDR *xdrs;
|
1332 |
|
|
u_int pos;
|
1333 |
|
|
.sp.5
|
1334 |
|
|
xdr_destroy(xdrs)
|
1335 |
|
|
XDR *xdrs;
|
1336 |
|
|
.DE
|
1337 |
|
|
The routine
|
1338 |
|
|
.I xdr_getpos()
|
1339 |
|
|
.IX xdr_getpos() "" \fIxdr_getpos()\fP
|
1340 |
|
|
returns an unsigned integer
|
1341 |
|
|
that describes the current position in the data stream.
|
1342 |
|
|
Warning: In some XDR streams, the returned value of
|
1343 |
|
|
.I xdr_getpos()
|
1344 |
|
|
is meaningless;
|
1345 |
|
|
the routine returns a \-1 in this case
|
1346 |
|
|
(though \-1 should be a legitimate value).
|
1347 |
|
|
.LP
|
1348 |
|
|
The routine
|
1349 |
|
|
.I xdr_setpos()
|
1350 |
|
|
.IX xdr_setpos() "" \fIxdr_setpos()\fP
|
1351 |
|
|
sets a stream position to
|
1352 |
|
|
.I pos .
|
1353 |
|
|
Warning: In some XDR streams, setting a position is impossible;
|
1354 |
|
|
in such cases,
|
1355 |
|
|
.I xdr_setpos()
|
1356 |
|
|
will return
|
1357 |
|
|
.I FALSE .
|
1358 |
|
|
This routine will also fail if the requested position is out-of-bounds.
|
1359 |
|
|
The definition of bounds varies from stream to stream.
|
1360 |
|
|
.LP
|
1361 |
|
|
The
|
1362 |
|
|
.I xdr_destroy()
|
1363 |
|
|
.IX xdr_destroy() "" \fIxdr_destroy()\fP
|
1364 |
|
|
primitive destroys the XDR stream.
|
1365 |
|
|
Usage of the stream
|
1366 |
|
|
after calling this routine is undefined.
|
1367 |
|
|
.NH 2
|
1368 |
|
|
\&XDR Operation Directions
|
1369 |
|
|
.IX XDR "operation directions"
|
1370 |
|
|
.IX "direction of XDR operations"
|
1371 |
|
|
.LP
|
1372 |
|
|
At times you may wish to optimize XDR routines by taking
|
1373 |
|
|
advantage of the direction of the operation \(em
|
1374 |
|
|
.I XDR_ENCODE
|
1375 |
|
|
.I XDR_DECODE
|
1376 |
|
|
or
|
1377 |
|
|
.I XDR_FREE
|
1378 |
|
|
The value
|
1379 |
|
|
.I xdrs->x_op
|
1380 |
|
|
always contains the direction of the XDR operation.
|
1381 |
|
|
Programmers are not encouraged to take advantage of this information.
|
1382 |
|
|
Therefore, no example is presented here. However, an example in the
|
1383 |
|
|
.I "Linked Lists"
|
1384 |
|
|
topic below, demonstrates the usefulness of the
|
1385 |
|
|
.I xdrs->x_op
|
1386 |
|
|
field.
|
1387 |
|
|
.NH 2
|
1388 |
|
|
\&XDR Stream Access
|
1389 |
|
|
.IX "XDR" "stream access"
|
1390 |
|
|
.LP
|
1391 |
|
|
An XDR stream is obtained by calling the appropriate creation routine.
|
1392 |
|
|
These creation routines take arguments that are tailored to the
|
1393 |
|
|
specific properties of the stream.
|
1394 |
|
|
.LP
|
1395 |
|
|
Streams currently exist for (de)serialization of data to or from
|
1396 |
|
|
standard I/O
|
1397 |
|
|
.I FILE
|
1398 |
|
|
streams, TCP/IP connections and UNIX files, and memory.
|
1399 |
|
|
.NH 3
|
1400 |
|
|
\&Standard I/O Streams
|
1401 |
|
|
.IX "XDR" "standard I/O streams"
|
1402 |
|
|
.LP
|
1403 |
|
|
XDR streams can be interfaced to standard I/O using the
|
1404 |
|
|
.I xdrstdio_create()
|
1405 |
|
|
.IX xdrstdio_create() "" \fIxdrstdio_create()\fP
|
1406 |
|
|
routine as follows:
|
1407 |
|
|
.DS
|
1408 |
|
|
.ft CW
|
1409 |
|
|
#include
|
1410 |
|
|
#include /* \fIxdr streams part of rpc\fP */
|
1411 |
|
|
.sp.5
|
1412 |
|
|
void
|
1413 |
|
|
xdrstdio_create(xdrs, fp, x_op)
|
1414 |
|
|
XDR *xdrs;
|
1415 |
|
|
FILE *fp;
|
1416 |
|
|
enum xdr_op x_op;
|
1417 |
|
|
.DE
|
1418 |
|
|
The routine
|
1419 |
|
|
.I xdrstdio_create()
|
1420 |
|
|
initializes an XDR stream pointed to by
|
1421 |
|
|
.I xdrs .
|
1422 |
|
|
The XDR stream interfaces to the standard I/O library.
|
1423 |
|
|
Parameter
|
1424 |
|
|
.I fp
|
1425 |
|
|
is an open file, and
|
1426 |
|
|
.I x_op
|
1427 |
|
|
is an XDR direction.
|
1428 |
|
|
.NH 3
|
1429 |
|
|
\&Memory Streams
|
1430 |
|
|
.IX "XDR" "memory streams"
|
1431 |
|
|
.LP
|
1432 |
|
|
Memory streams allow the streaming of data into or out of
|
1433 |
|
|
a specified area of memory:
|
1434 |
|
|
.DS
|
1435 |
|
|
.ft CW
|
1436 |
|
|
#include
|
1437 |
|
|
.sp.5
|
1438 |
|
|
void
|
1439 |
|
|
xdrmem_create(xdrs, addr, len, x_op)
|
1440 |
|
|
XDR *xdrs;
|
1441 |
|
|
char *addr;
|
1442 |
|
|
u_int len;
|
1443 |
|
|
enum xdr_op x_op;
|
1444 |
|
|
.DE
|
1445 |
|
|
The routine
|
1446 |
|
|
.I xdrmem_create()
|
1447 |
|
|
.IX xdrmem_create() "" \fIxdrmem_create()\fP
|
1448 |
|
|
initializes an XDR stream in local memory.
|
1449 |
|
|
The memory is pointed to by parameter
|
1450 |
|
|
.I addr ;
|
1451 |
|
|
parameter
|
1452 |
|
|
.I len
|
1453 |
|
|
is the length in bytes of the memory.
|
1454 |
|
|
The parameters
|
1455 |
|
|
.I xdrs
|
1456 |
|
|
and
|
1457 |
|
|
.I x_op
|
1458 |
|
|
are identical to the corresponding parameters of
|
1459 |
|
|
.I xdrstdio_create ().
|
1460 |
|
|
Currently, the UDP/IP implementation of RPC uses
|
1461 |
|
|
.I xdrmem_create ().
|
1462 |
|
|
Complete call or result messages are built in memory before calling the
|
1463 |
|
|
.I sendto()
|
1464 |
|
|
system routine.
|
1465 |
|
|
.NH 3
|
1466 |
|
|
\&Record (TCP/IP) Streams
|
1467 |
|
|
.IX "XDR" "record (TCP/IP) streams"
|
1468 |
|
|
.LP
|
1469 |
|
|
A record stream is an XDR stream built on top of
|
1470 |
|
|
a record marking standard that is built on top of the
|
1471 |
|
|
UNIX file or 4.2 BSD connection interface.
|
1472 |
|
|
.DS
|
1473 |
|
|
.ft CW
|
1474 |
|
|
#include /* \fIxdr streams part of rpc\fP */
|
1475 |
|
|
.sp.5
|
1476 |
|
|
xdrrec_create(xdrs,
|
1477 |
|
|
sendsize, recvsize, iohandle, readproc, writeproc)
|
1478 |
|
|
XDR *xdrs;
|
1479 |
|
|
u_int sendsize, recvsize;
|
1480 |
|
|
char *iohandle;
|
1481 |
|
|
int (*readproc)(), (*writeproc)();
|
1482 |
|
|
.DE
|
1483 |
|
|
The routine
|
1484 |
|
|
.I xdrrec_create()
|
1485 |
|
|
provides an XDR stream interface that allows for a bidirectional,
|
1486 |
|
|
arbitrarily long sequence of records.
|
1487 |
|
|
The contents of the records are meant to be data in XDR form.
|
1488 |
|
|
The stream's primary use is for interfacing RPC to TCP connections.
|
1489 |
|
|
However, it can be used to stream data into or out of normal
|
1490 |
|
|
UNIX files.
|
1491 |
|
|
.LP
|
1492 |
|
|
The parameter
|
1493 |
|
|
.I xdrs
|
1494 |
|
|
is similar to the corresponding parameter described above.
|
1495 |
|
|
The stream does its own data buffering similar to that of standard I/O.
|
1496 |
|
|
The parameters
|
1497 |
|
|
.I sendsize
|
1498 |
|
|
and
|
1499 |
|
|
.I recvsize
|
1500 |
|
|
determine the size in bytes of the output and input buffers, respectively;
|
1501 |
|
|
if their values are zero (0), then predetermined defaults are used.
|
1502 |
|
|
When a buffer needs to be filled or flushed, the routine
|
1503 |
|
|
.I readproc()
|
1504 |
|
|
or
|
1505 |
|
|
.I writeproc()
|
1506 |
|
|
is called, respectively.
|
1507 |
|
|
The usage and behavior of these
|
1508 |
|
|
routines are similar to the UNIX system calls
|
1509 |
|
|
.I read()
|
1510 |
|
|
and
|
1511 |
|
|
.I write ().
|
1512 |
|
|
However,
|
1513 |
|
|
the first parameter to each of these routines is the opaque parameter
|
1514 |
|
|
.I iohandle .
|
1515 |
|
|
The other two parameters
|
1516 |
|
|
.I buf ""
|
1517 |
|
|
and
|
1518 |
|
|
.I nbytes )
|
1519 |
|
|
and the results
|
1520 |
|
|
(byte count) are identical to the system routines.
|
1521 |
|
|
If
|
1522 |
|
|
.I xxx
|
1523 |
|
|
is
|
1524 |
|
|
.I readproc()
|
1525 |
|
|
or
|
1526 |
|
|
.I writeproc (),
|
1527 |
|
|
then it has the following form:
|
1528 |
|
|
.DS
|
1529 |
|
|
.ft CW
|
1530 |
|
|
.ft I
|
1531 |
|
|
/*
|
1532 |
|
|
* returns the actual number of bytes transferred.
|
1533 |
|
|
* -1 is an error
|
1534 |
|
|
*/
|
1535 |
|
|
.ft CW
|
1536 |
|
|
int
|
1537 |
|
|
xxx(iohandle, buf, len)
|
1538 |
|
|
char *iohandle;
|
1539 |
|
|
char *buf;
|
1540 |
|
|
int nbytes;
|
1541 |
|
|
.DE
|
1542 |
|
|
The XDR stream provides means for delimiting records in the byte stream.
|
1543 |
|
|
The implementation details of delimiting records in a stream are
|
1544 |
|
|
discussed in the
|
1545 |
|
|
.I "Advanced Topics"
|
1546 |
|
|
topic below.
|
1547 |
|
|
The primitives that are specific to record streams are as follows:
|
1548 |
|
|
.DS
|
1549 |
|
|
.ft CW
|
1550 |
|
|
bool_t
|
1551 |
|
|
xdrrec_endofrecord(xdrs, flushnow)
|
1552 |
|
|
XDR *xdrs;
|
1553 |
|
|
bool_t flushnow;
|
1554 |
|
|
.sp.5
|
1555 |
|
|
bool_t
|
1556 |
|
|
xdrrec_skiprecord(xdrs)
|
1557 |
|
|
XDR *xdrs;
|
1558 |
|
|
.sp.5
|
1559 |
|
|
bool_t
|
1560 |
|
|
xdrrec_eof(xdrs)
|
1561 |
|
|
XDR *xdrs;
|
1562 |
|
|
.DE
|
1563 |
|
|
The routine
|
1564 |
|
|
.I xdrrec_endofrecord()
|
1565 |
|
|
.IX xdrrec_endofrecord() "" \fIxdrrec_endofrecord()\fP
|
1566 |
|
|
causes the current outgoing data to be marked as a record.
|
1567 |
|
|
If the parameter
|
1568 |
|
|
.I flushnow
|
1569 |
|
|
is
|
1570 |
|
|
.I TRUE ,
|
1571 |
|
|
then the stream's
|
1572 |
|
|
.I writeproc
|
1573 |
|
|
will be called; otherwise,
|
1574 |
|
|
.I writeproc
|
1575 |
|
|
will be called when the output buffer has been filled.
|
1576 |
|
|
.LP
|
1577 |
|
|
The routine
|
1578 |
|
|
.I xdrrec_skiprecord()
|
1579 |
|
|
.IX xdrrec_skiprecord() "" \fIxdrrec_skiprecord()\fP
|
1580 |
|
|
causes an input stream's position to be moved past
|
1581 |
|
|
the current record boundary and onto the
|
1582 |
|
|
beginning of the next record in the stream.
|
1583 |
|
|
.LP
|
1584 |
|
|
If there is no more data in the stream's input buffer,
|
1585 |
|
|
then the routine
|
1586 |
|
|
.I xdrrec_eof()
|
1587 |
|
|
.IX xdrrec_eof() "" \fIxdrrec_eof()\fP
|
1588 |
|
|
returns
|
1589 |
|
|
.I TRUE .
|
1590 |
|
|
That is not to say that there is no more data
|
1591 |
|
|
in the underlying file descriptor.
|
1592 |
|
|
.NH 2
|
1593 |
|
|
\&XDR Stream Implementation
|
1594 |
|
|
.IX "XDR" "stream implementation"
|
1595 |
|
|
.IX "stream implementation in XDR"
|
1596 |
|
|
.LP
|
1597 |
|
|
This section provides the abstract data types needed
|
1598 |
|
|
to implement new instances of XDR streams.
|
1599 |
|
|
.NH 3
|
1600 |
|
|
\&The XDR Object
|
1601 |
|
|
.IX "XDR" "object"
|
1602 |
|
|
.LP
|
1603 |
|
|
The following structure defines the interface to an XDR stream:
|
1604 |
|
|
.ie t .DS
|
1605 |
|
|
.el .DS L
|
1606 |
|
|
.ft CW
|
1607 |
|
|
enum xdr_op { XDR_ENCODE=0, XDR_DECODE=1, XDR_FREE=2 };
|
1608 |
|
|
.sp.5
|
1609 |
|
|
typedef struct {
|
1610 |
|
|
enum xdr_op x_op; /* \fIoperation; fast added param\fP */
|
1611 |
|
|
struct xdr_ops {
|
1612 |
|
|
bool_t (*x_getlong)(); /* \fIget long from stream\fP */
|
1613 |
|
|
bool_t (*x_putlong)(); /* \fIput long to stream\fP */
|
1614 |
|
|
bool_t (*x_getbytes)(); /* \fIget bytes from stream\fP */
|
1615 |
|
|
bool_t (*x_putbytes)(); /* \fIput bytes to stream\fP */
|
1616 |
|
|
u_int (*x_getpostn)(); /* \fIreturn stream offset\fP */
|
1617 |
|
|
bool_t (*x_setpostn)(); /* \fIreposition offset\fP */
|
1618 |
|
|
caddr_t (*x_inline)(); /* \fIptr to buffered data\fP */
|
1619 |
|
|
VOID (*x_destroy)(); /* \fIfree private area\fP */
|
1620 |
|
|
} *x_ops;
|
1621 |
|
|
caddr_t x_public; /* \fIusers' data\fP */
|
1622 |
|
|
caddr_t x_private; /* \fIpointer to private data\fP */
|
1623 |
|
|
caddr_t x_base; /* \fIprivate for position info\fP */
|
1624 |
|
|
int x_handy; /* \fIextra private word\fP */
|
1625 |
|
|
} XDR;
|
1626 |
|
|
.DE
|
1627 |
|
|
The
|
1628 |
|
|
.I x_op
|
1629 |
|
|
field is the current operation being performed on the stream.
|
1630 |
|
|
This field is important to the XDR primitives,
|
1631 |
|
|
but should not affect a stream's implementation.
|
1632 |
|
|
That is, a stream's implementation should not depend
|
1633 |
|
|
on this value.
|
1634 |
|
|
The fields
|
1635 |
|
|
.I x_private ,
|
1636 |
|
|
.I x_base ,
|
1637 |
|
|
and
|
1638 |
|
|
.I x_handy
|
1639 |
|
|
are private to the particular
|
1640 |
|
|
stream's implementation.
|
1641 |
|
|
The field
|
1642 |
|
|
.I x_public
|
1643 |
|
|
is for the XDR client and should never be used by
|
1644 |
|
|
the XDR stream implementations or the XDR primitives.
|
1645 |
|
|
.I x_getpostn() ,
|
1646 |
|
|
.I x_setpostn()
|
1647 |
|
|
and
|
1648 |
|
|
.I x_destroy()
|
1649 |
|
|
are macros for accessing operations. The operation
|
1650 |
|
|
.I x_inline()
|
1651 |
|
|
takes two parameters:
|
1652 |
|
|
an XDR *, and an unsigned integer, which is a byte count.
|
1653 |
|
|
The routine returns a pointer to a piece of
|
1654 |
|
|
the stream's internal buffer.
|
1655 |
|
|
The caller can then use the buffer segment for any purpose.
|
1656 |
|
|
From the stream's point of view, the bytes in the
|
1657 |
|
|
buffer segment have been consumed or put.
|
1658 |
|
|
The routine may return
|
1659 |
|
|
.I NULL
|
1660 |
|
|
if it cannot return a buffer segment of the requested size.
|
1661 |
|
|
(The
|
1662 |
|
|
.I x_inline()
|
1663 |
|
|
routine is for cycle squeezers.
|
1664 |
|
|
Use of the resulting buffer is not data-portable.
|
1665 |
|
|
Users are encouraged not to use this feature.)
|
1666 |
|
|
.LP
|
1667 |
|
|
The operations
|
1668 |
|
|
.I x_getbytes()
|
1669 |
|
|
and
|
1670 |
|
|
.I x_putbytes()
|
1671 |
|
|
blindly get and put sequences of bytes
|
1672 |
|
|
from or to the underlying stream;
|
1673 |
|
|
they return
|
1674 |
|
|
.I TRUE
|
1675 |
|
|
if they are successful, and
|
1676 |
|
|
.I FALSE
|
1677 |
|
|
otherwise. The routines have identical parameters (replace
|
1678 |
|
|
.I xxx ):
|
1679 |
|
|
.DS
|
1680 |
|
|
.ft CW
|
1681 |
|
|
bool_t
|
1682 |
|
|
xxxbytes(xdrs, buf, bytecount)
|
1683 |
|
|
XDR *xdrs;
|
1684 |
|
|
char *buf;
|
1685 |
|
|
u_int bytecount;
|
1686 |
|
|
.DE
|
1687 |
|
|
The operations
|
1688 |
|
|
.I x_getlong()
|
1689 |
|
|
and
|
1690 |
|
|
.I x_putlong()
|
1691 |
|
|
receive and put
|
1692 |
|
|
long numbers from and to the data stream.
|
1693 |
|
|
It is the responsibility of these routines
|
1694 |
|
|
to translate the numbers between the machine representation
|
1695 |
|
|
and the (standard) external representation.
|
1696 |
|
|
The UNIX primitives
|
1697 |
|
|
.I htonl()
|
1698 |
|
|
and
|
1699 |
|
|
.I ntohl()
|
1700 |
|
|
can be helpful in accomplishing this.
|
1701 |
|
|
The higher-level XDR implementation assumes that
|
1702 |
|
|
signed and unsigned long integers contain the same number of bits,
|
1703 |
|
|
and that nonnegative integers
|
1704 |
|
|
have the same bit representations as unsigned integers.
|
1705 |
|
|
The routines return
|
1706 |
|
|
.I TRUE
|
1707 |
|
|
if they succeed, and
|
1708 |
|
|
.I FALSE
|
1709 |
|
|
otherwise. They have identical parameters:
|
1710 |
|
|
.DS
|
1711 |
|
|
.ft CW
|
1712 |
|
|
bool_t
|
1713 |
|
|
xxxlong(xdrs, lp)
|
1714 |
|
|
XDR *xdrs;
|
1715 |
|
|
long *lp;
|
1716 |
|
|
.DE
|
1717 |
|
|
Implementors of new XDR streams must make an XDR structure
|
1718 |
|
|
(with new operation routines) available to clients,
|
1719 |
|
|
using some kind of create routine.
|
1720 |
|
|
.NH 1
|
1721 |
|
|
\&Advanced Topics
|
1722 |
|
|
.IX XDR "advanced topics"
|
1723 |
|
|
.LP
|
1724 |
|
|
This section describes techniques for passing data structures that
|
1725 |
|
|
are not covered in the preceding sections. Such structures include
|
1726 |
|
|
linked lists (of arbitrary lengths). Unlike the simpler examples
|
1727 |
|
|
covered in the earlier sections, the following examples are written
|
1728 |
|
|
using both the XDR C library routines and the XDR data description
|
1729 |
|
|
language.
|
1730 |
|
|
The
|
1731 |
|
|
.I "External Data Representation Standard: Protocol Specification"
|
1732 |
|
|
describes this
|
1733 |
|
|
language in complete detail.
|
1734 |
|
|
.NH 2
|
1735 |
|
|
\&Linked Lists
|
1736 |
|
|
.IX XDR "linked lists"
|
1737 |
|
|
.LP
|
1738 |
|
|
The last example in the
|
1739 |
|
|
.I Pointers
|
1740 |
|
|
topic earlier in this chapter
|
1741 |
|
|
presented a C data structure and its associated XDR
|
1742 |
|
|
routines for a individual's gross assets and liabilities.
|
1743 |
|
|
The example is duplicated below:
|
1744 |
|
|
.ie t .DS
|
1745 |
|
|
.el .DS L
|
1746 |
|
|
.ft CW
|
1747 |
|
|
struct gnumbers {
|
1748 |
|
|
long g_assets;
|
1749 |
|
|
long g_liabilities;
|
1750 |
|
|
};
|
1751 |
|
|
.sp.5
|
1752 |
|
|
bool_t
|
1753 |
|
|
xdr_gnumbers(xdrs, gp)
|
1754 |
|
|
XDR *xdrs;
|
1755 |
|
|
struct gnumbers *gp;
|
1756 |
|
|
{
|
1757 |
|
|
if (xdr_long(xdrs, &(gp->g_assets)))
|
1758 |
|
|
return(xdr_long(xdrs, &(gp->g_liabilities)));
|
1759 |
|
|
return(FALSE);
|
1760 |
|
|
}
|
1761 |
|
|
.DE
|
1762 |
|
|
.LP
|
1763 |
|
|
Now assume that we wish to implement a linked list of such information.
|
1764 |
|
|
A data structure could be constructed as follows:
|
1765 |
|
|
.ie t .DS
|
1766 |
|
|
.el .DS L
|
1767 |
|
|
.ft CW
|
1768 |
|
|
struct gnumbers_node {
|
1769 |
|
|
struct gnumbers gn_numbers;
|
1770 |
|
|
struct gnumbers_node *gn_next;
|
1771 |
|
|
};
|
1772 |
|
|
.sp .5
|
1773 |
|
|
typedef struct gnumbers_node *gnumbers_list;
|
1774 |
|
|
.DE
|
1775 |
|
|
.LP
|
1776 |
|
|
The head of the linked list can be thought of as the data object;
|
1777 |
|
|
that is, the head is not merely a convenient shorthand for a
|
1778 |
|
|
structure. Similarly the
|
1779 |
|
|
.I gn_next
|
1780 |
|
|
field is used to indicate whether or not the object has terminated.
|
1781 |
|
|
Unfortunately, if the object continues, the
|
1782 |
|
|
.I gn_next
|
1783 |
|
|
field is also the address of where it continues. The link addresses
|
1784 |
|
|
carry no useful information when the object is serialized.
|
1785 |
|
|
.LP
|
1786 |
|
|
The XDR data description of this linked list is described by the
|
1787 |
|
|
recursive declaration of
|
1788 |
|
|
.I gnumbers_list :
|
1789 |
|
|
.ie t .DS
|
1790 |
|
|
.el .DS L
|
1791 |
|
|
.ft CW
|
1792 |
|
|
struct gnumbers {
|
1793 |
|
|
int g_assets;
|
1794 |
|
|
int g_liabilities;
|
1795 |
|
|
};
|
1796 |
|
|
.sp .5
|
1797 |
|
|
struct gnumbers_node {
|
1798 |
|
|
gnumbers gn_numbers;
|
1799 |
|
|
gnumbers_node *gn_next;
|
1800 |
|
|
};
|
1801 |
|
|
.DE
|
1802 |
|
|
.LP
|
1803 |
|
|
In this description, the boolean indicates whether there is more data
|
1804 |
|
|
following it. If the boolean is
|
1805 |
|
|
.I FALSE ,
|
1806 |
|
|
then it is the last data field of the structure. If it is
|
1807 |
|
|
.I TRUE ,
|
1808 |
|
|
then it is followed by a gnumbers structure and (recursively) by a
|
1809 |
|
|
.I gnumbers_list .
|
1810 |
|
|
Note that the C declaration has no boolean explicitly declared in it
|
1811 |
|
|
(though the
|
1812 |
|
|
.I gn_next
|
1813 |
|
|
field implicitly carries the information), while the XDR data
|
1814 |
|
|
description has no pointer explicitly declared in it.
|
1815 |
|
|
.LP
|
1816 |
|
|
Hints for writing the XDR routines for a
|
1817 |
|
|
.I gnumbers_list
|
1818 |
|
|
follow easily from the XDR description above. Note how the primitive
|
1819 |
|
|
.I xdr_pointer()
|
1820 |
|
|
is used to implement the XDR union above.
|
1821 |
|
|
.ie t .DS
|
1822 |
|
|
.el .DS L
|
1823 |
|
|
.ft CW
|
1824 |
|
|
bool_t
|
1825 |
|
|
xdr_gnumbers_node(xdrs, gn)
|
1826 |
|
|
XDR *xdrs;
|
1827 |
|
|
gnumbers_node *gn;
|
1828 |
|
|
{
|
1829 |
|
|
return(xdr_gnumbers(xdrs, &gn->gn_numbers) &&
|
1830 |
|
|
xdr_gnumbers_list(xdrs, &gp->gn_next));
|
1831 |
|
|
}
|
1832 |
|
|
.sp .5
|
1833 |
|
|
bool_t
|
1834 |
|
|
xdr_gnumbers_list(xdrs, gnp)
|
1835 |
|
|
XDR *xdrs;
|
1836 |
|
|
gnumbers_list *gnp;
|
1837 |
|
|
{
|
1838 |
|
|
return(xdr_pointer(xdrs, gnp,
|
1839 |
|
|
sizeof(struct gnumbers_node),
|
1840 |
|
|
xdr_gnumbers_node));
|
1841 |
|
|
}
|
1842 |
|
|
.DE
|
1843 |
|
|
.LP
|
1844 |
|
|
The unfortunate side effect of XDR'ing a list with these routines
|
1845 |
|
|
is that the C stack grows linearly with respect to the number of
|
1846 |
|
|
node in the list. This is due to the recursion. The following
|
1847 |
|
|
routine collapses the above two mutually recursive into a single,
|
1848 |
|
|
non-recursive one.
|
1849 |
|
|
.ie t .DS
|
1850 |
|
|
.el .DS L
|
1851 |
|
|
.ft CW
|
1852 |
|
|
bool_t
|
1853 |
|
|
xdr_gnumbers_list(xdrs, gnp)
|
1854 |
|
|
XDR *xdrs;
|
1855 |
|
|
gnumbers_list *gnp;
|
1856 |
|
|
{
|
1857 |
|
|
bool_t more_data;
|
1858 |
|
|
gnumbers_list *nextp;
|
1859 |
|
|
.sp .5
|
1860 |
|
|
for (;;) {
|
1861 |
|
|
more_data = (*gnp != NULL);
|
1862 |
|
|
if (!xdr_bool(xdrs, &more_data)) {
|
1863 |
|
|
return(FALSE);
|
1864 |
|
|
}
|
1865 |
|
|
if (! more_data) {
|
1866 |
|
|
break;
|
1867 |
|
|
}
|
1868 |
|
|
if (xdrs->x_op == XDR_FREE) {
|
1869 |
|
|
nextp = &(*gnp)->gn_next;
|
1870 |
|
|
}
|
1871 |
|
|
if (!xdr_reference(xdrs, gnp,
|
1872 |
|
|
sizeof(struct gnumbers_node), xdr_gnumbers)) {
|
1873 |
|
|
|
1874 |
|
|
return(FALSE);
|
1875 |
|
|
}
|
1876 |
|
|
gnp = (xdrs->x_op == XDR_FREE) ?
|
1877 |
|
|
nextp : &(*gnp)->gn_next;
|
1878 |
|
|
}
|
1879 |
|
|
*gnp = NULL;
|
1880 |
|
|
return(TRUE);
|
1881 |
|
|
}
|
1882 |
|
|
.DE
|
1883 |
|
|
.LP
|
1884 |
|
|
The first task is to find out whether there is more data or not,
|
1885 |
|
|
so that this boolean information can be serialized. Notice that
|
1886 |
|
|
this statement is unnecessary in the
|
1887 |
|
|
.I XDR_DECODE
|
1888 |
|
|
case, since the value of more_data is not known until we
|
1889 |
|
|
deserialize it in the next statement.
|
1890 |
|
|
.LP
|
1891 |
|
|
The next statement XDR's the more_data field of the XDR union.
|
1892 |
|
|
Then if there is truly no more data, we set this last pointer to
|
1893 |
|
|
.I NULL
|
1894 |
|
|
to indicate the end of the list, and return
|
1895 |
|
|
.I TRUE
|
1896 |
|
|
because we are done. Note that setting the pointer to
|
1897 |
|
|
.I NULL
|
1898 |
|
|
is only important in the
|
1899 |
|
|
.I XDR_DECODE
|
1900 |
|
|
case, since it is already
|
1901 |
|
|
.I NULL
|
1902 |
|
|
in the
|
1903 |
|
|
.I XDR_ENCODE
|
1904 |
|
|
and
|
1905 |
|
|
XDR_FREE
|
1906 |
|
|
cases.
|
1907 |
|
|
.LP
|
1908 |
|
|
Next, if the direction is
|
1909 |
|
|
.I XDR_FREE ,
|
1910 |
|
|
the value of
|
1911 |
|
|
.I nextp
|
1912 |
|
|
is set to indicate the location of the next pointer in the list.
|
1913 |
|
|
We do this now because we need to dereference gnp to find the
|
1914 |
|
|
location of the next item in the list, and after the next
|
1915 |
|
|
statement the storage pointed to by
|
1916 |
|
|
.I gnp
|
1917 |
|
|
will be freed up and no be longer valid. We can't do this for all
|
1918 |
|
|
directions though, because in the
|
1919 |
|
|
.I XDR_DECODE
|
1920 |
|
|
direction the value of
|
1921 |
|
|
.I gnp
|
1922 |
|
|
won't be set until the next statement.
|
1923 |
|
|
.LP
|
1924 |
|
|
Next, we XDR the data in the node using the primitive
|
1925 |
|
|
.I xdr_reference ().
|
1926 |
|
|
.I xdr_reference()
|
1927 |
|
|
is like
|
1928 |
|
|
.I xdr_pointer()
|
1929 |
|
|
which we used before, but it does not
|
1930 |
|
|
send over the boolean indicating whether there is more data.
|
1931 |
|
|
We use it instead of
|
1932 |
|
|
.I xdr_pointer()
|
1933 |
|
|
because we have already XDR'd this information ourselves. Notice
|
1934 |
|
|
that the xdr routine passed is not the same type as an element
|
1935 |
|
|
in the list. The routine passed is
|
1936 |
|
|
.I xdr_gnumbers (),
|
1937 |
|
|
for XDR'ing gnumbers, but each element in the list is actually of
|
1938 |
|
|
type
|
1939 |
|
|
.I gnumbers_node .
|
1940 |
|
|
We don't pass
|
1941 |
|
|
.I xdr_gnumbers_node()
|
1942 |
|
|
because it is recursive, and instead use
|
1943 |
|
|
.I xdr_gnumbers()
|
1944 |
|
|
which XDR's all of the non-recursive part. Note that this trick
|
1945 |
|
|
will work only if the
|
1946 |
|
|
.I gn_numbers
|
1947 |
|
|
field is the first item in each element, so that their addresses
|
1948 |
|
|
are identical when passed to
|
1949 |
|
|
.I xdr_reference ().
|
1950 |
|
|
.LP
|
1951 |
|
|
Finally, we update
|
1952 |
|
|
.I gnp
|
1953 |
|
|
to point to the next item in the list. If the direction is
|
1954 |
|
|
.I XDR_FREE ,
|
1955 |
|
|
we set it to the previously saved value, otherwise we can
|
1956 |
|
|
dereference
|
1957 |
|
|
.I gnp
|
1958 |
|
|
to get the proper value. Though harder to understand than the
|
1959 |
|
|
recursive version, this non-recursive routine is far less likely
|
1960 |
|
|
to blow the C stack. It will also run more efficiently since
|
1961 |
|
|
a lot of procedure call overhead has been removed. Most lists
|
1962 |
|
|
are small though (in the hundreds of items or less) and the
|
1963 |
|
|
recursive version should be sufficient for them.
|
1964 |
|
|
.EQ
|
1965 |
|
|
delim off
|
1966 |
|
|
.EN
|