1 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
---|
2 | <HTML>
|
---|
3 | <HEAD>
|
---|
4 | <META NAME="GENERATOR" CONTENT="SGML-Tools 1.0.9">
|
---|
5 | <TITLE>YAZ User's Guide and Reference: The ODR Module</TITLE>
|
---|
6 | <LINK HREF="yaz-6.html" REL=next>
|
---|
7 | <LINK HREF="yaz-4.html" REL=previous>
|
---|
8 | <LINK HREF="yaz.html#toc5" REL=contents>
|
---|
9 | </HEAD>
|
---|
10 | <BODY>
|
---|
11 | <A HREF="yaz-6.html">Next</A>
|
---|
12 | <A HREF="yaz-4.html">Previous</A>
|
---|
13 | <A HREF="yaz.html#toc5">Contents</A>
|
---|
14 | <HR>
|
---|
15 | <H2><A NAME="odr"></A> <A NAME="s5">5. The ODR Module</A></H2>
|
---|
16 |
|
---|
17 | <H2><A NAME="ss5.1">5.1 Introduction</A>
|
---|
18 | </H2>
|
---|
19 |
|
---|
20 | <P><B>ODR</B> is the BER-encoding/decoding subsystem of <B>YAZ</B>. Care as been taken
|
---|
21 | to isolate <B>ODR</B> from the rest of the package - specifically from the
|
---|
22 | transport interface. <B>ODR</B> may be used in any context where basic
|
---|
23 | ASN.1/BER representations are used.
|
---|
24 | <P>If you are only interested in writing a Z39.50 implementation based on
|
---|
25 | the PDUs that are already provided with <B>YAZ</B>, you only need to concern
|
---|
26 | yourself with the section on managing ODR streams (section
|
---|
27 | <A HREF="#odr-use">Using ODR</A>). Only if you need to
|
---|
28 | implement ASN.1 beyond that which has been provided, should you
|
---|
29 | worry about the second half of the documentation
|
---|
30 | (section
|
---|
31 | <A HREF="#odr-prog">Programming with ODR</A>). If you use
|
---|
32 | one of the higher-level interfaces, you can skip this section entirely.
|
---|
33 | <P>This is important, so we'll repeat it for emphasis: <I>You do not
|
---|
34 | need to read section
|
---|
35 | <A HREF="#odr-prog">Programming with ODR</A> to
|
---|
36 | implement Z39.50 with <B>YAZ</B>.</I>
|
---|
37 | <P>If you need a part of the protocol that isn't already in <B>YAZ</B>, you
|
---|
38 | should
|
---|
39 | contact the authors before going to work on it yourself: We
|
---|
40 | might already be working on it. Conversely, if you implement a useful
|
---|
41 | part of the protocol before us, we'd be happy to include it in a
|
---|
42 | future release.
|
---|
43 | <P>
|
---|
44 | <H2><A NAME="odr-use"></A> <A NAME="ss5.2">5.2 Using ODR</A>
|
---|
45 | </H2>
|
---|
46 |
|
---|
47 | <P>
|
---|
48 | <H3>ODR Streams</H3>
|
---|
49 |
|
---|
50 | <P>Conceptually, the ODR stream is the source of encoded data in the
|
---|
51 | decoding mode; when encoding, it is the receptacle for the encoded
|
---|
52 | data. Before you can use an ODR stream it must be allocated. This is
|
---|
53 | done with the function
|
---|
54 | <P>
|
---|
55 | <BLOCKQUOTE><CODE>
|
---|
56 | <PRE>
|
---|
57 | ODR odr_createmem(int direction);
|
---|
58 | </PRE>
|
---|
59 | </CODE></BLOCKQUOTE>
|
---|
60 | <P>The <CODE>odr_createmem()</CODE> function takes as argument one of three manifest
|
---|
61 | constants: <CODE>ODR_ENCODE</CODE>, <CODE>ODR_DECODE</CODE>, or <CODE>ODR_PRINT</CODE>. An
|
---|
62 | ODR stream can be
|
---|
63 | in only one mode - it is not possible to change its mode once it's
|
---|
64 | selected. Typically, your program will allocate at least two ODR
|
---|
65 | streams - one for decoding, and one for encoding.
|
---|
66 | <P>When you're done with the stream, you can use
|
---|
67 | <P>
|
---|
68 | <BLOCKQUOTE><CODE>
|
---|
69 | <PRE>
|
---|
70 | void odr_destroy(ODR o);
|
---|
71 | </PRE>
|
---|
72 | </CODE></BLOCKQUOTE>
|
---|
73 | <P>to release the resources allocated for the stream.
|
---|
74 | <P>
|
---|
75 | <H3><A NAME="memory"></A> Memory Management</H3>
|
---|
76 |
|
---|
77 | <P>Two forms of memory management take place in the ODR system. The first
|
---|
78 | one, which has to do with allocating little bits of memory (sometimes
|
---|
79 | quite large bits of memory, actually) when a protocol package is
|
---|
80 | decoded, and turned into a complex of interlinked structures. This
|
---|
81 | section deals with this system, and how you can use it for your own
|
---|
82 | purposes. The next section deals with the memory management which is
|
---|
83 | required when encoding data - to make sure that a large enough buffer is
|
---|
84 | available to hold the fully encoded PDU.
|
---|
85 | <P>The <B>ODR</B> module has its own memory management system, which is
|
---|
86 | used whenever memory is required. Specifically, it is used to allocate
|
---|
87 | space for data when decoding incoming PDUs. You can use the memory
|
---|
88 | system for your own purposes, by using the function
|
---|
89 | <P>
|
---|
90 | <BLOCKQUOTE><CODE>
|
---|
91 | <PRE>
|
---|
92 | void *odr_malloc(ODR o, int size);
|
---|
93 | </PRE>
|
---|
94 | </CODE></BLOCKQUOTE>
|
---|
95 | <P>You can't use the normal <CODE>free</CODE>(2) routine to free memory allocated
|
---|
96 | by this function, and <B>ODR</B> doesn't provide a parallel function. Instead,
|
---|
97 | you can call
|
---|
98 | <P>
|
---|
99 | <BLOCKQUOTE><CODE>
|
---|
100 | <PRE>
|
---|
101 | void odr_reset(ODR o, int size);
|
---|
102 | </PRE>
|
---|
103 | </CODE></BLOCKQUOTE>
|
---|
104 | <P>when you are done with the
|
---|
105 | memory: Everything allocated since the last call to <CODE>odr_reset()</CODE> is
|
---|
106 | released. The <CODE>odr_reset()</CODE> call is also required to clear up an
|
---|
107 | error condition on a stream.
|
---|
108 | <P>The function
|
---|
109 | <P>
|
---|
110 | <BLOCKQUOTE><CODE>
|
---|
111 | <PRE>
|
---|
112 | int odr_total(ODR o);
|
---|
113 | </PRE>
|
---|
114 | </CODE></BLOCKQUOTE>
|
---|
115 | <P>returns the number of bytes allocated on the stream since the last call to
|
---|
116 | <CODE>odr_reset()</CODE>.
|
---|
117 | <P>The memory subsystem of <B>ODR</B> is fairly efficient at allocating and
|
---|
118 | releasing little bits of memory. Rather than managing the individual,
|
---|
119 | small bits of space, the system maintains a freelist of larger chunks
|
---|
120 | of memory, which are handed out in small bits. This scheme is
|
---|
121 | generally known as a <I>nibble memory</I> system. It is very useful for
|
---|
122 | maintaing short-lived constructions such as protocol PDUs.
|
---|
123 | <P>If you want to retain a bit of memory beyond the next call to
|
---|
124 | <CODE>odr_reset()</CODE>, you can use the function
|
---|
125 | <P>
|
---|
126 | <BLOCKQUOTE><CODE>
|
---|
127 | <PRE>
|
---|
128 | ODR_MEM odr_extract_mem(ODR o);
|
---|
129 | </PRE>
|
---|
130 | </CODE></BLOCKQUOTE>
|
---|
131 | <P>This function will give you control of the memory recently allocated
|
---|
132 | on the ODR stream. The memory will live (past calls to <CODE>odr_reset()</CODE>),
|
---|
133 | until you call the function
|
---|
134 | <P>
|
---|
135 | <BLOCKQUOTE><CODE>
|
---|
136 | <PRE>
|
---|
137 | void odr_release_mem(ODR_MEM p);
|
---|
138 | </PRE>
|
---|
139 | </CODE></BLOCKQUOTE>
|
---|
140 | <P>The opaque <CODE>ODR_MEM</CODE> handle has no other purpose than referencing
|
---|
141 | the memory block for you until you want to release it.
|
---|
142 | <P>You can use <CODE>odr_extract_mem()</CODE> repeatedly between allocating data,
|
---|
143 | to retain individual control of separate chunks of data.
|
---|
144 | <P>
|
---|
145 | <H3>Encoding and Decoding Data</H3>
|
---|
146 |
|
---|
147 | <P>When encoding data, the ODR stream will write the encoded octet string
|
---|
148 | in an internal buffer. To retrieve the data, use the function
|
---|
149 | <P>
|
---|
150 | <BLOCKQUOTE><CODE>
|
---|
151 | <PRE>
|
---|
152 | char *odr_getbuf(ODR o, int *len, int *size);
|
---|
153 | </PRE>
|
---|
154 | </CODE></BLOCKQUOTE>
|
---|
155 | <P>The integer pointed to by len is set to the length of the encoded
|
---|
156 | data, and a pointer to that data is returned. *<CODE>size</CODE> is set to the size
|
---|
157 | of the buffer (unless <CODE>size</CODE> is null, signalling that you are
|
---|
158 | not interested in the size). The next call to a primitive function
|
---|
159 | using the same ODR stream will overwrite the data, unless a different
|
---|
160 | buffer has been supplied using the call
|
---|
161 | <P>
|
---|
162 | <BLOCKQUOTE><CODE>
|
---|
163 | <PRE>
|
---|
164 | void odr_setbuf(ODR o, char *buf, int len, int can_grow);
|
---|
165 | </PRE>
|
---|
166 | </CODE></BLOCKQUOTE>
|
---|
167 | <P>which sets the encoding (or decoding) buffer used by <CODE>o</CODE> to
|
---|
168 | <CODE>buf</CODE>,
|
---|
169 | using the length <CODE>len</CODE>. Before a call to an encoding function,
|
---|
170 | you can use <CODE>odr_setbuf()</CODE> to provide the stream with an encoding buffer
|
---|
171 | of sufficient size (length). The <CODE>can_grow</CODE> parameter tells the encoding
|
---|
172 | ODR stream whether it is allowed to use <CODE>realloc</CODE>(2) to increase the size
|
---|
173 | of the buffer when necessary. The default condition of a new encoding
|
---|
174 | stream is equivalent to the results of calling
|
---|
175 | <P>
|
---|
176 | <BLOCKQUOTE><CODE>
|
---|
177 | <PRE>
|
---|
178 | odr_setbuf(stream, 0, 0, 1);
|
---|
179 | </PRE>
|
---|
180 | </CODE></BLOCKQUOTE>
|
---|
181 | <P>In this case, the stream will allocate and reallocate memory as
|
---|
182 | necessary. The stream reallocates memory by repeatedly doubling the
|
---|
183 | size of the buffer - the result is that the buffer will typically
|
---|
184 | reach its maximum, working size with only a small number of reallocation
|
---|
185 | operations. The memory is freed by the stream when the latter is destroyed,
|
---|
186 | unless it was assigned by the user with the <CODE>can_grow</CODE>
|
---|
187 | parameter set to zero (in this case, you are expected to retain
|
---|
188 | control of the memory yourself).
|
---|
189 | <P>To assume full control of an encoded buffer, you must first call
|
---|
190 | <CODE>odr_getbuf()</CODE> to fetch the buffer and its length. Next, you should
|
---|
191 | call <CODE>odr_setbuf()</CODE> to provide a different buffer (or a null
|
---|
192 | pointer) to the stream. In the simplest case, you will reuse the same
|
---|
193 | buffer over and over again, and you will just need to call
|
---|
194 | <CODE>odr_getbuf()</CODE> after each encoding operation to get the length and
|
---|
195 | address of the buffer. Note that the stream may reallocate the buffer
|
---|
196 | during an encoding operation, so it is necessary to retrieve the
|
---|
197 | correct address after each encoding operation.
|
---|
198 | <P>It is important to realise that the ODR stream will not release this
|
---|
199 | memory when you call <CODE>odr_reset()</CODE>: It will merely update its
|
---|
200 | internal pointers to prepare for the encoding of a new data value.
|
---|
201 | When the stream is released by the <CODE>odr_destroy()</CODE> function, the
|
---|
202 | memory given to it by odr_setbuf will be released <I>only</I> if the
|
---|
203 | <CODE>can_grow</CODE> parameter to <CODE>odr_setbuf()</CODE> was nonzero. The
|
---|
204 | <CODE>can_grow</CODE> parameter, in other words, is a way of signalling who
|
---|
205 | is to own the buffer, you or the ODR stream. If you never call
|
---|
206 | <CODE>odr_setbuf()</CODE> on your encoding stream, which is typically the
|
---|
207 | case, the buffer allocated by the stream will belong to the stream by
|
---|
208 | default.
|
---|
209 | <P>When you wish to decode data, you should first call <CODE>odr_setbuf()</CODE>, to
|
---|
210 | tell the decoding stream where to find the encoded data, and how long
|
---|
211 | the buffer is (the <CODE>can_grow</CODE> parameter is ignored by a decoding
|
---|
212 | stream). After this, you can call the function corresponding to the
|
---|
213 | data you wish to decode (eg, <CODE>odr_integer()</CODE> odr <CODE>z_APDU()</CODE>).
|
---|
214 | <P>Examples of encoding/decoding functions:
|
---|
215 | <P>
|
---|
216 | <BLOCKQUOTE><CODE>
|
---|
217 | <PRE>
|
---|
218 | int odr_integer(ODR o, int **p, int optional, const char *name);
|
---|
219 |
|
---|
220 | int z_APDU(ODR o, Z_APDU **p, int optional, const char *name);
|
---|
221 | </PRE>
|
---|
222 | </CODE></BLOCKQUOTE>
|
---|
223 | <P>If the data is absent (or
|
---|
224 | doesn't match the tag corresponding to the type), the return value
|
---|
225 | will be either 0 or 1 depending on the <CODE>optional</CODE> flag. If
|
---|
226 | <CODE>optional</CODE>
|
---|
227 | is 0 and the data is absent, an error flag will be raised in the
|
---|
228 | stream, and you'll need to call <CODE>odr_reset()</CODE> before you can use the
|
---|
229 | stream again. If <CODE>optional</CODE> is nonzero, the pointer <I>pointed to</I> by
|
---|
230 | <CODE>p</CODE> will be set to the null value, and the function will return 1.
|
---|
231 | The <CODE>name</CODE> argument is used to pretty-print the tag in question.
|
---|
232 | It may be set to <CODE>NULL</CODE> if pretty-printing is not desired.
|
---|
233 | <P>If the data value is found where it's expected, the pointer
|
---|
234 | <I>pointed to</I> by the <CODE>p</CODE> argument will be set to point to the
|
---|
235 | decoded type. The
|
---|
236 | space for the type will be allocated and owned by the ODR stream, and
|
---|
237 | it will live until you call <CODE>odr_reset()</CODE> on the stream. You cannot use
|
---|
238 | <CODE>free</CODE>(2) to release the memory. You can decode several data elements
|
---|
239 | (by repeated calls to <CODE>odr_setbuf()</CODE> and your decoding function), and
|
---|
240 | new memory will be allocated each time. When you do call
|
---|
241 | <CODE>odr_reset()</CODE>,
|
---|
242 | everything decoded since the last call to <CODE>odr_reset()</CODE> will be
|
---|
243 | released.
|
---|
244 | <P>The use of the double indirection can be a little confusing at first
|
---|
245 | (its purpose will become clear later on, hopefully),
|
---|
246 | so an example is in order. We'll encode an integer value, and
|
---|
247 | immediately decode it again using a different stream. A useless, but
|
---|
248 | informative operation.
|
---|
249 | <P>
|
---|
250 | <BLOCKQUOTE><CODE>
|
---|
251 | <PRE>
|
---|
252 | void do_nothing_useful(int value)
|
---|
253 | {
|
---|
254 | ODR encode, decode;
|
---|
255 | int *valp, *resvalp;
|
---|
256 | char *bufferp;
|
---|
257 | int len;
|
---|
258 |
|
---|
259 | /* allocate streams */
|
---|
260 | if (!(encode = odr_createmem(ODR_ENCODE)))
|
---|
261 | return;
|
---|
262 | if (!(decode = odr_createmem(ODR_DECODE)))
|
---|
263 | return;
|
---|
264 |
|
---|
265 | valp = &value;
|
---|
266 | if (odr_integer(encode, &valp, 0, 0) == 0)
|
---|
267 | {
|
---|
268 | printf("encoding went bad\n");
|
---|
269 | return;
|
---|
270 | }
|
---|
271 | bufferp = odr_getbuf(encode, &len);
|
---|
272 | printf("length of encoded data is %d\n", len);
|
---|
273 |
|
---|
274 | /* now let's decode the thing again */
|
---|
275 | odr_setbuf(decode, bufferp, len);
|
---|
276 | if (odr_integer(decode, &resvalp, 0, 0) == 0)
|
---|
277 | {
|
---|
278 | printf("decoding went bad\n");
|
---|
279 | return;
|
---|
280 | }
|
---|
281 | printf("the value is %d\n", *resvalp);
|
---|
282 |
|
---|
283 | /* clean up */
|
---|
284 | odr_destroy(encode);
|
---|
285 | odr_destroy(decode);
|
---|
286 | }
|
---|
287 | </PRE>
|
---|
288 | </CODE></BLOCKQUOTE>
|
---|
289 | <P>This looks like a lot of work, offhand. In practice, the ODR streams
|
---|
290 | will typically be allocated once, in the beginning of your program (or at the
|
---|
291 | beginning of a new network session), and the encoding and decoding
|
---|
292 | will only take place in a few, isolated places in your program, so the
|
---|
293 | overhead is quite manageable.
|
---|
294 | <P>
|
---|
295 | <H3>Diagnostics</H3>
|
---|
296 |
|
---|
297 | <P>The encoding/decoding functions all return 0 when an error occurs.
|
---|
298 | Until you call <CODE>odr_reset()</CODE>, you cannot use the stream again, and
|
---|
299 | any function called will immediately return 0.
|
---|
300 | <P>To provide information to the programmer or administrator, the
|
---|
301 | function
|
---|
302 | <P>
|
---|
303 | <BLOCKQUOTE><CODE>
|
---|
304 | <PRE>
|
---|
305 | void odr_perror(ODR o, char *message);
|
---|
306 | </PRE>
|
---|
307 | </CODE></BLOCKQUOTE>
|
---|
308 | <P>is provided, which prints the <CODE>message</CODE> argument to <CODE>stderr</CODE>
|
---|
309 | along with an error message from the stream.
|
---|
310 | <P>You can also use the function
|
---|
311 | <P>
|
---|
312 | <BLOCKQUOTE><CODE>
|
---|
313 | <PRE>
|
---|
314 | int odr_geterror(ODR o);
|
---|
315 | </PRE>
|
---|
316 | </CODE></BLOCKQUOTE>
|
---|
317 | <P>to get the current error number from the screen. The number will be
|
---|
318 | one of these constants:
|
---|
319 | <P>
|
---|
320 | <DL>
|
---|
321 | <DT><B>OMEMORY</B><DD><P>Memory allocation failed.
|
---|
322 | <DT><B>OSYSERR</B><DD><P>A system- or library call has failed. The standard
|
---|
323 | diagnostic variable <CODE>errno</CODE>
|
---|
324 | should be examined to determine the actual error.
|
---|
325 | <DT><B>OSPACE</B><DD><P>No more space for encoding. This will only occur when the user has
|
---|
326 | explicitly provided a buffer for an encoding stream without allowing
|
---|
327 | the system to allocate more space.
|
---|
328 | <DT><B>OREQUIRED</B><DD><P>This is a common protocol error; A required data element was
|
---|
329 | missing during
|
---|
330 | encoding or decoding.
|
---|
331 | <DT><B>OUNEXPECTED</B><DD><P>An unexpected data element was found during decoding.
|
---|
332 | <DT><B>OOTHER</B><DD><P>Other error. This is typically an indication of misuse of
|
---|
333 | the <B>ODR</B> system by the programmer, and also that the diagnostic
|
---|
334 | system isn't as good as it should be, yet.
|
---|
335 | </DL>
|
---|
336 | <P>The character string array
|
---|
337 | <P>
|
---|
338 | <BLOCKQUOTE><CODE>
|
---|
339 | <PRE>
|
---|
340 | char *odr_errlist[]
|
---|
341 | </PRE>
|
---|
342 | </CODE></BLOCKQUOTE>
|
---|
343 | <P>can be indexed by the error code to obtain a human-readable
|
---|
344 | representation of the problem.
|
---|
345 | <P>
|
---|
346 | <H3>Summary and Synopsis</H3>
|
---|
347 |
|
---|
348 | <P>
|
---|
349 | <BLOCKQUOTE><CODE>
|
---|
350 | <PRE>
|
---|
351 | #include <odr.h>
|
---|
352 |
|
---|
353 | ODR odr_createmem(int direction);
|
---|
354 |
|
---|
355 | void odr_destroy(ODR o);
|
---|
356 |
|
---|
357 | void odr_reset(ODR o);
|
---|
358 |
|
---|
359 | char *odr_getbuf(ODR o, int *len);
|
---|
360 |
|
---|
361 | void odr_setbuf(ODR o, char *buf, int len);
|
---|
362 |
|
---|
363 | void *odr_malloc(ODR o, int size);
|
---|
364 |
|
---|
365 | ODR_MEM odr_extract_mem(ODR o);
|
---|
366 |
|
---|
367 | void odr_release_mem(ODR_MEM r);
|
---|
368 |
|
---|
369 | int odr_geterror(ODR o);
|
---|
370 |
|
---|
371 | void odr_perror(char *message);
|
---|
372 |
|
---|
373 | extern char *odr_errlist[];
|
---|
374 | </PRE>
|
---|
375 | </CODE></BLOCKQUOTE>
|
---|
376 | <P>
|
---|
377 | <H2><A NAME="odr-prog"></A> <A NAME="ss5.3">5.3 Programming with ODR</A>
|
---|
378 | </H2>
|
---|
379 |
|
---|
380 | <P>The API of <B>ODR</B> is designed to reflect the structure of ASN.1, rather
|
---|
381 | than BER itself. Future releases may be able to represent data in
|
---|
382 | other external forms.
|
---|
383 | <P>The interface is based loosely on that of the Sun Microsystems XDR routines.
|
---|
384 | Specifically, each function which corresponds to an ASN.1 primitive
|
---|
385 | type has a dual function. Depending on the settings of the ODR
|
---|
386 | stream which is supplied as a parameter, the function may be used
|
---|
387 | either to encode or decode data. The functions that can be built
|
---|
388 | using these primitive functions, to represent more complex datatypes, share
|
---|
389 | this quality. The result is that you only have to enter the definition
|
---|
390 | for a type once - and you have the functionality of encoding, decoding
|
---|
391 | (and pretty-printing) all in one unit. The resulting C source code is
|
---|
392 | quite compact, and is a pretty straightforward representation of the
|
---|
393 | source ASN.1 specification. Although no ASN.1 compiler is supplied
|
---|
394 | with <B>ODR</B> at this time, it shouldn't be too difficult to write one, or
|
---|
395 | perhaps even to adapt an existing compiler to output <B>ODR</B> routines
|
---|
396 | (not surprisingly, writing encoders/decoders using <B>ODR</B> turns out
|
---|
397 | to be boring work).
|
---|
398 | <P>In many cases, the model of the XDR functions works quite well in this
|
---|
399 | role. In
|
---|
400 | others, it is less elegant. Most of the hassle comes from the optional
|
---|
401 | SEQUENCE memebers which don't exist in XDR.
|
---|
402 | <P>
|
---|
403 | <H3>The Primitive ASN.1 Types</H3>
|
---|
404 |
|
---|
405 | <P>ASN.1 defines a number of primitive types (many of which correspond
|
---|
406 | roughly to
|
---|
407 | primitive types in structured programming languages, such as C).
|
---|
408 | <P>
|
---|
409 | <H3>INTEGER</H3>
|
---|
410 |
|
---|
411 | <P>The <B>ODR</B> function for encoding or decoding (or printing) the ASN.1
|
---|
412 | INTEGER type looks like this:
|
---|
413 | <P>
|
---|
414 | <BLOCKQUOTE><CODE>
|
---|
415 | <PRE>
|
---|
416 | int odr_integer(ODR o, int **p, int optional, const char *name);
|
---|
417 | </PRE>
|
---|
418 | </CODE></BLOCKQUOTE>
|
---|
419 | <P>(we don't allow values that can't be contained in a C integer.)
|
---|
420 | <P>This form is typical of the primitive <B>ODR</B> functions. They are named
|
---|
421 | after the type of data that they encode or decode. They take an
|
---|
422 | ODR
|
---|
423 | stream, an indirect reference to the type in question, and an
|
---|
424 | <CODE>optional</CODE> flag (corresponding to the OPTIONAL keyword of ASN.1) as
|
---|
425 | parameters. They all return an integer value of either one or zero.
|
---|
426 | When you use the primitive functions to construct encoders for complex
|
---|
427 | types of your own, you should follow this model as well. This
|
---|
428 | ensures that your new types can be reused as elements in yet more
|
---|
429 | complex types.
|
---|
430 | <P>The <CODE>o</CODE> parameter should obviously refer to a properly
|
---|
431 | initialized ODR
|
---|
432 | stream of the right type (encoding/decoding/printing) for the
|
---|
433 | operation that you wish to perform.
|
---|
434 | <P>When encoding or printing, the function first looks at *<CODE>p</CODE>. If
|
---|
435 | *<CODE>p</CODE> (the
|
---|
436 | pointer pointed to by <CODE>p</CODE>) is a null pointer, this is taken to mean that
|
---|
437 | the data element is absent. If the <CODE>optional</CODE> parameter is nonzero,
|
---|
438 | the function will return one (signifying success) without any further
|
---|
439 | processing. If the <CODE>optional</CODE> is zero, an internal error flag is
|
---|
440 | set in the ODR stream, and the function will return 0. No further
|
---|
441 | operations can be carried out on the stream without a call to
|
---|
442 | the function <CODE>odr_reset()</CODE>.
|
---|
443 | <P>If *<CODE>p</CODE> is not a null pointer, it is expected to point to an instance of
|
---|
444 | the data type. The data will be subjected to the encoding rules, and
|
---|
445 | the result will be placed in the buffer held by the ODR stream.
|
---|
446 | <P>The other ASN.1 primitives have similar functions that operate in
|
---|
447 | similar manners:
|
---|
448 | <P>
|
---|
449 | <H3>BOOLEAN</H3>
|
---|
450 |
|
---|
451 | <P>
|
---|
452 | <BLOCKQUOTE><CODE>
|
---|
453 | <PRE>
|
---|
454 | int odr_bool(ODR o, bool_t **p, int optional, const char *name);
|
---|
455 | </PRE>
|
---|
456 | </CODE></BLOCKQUOTE>
|
---|
457 | <P>
|
---|
458 | <H3>REAL</H3>
|
---|
459 |
|
---|
460 | <P>Not defined.
|
---|
461 | <P>
|
---|
462 | <H3>NULL</H3>
|
---|
463 |
|
---|
464 | <P>
|
---|
465 | <BLOCKQUOTE><CODE>
|
---|
466 | <PRE>
|
---|
467 | int odr_null(ODR o, bool_t **p, int optional, const char *name);
|
---|
468 | </PRE>
|
---|
469 | </CODE></BLOCKQUOTE>
|
---|
470 | <P>In this case, the value of **p is not important. If *p is different
|
---|
471 | from the null pointer, the null value is present, otherwise it's
|
---|
472 | absent.
|
---|
473 | <P>
|
---|
474 | <H3>OCTET STRING</H3>
|
---|
475 |
|
---|
476 | <P>
|
---|
477 | <BLOCKQUOTE><CODE>
|
---|
478 | <PRE>
|
---|
479 | typedef struct odr_oct
|
---|
480 | {
|
---|
481 | unsigned char *buf;
|
---|
482 | int len;
|
---|
483 | int size;
|
---|
484 | } Odr_oct;
|
---|
485 |
|
---|
486 | int odr_octetstring(ODR o, Odr_oct **p, int optional, const char *name);
|
---|
487 | </PRE>
|
---|
488 | </CODE></BLOCKQUOTE>
|
---|
489 | <P>The <CODE>buf</CODE> field should point to the character array that holds the
|
---|
490 | octetstring. The <CODE>len</CODE> field holds the actual length, while the
|
---|
491 | <CODE>size</CODE>
|
---|
492 | field gives the size of the allocated array (not of interest to you,
|
---|
493 | in most cases). The character array need not be null terminated.
|
---|
494 | <P>To make things a little easier, an alternative is given for string
|
---|
495 | types that are not expected to contain embedded NULL characters (eg.
|
---|
496 | VisibleString):
|
---|
497 | <P>
|
---|
498 | <BLOCKQUOTE><CODE>
|
---|
499 | <PRE>
|
---|
500 | int odr_cstring(ODR o, char **p, int optional, const char *name);
|
---|
501 | </PRE>
|
---|
502 | </CODE></BLOCKQUOTE>
|
---|
503 | <P>Which encoded or decodes between OCTETSTRING representations and
|
---|
504 | null-terminates C strings.
|
---|
505 | <P>Functions are provided for the derived string types, eg:
|
---|
506 | <P>
|
---|
507 | <BLOCKQUOTE><CODE>
|
---|
508 | <PRE>
|
---|
509 | int odr_visiblestring(ODR o, char **p, int optional, const char *name);
|
---|
510 | </PRE>
|
---|
511 | </CODE></BLOCKQUOTE>
|
---|
512 | <P>
|
---|
513 | <H3>BIT STRING</H3>
|
---|
514 |
|
---|
515 | <P>
|
---|
516 | <BLOCKQUOTE><CODE>
|
---|
517 | <PRE>
|
---|
518 | int odr_bitstring(ODR o, Odr_bitmask **p, int optional, const char *name);
|
---|
519 | </PRE>
|
---|
520 | </CODE></BLOCKQUOTE>
|
---|
521 | <P>The opaque type <CODE>Odr_bitmask</CODE> is only suitable for holding relatively brief bit
|
---|
522 | strings, eg. for options fields, etc. The constant
|
---|
523 | <CODE>ODR_BITMASK_SIZE</CODE>
|
---|
524 | multiplied by 8 gives the maximum possible number of bits.
|
---|
525 | <P>A set of macros are provided for manipulating the
|
---|
526 | <CODE>Odr_bitmask</CODE>
|
---|
527 | type:
|
---|
528 | <P>
|
---|
529 | <BLOCKQUOTE><CODE>
|
---|
530 | <PRE>
|
---|
531 | void ODR_MASK_ZERO(Odr_bitmask *b);
|
---|
532 |
|
---|
533 | void ODR_MASK_SET(Odr_bitmask *b, int bitno);
|
---|
534 |
|
---|
535 | void ODR_MASK_CLEAR(Odr_bitmask *b, int bitno);
|
---|
536 |
|
---|
537 | int ODR_MASK_GET(Odr_bitmask *b, int bitno);
|
---|
538 | </PRE>
|
---|
539 | </CODE></BLOCKQUOTE>
|
---|
540 | <P>The functions are modelled after the manipulation functions that
|
---|
541 | accompany the <CODE>fd_set</CODE> type used by the <CODE>select</CODE>(2) call.
|
---|
542 | <CODE>ODR_MASK_ZERO</CODE> should always be called first on a new bitmask, to
|
---|
543 | initialize the bits to zero.
|
---|
544 | <P>
|
---|
545 | <H3>OBJECT IDENTIFIER</H3>
|
---|
546 |
|
---|
547 | <P>
|
---|
548 | <BLOCKQUOTE><CODE>
|
---|
549 | <PRE>
|
---|
550 | int odr_oid(ODR o, Odr_oid **p, int optional, const char *name);
|
---|
551 | </PRE>
|
---|
552 | </CODE></BLOCKQUOTE>
|
---|
553 | <P>The C OID represenation is simply an array of integers, terminated by
|
---|
554 | the value -1 (the <CODE>Odr_oid</CODE> type is synonymous with the <CODE>int</CODE>
|
---|
555 | type). We suggest that you use the OID database module (see section
|
---|
556 | <A HREF="yaz-3.html#oid">Object Identifiers</A>) to handle object identifiers
|
---|
557 | in your application.
|
---|
558 | <P>
|
---|
559 | <H3><A NAME="tag-prim"></A> Tagging Primitive Types</H3>
|
---|
560 |
|
---|
561 | <P>The simplest way of tagging a type is to use the <CODE>odr_implicit_tag()</CODE> or
|
---|
562 | <CODE>odr_explicit_tag()</CODE> macros:
|
---|
563 | <P>
|
---|
564 | <BLOCKQUOTE><CODE>
|
---|
565 | <PRE>
|
---|
566 | int odr_implicit_tag(ODR o, Odr_fun fun, int class, int tag, int
|
---|
567 | optional, const char *name);
|
---|
568 |
|
---|
569 | int odr_explicit_tag(ODR o, Odr_fun fun, int class, int tag,
|
---|
570 | int optional, const char *name);
|
---|
571 | </PRE>
|
---|
572 | </CODE></BLOCKQUOTE>
|
---|
573 | <P>To create a type derived from the integer type by implicit tagging, you
|
---|
574 | might write:
|
---|
575 | <P>
|
---|
576 | <BLOCKQUOTE><CODE>
|
---|
577 | <PRE>
|
---|
578 | MyInt ::= [210] IMPLICIT INTEGER
|
---|
579 | </PRE>
|
---|
580 | </CODE></BLOCKQUOTE>
|
---|
581 | <P>In the <B>ODR</B> system, this would be written like:
|
---|
582 | <P>
|
---|
583 | <BLOCKQUOTE><CODE>
|
---|
584 | <PRE>
|
---|
585 | int myInt(ODR o, int **p, int optional, const char *name)
|
---|
586 | {
|
---|
587 | return odr_implicit_tag(o, odr_integer, p,
|
---|
588 | ODR_CONTEXT, 210, optional, name);
|
---|
589 | }
|
---|
590 | </PRE>
|
---|
591 | </CODE></BLOCKQUOTE>
|
---|
592 | <P>The function <CODE>myInt()</CODE> can then be used like any of the primitive
|
---|
593 | functions provided by ODR. Note that the behavior of
|
---|
594 | <CODE>odr_explicit()</CODE>
|
---|
595 | and <CODE>odr_implicit()</CODE> macros act exactly the same as the functions they
|
---|
596 | are applied to - they respond to error conditions, etc, in the same
|
---|
597 | manner - they simply have three extra parameters. The class parameter may
|
---|
598 | take one of the values: <CODE>ODR_CONTEXT</CODE>, <CODE>ODR_PRIVATE</CODE>,
|
---|
599 | <CODE>ODR_UNIVERSAL</CODE>, or
|
---|
600 | <CODE>ODR_APPLICATION</CODE>.
|
---|
601 | <P>
|
---|
602 | <H3>Constructed Types</H3>
|
---|
603 |
|
---|
604 | <P>Constructed types are created by combining primitive types. The
|
---|
605 | <B>ODR</B>
|
---|
606 | system only implements the SEQUENCE and SEQUENCE OF constructions
|
---|
607 | (although adding the rest of the container types should be simple
|
---|
608 | enough, if the need arises).
|
---|
609 | <P>For implementing SEQUENCEs, the functions
|
---|
610 | <P>
|
---|
611 | <BLOCKQUOTE><CODE>
|
---|
612 | <PRE>
|
---|
613 | int odr_sequence_begin(ODR o, void *p, int size, const char *name);
|
---|
614 | int odr_sequence_end(ODR o);
|
---|
615 | </PRE>
|
---|
616 | </CODE></BLOCKQUOTE>
|
---|
617 | <P>are provided.
|
---|
618 | <P>The <CODE>odr_sequence_begin()</CODE> function should be called in the beginning of a
|
---|
619 | function that implements a SEQUENCE type. Its parameters are the
|
---|
620 | <B>ODR</B>
|
---|
621 | stream, a pointer (to a pointer to the type you're implementing), and
|
---|
622 | the <CODE>size</CODE> of the type (typically a C structure). On encoding, it
|
---|
623 | returns 1 if *<CODE>p</CODE> is a null pointer. The <CODE>size</CODE> parameter is ignored. On
|
---|
624 | decoding, it returns 1 if the type is found in the data stream.
|
---|
625 | <CODE>size</CODE>
|
---|
626 | bytes of memory are allocated, and *<CODE>p</CODE> is set to point to this space.
|
---|
627 | <CODE>odr_sequence_end()</CODE> is called at the end of the complex function. Assume
|
---|
628 | that a type is defined like this:
|
---|
629 | <P>
|
---|
630 | <BLOCKQUOTE><CODE>
|
---|
631 | <PRE>
|
---|
632 | MySequence ::= SEQUENCE {
|
---|
633 | intval INTEGER,
|
---|
634 | boolval BOOLEAN OPTIONAL }
|
---|
635 | </PRE>
|
---|
636 | </CODE></BLOCKQUOTE>
|
---|
637 | <P>The corresponding ODR encoder/decoder function and the associated data
|
---|
638 | structures could be written like this:
|
---|
639 | <P>
|
---|
640 | <BLOCKQUOTE><CODE>
|
---|
641 | <PRE>
|
---|
642 | typedef struct MySequence
|
---|
643 | {
|
---|
644 | int *intval;
|
---|
645 | bool_t *boolval;
|
---|
646 | } MySequence;
|
---|
647 |
|
---|
648 | int mySequence(ODR o, MySequence **p, int optional, const char *name)
|
---|
649 | {
|
---|
650 | if (odr_sequence_begin(o, p, sizeof(**p), name) == 0)
|
---|
651 | return optional && odr_ok(o);
|
---|
652 | return
|
---|
653 | odr_integer(o, &(*p)->intval, 0, "intval") &&
|
---|
654 | odr_bool(o, &(*p)->boolval, 1, "boolval") &&
|
---|
655 | odr_sequence_end(o);
|
---|
656 | }
|
---|
657 | </PRE>
|
---|
658 | </CODE></BLOCKQUOTE>
|
---|
659 | <P>Note the 1 in the call to <CODE>odr_bool()</CODE>, to mark that the sequence
|
---|
660 | member is optional. If either of the member types had been tagged, the
|
---|
661 | macros <CODE>odr_implicit()</CODE> or <CODE>odr_explicit()</CODE> could have been used.
|
---|
662 | The new
|
---|
663 | function can be used exactly like the standard functions provided with
|
---|
664 | <B>ODR</B>. It will encode, decode or pretty-print a data value of the
|
---|
665 | <CODE>MySequence</CODE> type. We like to name types with an initial capital, as
|
---|
666 | done in ASN.1 definitions, and to name the corresponding function with
|
---|
667 | the first character of the name in lower case. You could, of course,
|
---|
668 | name your structures, types, and functions any way you please - as
|
---|
669 | long as you're consistent, and your code is easily readable.
|
---|
670 | <CODE>odr_ok</CODE> is
|
---|
671 | just that - a predicate that returns the state of the stream. It is
|
---|
672 | used to ensure that the behaviour of the new type is compatible with
|
---|
673 | the interface of the primitive types.
|
---|
674 | <P>
|
---|
675 | <H3>Tagging Constructed Types</H3>
|
---|
676 |
|
---|
677 | <P><I>NOTE: See section
|
---|
678 | <A HREF="#tag-prim">Tagging Primitive types</A>
|
---|
679 | for information on how to tag the primitive types, as well as types
|
---|
680 | that are already defined.</I>
|
---|
681 | <P>
|
---|
682 | <H3>Implicit Tagging</H3>
|
---|
683 |
|
---|
684 | <P>Assume the type above had been defined as
|
---|
685 | <P>
|
---|
686 | <BLOCKQUOTE><CODE>
|
---|
687 | <PRE>
|
---|
688 | MySequence ::= [10] IMPLICIT SEQUENCE {
|
---|
689 | intval INTEGER,
|
---|
690 | boolval BOOLEAN OPTIONAL }
|
---|
691 | </PRE>
|
---|
692 | </CODE></BLOCKQUOTE>
|
---|
693 | <P>You would implement this in <B>ODR</B> by calling the function
|
---|
694 | <P>
|
---|
695 | <BLOCKQUOTE><CODE>
|
---|
696 | <PRE>
|
---|
697 | int odr_implicit_settag(ODR o, int class, int tag);
|
---|
698 | </PRE>
|
---|
699 | </CODE></BLOCKQUOTE>
|
---|
700 | <P>which overrides the tag of the type immediately following it. The
|
---|
701 | macro <CODE>odr_implicit()</CODE> works by calling <CODE>odr_implicit_settag()</CODE>
|
---|
702 | immediately
|
---|
703 | before calling the function pointer argument. Your type function could
|
---|
704 | look like this:
|
---|
705 | <P>
|
---|
706 | <BLOCKQUOTE><CODE>
|
---|
707 | <PRE>
|
---|
708 | int mySequence(ODR o, MySequence **p, int optional, const char *name)
|
---|
709 | {
|
---|
710 | if (odr_implicit_settag(o, ODR_CONTEXT, 10) == 0 ||
|
---|
711 | odr_sequence_begin(o, p, sizeof(**p), name) == 0)
|
---|
712 | return optional && odr_ok(o);
|
---|
713 | return
|
---|
714 | odr_integer(o, &(*p)->intval, 0, "intval") &&
|
---|
715 | odr_bool(o, &(*p)->boolval, 1, "boolval") &&
|
---|
716 | odr_sequence_end(o);
|
---|
717 | }
|
---|
718 | </PRE>
|
---|
719 | </CODE></BLOCKQUOTE>
|
---|
720 | <P>The definition of the structure <CODE>MySequence</CODE> would be the same.
|
---|
721 | <P>
|
---|
722 | <H3>Explicit Tagging</H3>
|
---|
723 |
|
---|
724 | <P>Explicit tagging of constructed types is a little more complicated,
|
---|
725 | since you are in effect adding a level of construction to the data.
|
---|
726 | <P>Assume the definition:
|
---|
727 | <P>
|
---|
728 | <BLOCKQUOTE><CODE>
|
---|
729 | <PRE>
|
---|
730 | MySequence ::= [10] IMPLICIT SEQUENCE {
|
---|
731 | intval INTEGER,
|
---|
732 | boolval BOOLEAN OPTIONAL }
|
---|
733 | </PRE>
|
---|
734 | </CODE></BLOCKQUOTE>
|
---|
735 | <P>Since the new type has an extra level of construction, two new functions
|
---|
736 | are needed to encapsulate the base type:
|
---|
737 | <P>
|
---|
738 | <BLOCKQUOTE><CODE>
|
---|
739 | <PRE>
|
---|
740 | int odr_constructed_begin(ODR o, void *p, int class, int tag,
|
---|
741 | const char *name);
|
---|
742 |
|
---|
743 | int odr_constructed_end(ODR o);
|
---|
744 | </PRE>
|
---|
745 | </CODE></BLOCKQUOTE>
|
---|
746 | <P>Assume that the IMPLICIT in the type definition above were replaced
|
---|
747 | with EXPLICIT (or that the IMPLICIT keyword were simply deleted, which
|
---|
748 | would be equivalent). The structure definition would look the same,
|
---|
749 | but the function would look like this:
|
---|
750 | <P>
|
---|
751 | <BLOCKQUOTE><CODE>
|
---|
752 | <PRE>
|
---|
753 | int mySequence(ODR o, MySequence **p, int optional, const char *name)
|
---|
754 | {
|
---|
755 | if (odr_constructed_begin(o, p, ODR_CONTEXT, 10, name) == 0)
|
---|
756 | return optional && odr_ok(o);
|
---|
757 | if (o->direction == ODR_DECODE)
|
---|
758 | *p = odr_malloc(o, sizeof(**p));
|
---|
759 | if (odr_sequence_begin(o, p, sizeof(**p), 0) == 0)
|
---|
760 | {
|
---|
761 | *p = 0; /* this is almost certainly a protocol error */
|
---|
762 | return 0;
|
---|
763 | }
|
---|
764 | return
|
---|
765 | odr_integer(o, &(*p)->intval, 0, "intval") &&
|
---|
766 | odr_bool(o, &(*p)->boolval, 1, "boolval") &&
|
---|
767 | odr_sequence_end(o) &&
|
---|
768 | odr_constructed_end(o);
|
---|
769 | }
|
---|
770 | </PRE>
|
---|
771 | </CODE></BLOCKQUOTE>
|
---|
772 | <P>Notice that the interface here gets kind of nasty. The reason is
|
---|
773 | simple: Explicitly tagged, constructed types are fairly rare in
|
---|
774 | the protocols that we care about, so the
|
---|
775 | aesthetic annoyance (not to mention the dangers of a cluttered
|
---|
776 | interface) is less than the time that would be required to develop a
|
---|
777 | better interface. Nevertheless, it is far from satisfying, and it's a
|
---|
778 | point that will be worked on in the future. One option for you would
|
---|
779 | be to simply apply the <CODE>odr_explicit()</CODE> macro to the first function,
|
---|
780 | and not
|
---|
781 | have to worry about <CODE>odr_constructed_*</CODE> yourself. Incidentally, as you
|
---|
782 | might have guessed, the <CODE>odr_sequence_</CODE> functions are themselves
|
---|
783 | implemented using the <CODE>odr_constructed_</CODE> functions.
|
---|
784 | <P>
|
---|
785 | <H3>SEQUENCE OF</H3>
|
---|
786 |
|
---|
787 | <P>To handle sequences (arrays) of a apecific type, the function
|
---|
788 | <P>
|
---|
789 | <BLOCKQUOTE><CODE>
|
---|
790 | <PRE>
|
---|
791 | int odr_sequence_of(ODR o, int (*fun)(ODR o, void *p, int optional),
|
---|
792 | void *p, int *num, const char *name);
|
---|
793 | </PRE>
|
---|
794 | </CODE></BLOCKQUOTE>
|
---|
795 | <P>The <CODE>fun</CODE> parameter is a pointer to the decoder/encoder
|
---|
796 | function of the type. <CODE>p</CODE> is a pointer to an array of pointers to your
|
---|
797 | type. <CODE>num</CODE> is the number of elements in the array.
|
---|
798 | <P>Assume a type
|
---|
799 | <P>
|
---|
800 | <BLOCKQUOTE><CODE>
|
---|
801 | <PRE>
|
---|
802 | MyArray ::= SEQUENCE OF INTEGER
|
---|
803 | </PRE>
|
---|
804 | </CODE></BLOCKQUOTE>
|
---|
805 | <P>The C representation might be
|
---|
806 | <P>
|
---|
807 | <BLOCKQUOTE><CODE>
|
---|
808 | <PRE>
|
---|
809 | typedef struct MyArray
|
---|
810 | {
|
---|
811 | int num_elements;
|
---|
812 | int **elements;
|
---|
813 | } MyArray;
|
---|
814 | </PRE>
|
---|
815 | </CODE></BLOCKQUOTE>
|
---|
816 | <P>And the function might look like
|
---|
817 | <P>
|
---|
818 | <BLOCKQUOTE><CODE>
|
---|
819 | <PRE>
|
---|
820 | int myArray(ODR o, MyArray **p, int optional, const char *name)
|
---|
821 | {
|
---|
822 | if (o->direction == ODR_DECODE)
|
---|
823 | *p = odr_malloc(o, sizeof(**p));
|
---|
824 | if (odr_sequence_of(o, odr_integer, &(*p)->elements,
|
---|
825 | &(*p)->num_elements, name))
|
---|
826 | return 1;
|
---|
827 | *p = 0;
|
---|
828 | return optional && odr_ok(o);
|
---|
829 | }
|
---|
830 | </PRE>
|
---|
831 | </CODE></BLOCKQUOTE>
|
---|
832 | <P>
|
---|
833 | <H3>CHOICE Types</H3>
|
---|
834 |
|
---|
835 | <P>The choice type is used fairly often in some ASN.1 definitions, so
|
---|
836 | some work has gone into streamlining its interface.
|
---|
837 | <P>CHOICE types are handled by the function:
|
---|
838 | <P>
|
---|
839 | <BLOCKQUOTE><CODE>
|
---|
840 | <PRE>
|
---|
841 | int odr_choice(ODR o, Odr_arm arm[], void *p, void *whichp,
|
---|
842 | const char *name);
|
---|
843 | </PRE>
|
---|
844 | </CODE></BLOCKQUOTE>
|
---|
845 | <P>The <CODE>arm</CODE> array is used to describe each of the possible types that the
|
---|
846 | CHOICE type may assume. Internally in your application, the CHOICE
|
---|
847 | type is represented as a discriminated union. That is, a C union
|
---|
848 | accompanied by an integer (or enum) identifying the active 'arm' of
|
---|
849 | the union. <CODE>whichp</CODE> is a pointer to the union discriminator. When
|
---|
850 | encoding, it is examined to determine the current type. When decoding,
|
---|
851 | it is set to reference the type that was found in the input stream.
|
---|
852 | <P>The Odr_arm type is defined thus:
|
---|
853 | <P>
|
---|
854 | <BLOCKQUOTE><CODE>
|
---|
855 | <PRE>
|
---|
856 | typedef struct odr_arm
|
---|
857 | {
|
---|
858 | int tagmode;
|
---|
859 | int class;
|
---|
860 | int tag;
|
---|
861 | int which;
|
---|
862 | Odr_fun fun;
|
---|
863 | char *name;
|
---|
864 | } Odr_arm;
|
---|
865 | </PRE>
|
---|
866 | </CODE></BLOCKQUOTE>
|
---|
867 | <P>The interpretation of the fields are:
|
---|
868 | <P>
|
---|
869 | <DL>
|
---|
870 | <DT><B>tagmode</B><DD><P>Either <CODE>ODR_IMPLICIT</CODE>, <CODE>ODR_EXPLICIT</CODE>, or <CODE>ODR_NONE</CODE>
|
---|
871 | (-1) to mark
|
---|
872 | no tagging.
|
---|
873 | <DT><B>class, tag</B><DD><P>The class and tag of the type (-1 if no tagging is
|
---|
874 | used).
|
---|
875 | <DT><B>which</B><DD><P>The value of the discriminator that corresponds to
|
---|
876 | this CHOICE element. Typically, it will be a #defined constant, or
|
---|
877 | an enum member.
|
---|
878 | <DT><B>fun</B><DD><P>A pointer to a function that implements the type of
|
---|
879 | the CHOICE member. It may be either a standard <B>ODR</B> type or a type
|
---|
880 | defined by yourself.
|
---|
881 | <DT><B>name</B><DD><P>Name of tag.
|
---|
882 | </DL>
|
---|
883 | <P>A handy way to prepare the array for use by the <CODE>odr_choice()</CODE> function
|
---|
884 | is to
|
---|
885 | define it as a static, initialized array in the beginning of your
|
---|
886 | decoding/encoding function. Assume the type definition:
|
---|
887 | <P>
|
---|
888 | <BLOCKQUOTE><CODE>
|
---|
889 | <PRE>
|
---|
890 | MyChoice ::= CHOICE {
|
---|
891 | untagged INTEGER,
|
---|
892 | tagged [99] IMPLICIT INTEGER,
|
---|
893 | other BOOLEAN
|
---|
894 | }
|
---|
895 | </PRE>
|
---|
896 | </CODE></BLOCKQUOTE>
|
---|
897 | <P>Your C type might look like
|
---|
898 | <P>
|
---|
899 | <BLOCKQUOTE><CODE>
|
---|
900 | <PRE>
|
---|
901 | typedef struct MyChoice
|
---|
902 | {
|
---|
903 | enum
|
---|
904 | {
|
---|
905 | MyChoice_untagged,
|
---|
906 | MyChoice_tagged,
|
---|
907 | MyChoice_other
|
---|
908 | } which;
|
---|
909 | union
|
---|
910 | {
|
---|
911 | int *untagged;
|
---|
912 | int *tagged;
|
---|
913 | bool_t *other;
|
---|
914 | } u;
|
---|
915 | };
|
---|
916 | </PRE>
|
---|
917 | </CODE></BLOCKQUOTE>
|
---|
918 | <P>And your function could look like this:
|
---|
919 | <P>
|
---|
920 | <BLOCKQUOTE><CODE>
|
---|
921 | <PRE>
|
---|
922 | int myChoice(ODR o, MyChoice **p, int optional, const char *name)
|
---|
923 | {
|
---|
924 | static Odr_arm arm[] =
|
---|
925 | {
|
---|
926 | {-1, -1, -1, MyChoice_untagged, odr_integer, "untagged"},
|
---|
927 | {ODR_IMPLICIT, ODR_CONTEXT, 99, MyChoice_tagged, odr_integer,
|
---|
928 | "tagged"},
|
---|
929 | {-1, -1, -1, MyChoice_other, odr_boolean, "other"},
|
---|
930 | {-1, -1, -1, -1, 0}
|
---|
931 | };
|
---|
932 |
|
---|
933 | if (o->direction == ODR_DECODE)
|
---|
934 | *p = odr_malloc(o, sizeof(**p);
|
---|
935 | else if (!*p)
|
---|
936 | return optional && odr_ok(o);
|
---|
937 |
|
---|
938 | if (odr_choice(o, arm, &(*p)->u, &(*p)->which), name)
|
---|
939 | return 1;
|
---|
940 | *p = 0;
|
---|
941 | return optional && odr_ok(o);
|
---|
942 | }
|
---|
943 | </PRE>
|
---|
944 | </CODE></BLOCKQUOTE>
|
---|
945 | <P>In some cases (say, a non-optional choice which is a member of a sequence),
|
---|
946 | you can "embed" the union and its discriminator in the structure
|
---|
947 | belonging to the enclosing type, and you won't need to fiddle with
|
---|
948 | memory allocation to create a separate structure to wrap the
|
---|
949 | discriminator and union.
|
---|
950 | <P>The corresponding function is somewhat nicer in the Sun XDR interface.
|
---|
951 | Most of the complexity of this interface comes from the possibility of
|
---|
952 | declaring sequence elements (including CHOICEs) optional.
|
---|
953 | <P>The ASN.1 specifictions naturally requires that each member of a
|
---|
954 | CHOICE have a distinct tag, so they can be told apart on decoding.
|
---|
955 | Sometimes it can be useful to define a CHOICE that has multiple types
|
---|
956 | that share the same tag. You'll need some other mechanism, perhaps
|
---|
957 | keyed to the context of the CHOICE type. In effect, we would like to
|
---|
958 | introduce a level of context-sensitiveness to our ASN.1 specification.
|
---|
959 | When encoding an internal representation, we have no problem, as long
|
---|
960 | as each CHOICE member has a distinct discriminator value. For
|
---|
961 | decoding, we need a way to tell the choice function to look for a
|
---|
962 | specific arm of the table. The function
|
---|
963 | <P>
|
---|
964 | <BLOCKQUOTE><CODE>
|
---|
965 | <PRE>
|
---|
966 | void odr_choice_bias(ODR o, int what);
|
---|
967 | </PRE>
|
---|
968 | </CODE></BLOCKQUOTE>
|
---|
969 | <P>provides this functionality. When called, it leaves a notice for the
|
---|
970 | next call to <CODE>odr_choice()</CODE> to be called on the decoding
|
---|
971 | stream <CODE>o</CODE> that only the <CODE>arm</CODE> entry with a <CODE>which</CODE> field
|
---|
972 | equal to <CODE>what</CODE> should be tried.
|
---|
973 | <P>The most important application (perhaps the only one, really) is in
|
---|
974 | the definition of application-specific EXTERNAL encoders/decoders
|
---|
975 | which will automatically decode an ANY member given the direct or
|
---|
976 | indirect reference.
|
---|
977 | <P>
|
---|
978 | <H2><A NAME="ss5.4">5.4 Debugging</A>
|
---|
979 | </H2>
|
---|
980 |
|
---|
981 | <P>The protocol modules are suffering somewhat from a lack of diagnostic
|
---|
982 | tools at the moment. Specifically ways to pretty-print PDUs that
|
---|
983 | aren't recognized by the system. We'll include something to this end
|
---|
984 | in a not-too-distant release. In the meantime, what we do when we get
|
---|
985 | packages we don't understand is to compile the ODR module with
|
---|
986 | <CODE>ODR_DEBUG</CODE> defined. This causes the module to dump tracing
|
---|
987 | information as it processes data units. With this output and the
|
---|
988 | protocol specification (Z39.50), it is generally fairly easy to see
|
---|
989 | what goes wrong.
|
---|
990 | <P>
|
---|
991 | <HR>
|
---|
992 | <A HREF="yaz-6.html">Next</A>
|
---|
993 | <A HREF="yaz-4.html">Previous</A>
|
---|
994 | <A HREF="yaz.html#toc5">Contents</A>
|
---|
995 | </BODY>
|
---|
996 | </HTML>
|
---|