-<!-- $Id: odr.xml,v 1.3 2001-07-20 21:34:36 adam Exp $ -->
- <chapter><title id="odr">The ODR Module</title>
-
- <sect1><title>Introduction</title>
+ <chapter id="odr"><title>The ODR Module</title>
+
+ <sect1 id="odr.introduction"><title>Introduction</title>
<para>
&odr; is the BER-encoding/decoding subsystem of &yaz;. Care as been taken
<para>
If you are only interested in writing a Z39.50 implementation based on
the PDUs that are already provided with &yaz;, you only need to concern
- yourself with the section on managing ODR streams (section
- <link linkend="odr-use">Using ODR</link>). Only if you need to
+ yourself with the section on managing ODR streams
+ (<xref linkend="odr.use"/>). Only if you need to
implement ASN.1 beyond that which has been provided, should you
worry about the second half of the documentation
- (section <link linkend="odr-prog">Programming with ODR</link>).
+ (<xref linkend="odr.programming"/>).
If you use one of the higher-level interfaces, you can skip this
section entirely.
</para>
<para>
- This is important, so we'll repeat it for emphasis: <emphasis>You do not
- need to read section <link linkend="odr-prog">Programming with ODR</link> to
- implement Z39.50 with &yaz;.</emphasis>
+ This is important, so we'll repeat it for emphasis: <emphasis>You do
+ not need to read <xref linkend="odr.programming"/>
+ to implement Z39.50 with &yaz;.</emphasis>
</para>
<para>
</para>
</sect1>
- <sect1><title id="odr-use">Using ODR</title>
+ <sect1 id="odr.use"><title>Using ODR</title>
- <sect2><title>ODR Streams</title>
+ <sect2 id="odr.streams"><title>ODR Streams</title>
<para>
Conceptually, the ODR stream is the source of encoded data in the
</para>
</sect2>
- <sect2><title id="memory">Memory Management</title>
+ <sect2 id="odr.memory.management"><title id="memory">Memory Management</title>
<para>
Two forms of memory management take place in the &odr; system. The first
</para>
<synopsis>
- void *odr_malloc(ODR o, int size);
+ void *odr_malloc(ODR o, size_t size);
</synopsis>
<para>
</para>
<synopsis>
- void odr_reset(ODR o, int size);
+ void odr_reset(ODR o);
</synopsis>
<para>
</para>
<synopsis>
- int odr_total(ODR o);
+ size_t odr_total(ODR o);
</synopsis>
<para>
<para>
The memory subsystem of &odr; is fairly efficient at allocating and
releasing little bits of memory. Rather than managing the individual,
- small bits of space, the system maintains a freelist of larger chunks
+ small bits of space, the system maintains a free-list of larger chunks
of memory, which are handed out in small bits. This scheme is
generally known as a <emphasis>nibble memory</emphasis> system.
- It is very useful for maintaing short-lived constructions such
+ It is very useful for maintaining short-lived constructions such
as protocol PDUs.
</para>
</para>
</sect2>
- <sect2><title>Encoding and Decoding Data</title>
+ <sect2 id="odr.encoding.and.decoding"><title>Encoding and Decoding Data</title>
<para>
When encoding data, the ODR stream will write the encoded octet string
The integer pointed to by len is set to the length of the encoded
data, and a pointer to that data is returned. <literal>*size</literal>
is set to the size of the buffer (unless <literal>size</literal> is null,
- signalling that you are not interested in the size). The next call to
+ signaling that you are not interested in the size). The next call to
a primitive function using the same &odr; stream will overwrite the
data, unless a different buffer has been supplied using the call
</para>
</para>
<para>
- It is important to realise that the ODR stream will not release this
+ It is important to realize that the ODR stream will not release this
memory when you call <function>odr_reset()</function>: It will
merely update its internal pointers to prepare for the encoding of a
new data value.
be released <emphasis>only</emphasis> if the <literal>can_grow</literal>
parameter to <function>odr_setbuf()</function> was nonzero. The
<literal>can_grow</literal> parameter, in other words, is a way of
- signalling who is to own the buffer, you or the ODR stream. If you never call
+ signaling who is to own the buffer, you or the ODR stream. If you never call
<function>odr_setbuf()</function> on your encoding stream, which is
typically the case, the buffer allocated by the stream will belong to
the stream by default.
<function>z_APDU()</function>).
</para>
- <para>
- Examples of encoding/decoding functions:
- </para>
-
- <synopsis>
- int odr_integer(ODR o, int **p, int optional, const char *name);
+ <example id="example.odr.encoding.and.decoding.functions">
+ <title>Encoding and decoding functions</title>
+ <synopsis>
+ int odr_integer(ODR o, Odr_int **p, int optional, const char *name);
- int z_APDU(ODR o, Z_APDU **p, int optional, const char *name);
- </synopsis>
+ int z_APDU(ODR o, Z_APDU **p, int optional, const char *name);
+ </synopsis>
+ </example>
<para>
If the data is absent (or doesn't match the tag corresponding to
<function>free(2)</function> to release the memory.
You can decode several data elements (by repeated calls to
<function>odr_setbuf()</function> and your decoding function), and
- new memory will be allocated each time. When you do call
+ new memory will be allocated each time. When you do call
<function>odr_reset()</function>, everything decoded since the
last call to <function>odr_reset()</function> will be released.
</para>
- <para>
- The use of the double indirection can be a little confusing at first
- (its purpose will become clear later on, hopefully),
- so an example is in order. We'll encode an integer value, and
- immediately decode it again using a different stream. A useless, but
- informative operation.
- </para>
-
- <programlisting>
-
-void do_nothing_useful(int value)
+ <example id="example.odr.encoding.of.integer">
+ <title>Encoding and decoding of an integer</title>
+ <para>
+ The use of the double indirection can be a little confusing at first
+ (its purpose will become clear later on, hopefully),
+ so an example is in order. We'll encode an integer value, and
+ immediately decode it again using a different stream. A useless, but
+ informative operation.
+ </para>
+ <programlisting><![CDATA[
+void do_nothing_useful(Odr_int value)
{
ODR encode, decode;
- int *valp, *resvalp;
+ Odr_int *valp, *resvalp;
char *bufferp;
int len;
-
+
/* allocate streams */
if (!(encode = odr_createmem(ODR_ENCODE)))
return;
if (!(decode = odr_createmem(ODR_DECODE)))
return;
- valp = &value;
- if (odr_integer(encode, &valp, 0, 0) == 0)
+ valp = &value;
+ if (odr_integer(encode, &valp, 0, 0) == 0)
{
printf("encoding went bad\n");
return;
}
- bufferp = odr_getbuf(encode, &len);
- printf("length of encoded data is %d\n", len);
+ bufferp = odr_getbuf(encode, &len, 0);
+ printf("length of encoded data is %d\n", len);
/* now let's decode the thing again */
- odr_setbuf(decode, bufferp, len);
- if (odr_integer(decode, &resvalp, 0, 0) == 0)
+ odr_setbuf(decode, bufferp, len, 0);
+ if (odr_integer(decode, &resvalp, 0, 0) == 0)
{
printf("decoding went bad\n");
return;
}
- printf("the value is %d\n", *resvalp);
+ /* ODR_INT_PRINTF format for printf (such as %d) */
+ printf("the value is " ODR_INT_PRINTF "\n", *resvalp);
/* clean up */
odr_destroy(encode);
odr_destroy(decode);
}
- </programlisting>
+]]>
+ </programlisting>
+ <para>
+ This looks like a lot of work, offhand. In practice, the &odr; streams
+ will typically be allocated once, in the beginning of your program
+ (or at the beginning of a new network session), and the encoding
+ and decoding will only take place in a few, isolated places in your
+ program, so the overhead is quite manageable.
+ </para>
+ </example>
+
+ </sect2>
+ <sect2 id="odr.printing"><title>Printing</title>
<para>
- This looks like a lot of work, offhand. In practice, the &odr; streams
- will typically be allocated once, in the beginning of your program
- (or at the beginning of a new network session), and the encoding
- and decoding will only take place in a few, isolated places in your
- program, so the overhead is quite manageable.
+ When an ODR stream is created of type <literal>ODR_PRINT</literal>
+ the ODR module will print the contents of a PDU in a readable format.
+ By default output is written to the <literal>stderr</literal> stream.
+ This behavior can be changed, however, by calling the function
+ <synopsis>
+ odr_setprint(ODR o, FILE *file);
+ </synopsis>
+ before encoders or decoders are being invoked.
+ It is also possible to direct the output to a buffer (of indeed
+ another file), by using the more generic mechanism:
+ <synopsis>
+ void odr_set_stream(ODR o, void *handle,
+ void (*stream_write)(ODR o, void *handle, int type,
+ const char *buf, int len),
+ void (*stream_close)(void *handle));
+ </synopsis>
+ Here the user provides an opaque handle and two handlers,
+ <replaceable>stream_write</replaceable> for writing,
+ and <replaceable>stream_close</replaceable> which is supposed
+ to close/free resources associated with handle.
+ The <replaceable>stream_close</replaceable> handler is optional and
+ if NULL for the function is provided, it will not be invoked.
+ The <replaceable>stream_write</replaceable> takes the ODR handle
+ as parameter, the user defined handle, a type
+ <literal>ODR_OCTETSTRING</literal>, <literal>ODR_VISIBLESTRING</literal>
+ which indicates the type of contents is being written.
</para>
-
+ <para>
+ Another utility useful for diagnostics (error handling) or as
+ part of the printing facilities is:
+ <synopsis>
+ const char **odr_get_element_path(ODR o);
+ </synopsis>
+ which returns a list of current elements that ODR deals with at the
+ moment. For the returned array, say <literal>ar</literal>,
+ <literal>ar[0]</literal> is the top level element,
+ <literal>ar[n]</literal> is the last. The last element has the
+ property that <literal>ar[n+1] == NULL</literal>.
+ </para>
+ <example id="example.odr.element.path.record">
+ <title>Element Path for record</title>
+ <para>
+ For a database record part of a PresentResponse the
+ array returned by <function>odr_get_element</function>
+ is <literal>presentResponse</literal>, <literal>databaseOrSurDiagnostics</literal>, <literal>?</literal>, <literal>record</literal>, <literal>?</literal>, <literal>databaseRecord</literal> . The question mark appears due to
+ unnamed constructions.
+ </para>
+ </example>
</sect2>
-
- <sect2><title>Diagnostics</title>
+ <sect2 id="odr.diagnostics"><title>Diagnostics</title>
<para>
The encoding/decoding functions all return 0 when an error occurs.
one of these constants:
</para>
- <table frame="top"><title>ODR Error codes</title>
+ <table frame="top" id="odr.error.codes">
+ <title>ODR Error codes</title>
<tgroup cols="2">
<thead>
<row>
</para>
<synopsis>
- char *odr_errlist[]
+ char *odr_errlist[]
</synopsis>
<para>
</para>
</sect2>
- <sect2><title>Summary and Synopsis</title>
+ <sect2 id="odr.summary.and.synopsis">
+ <title>Summary and Synopsis</title>
<synopsis>
- #include <odr.h>
+ #include <yaz/odr.h>
ODR odr_createmem(int direction);
void odr_reset(ODR o);
- char *odr_getbuf(ODR o, int *len);
+ char *odr_getbuf(ODR o, int *len, int *size);
- void odr_setbuf(ODR o, char *buf, int len);
+ void odr_setbuf(ODR o, char *buf, int len, int can_grow);
void *odr_malloc(ODR o, int size);
- ODR_MEM odr_extract_mem(ODR o);
-
- void odr_release_mem(ODR_MEM r);
+ NMEM odr_extract_mem(ODR o);
int odr_geterror(ODR o);
- void odr_perror(char *message);
+ void odr_perror(ODR o, const char *message);
extern char *odr_errlist[];
</synopsis>
</sect2>
</sect1>
- <sect1><title id="odr-prog">Programming with ODR</title>
+ <sect1 id="odr.programming"><title>Programming with ODR</title>
<para>
The API of &odr; is designed to reflect the structure of ASN.1, rather
other external forms.
</para>
+ <tip>
+ <para>
+ There is an ASN.1 tutorial available at
+ <ulink url="&url.asn.1.tutorial;">this site</ulink>.
+ This site also has standards for ASN.1 (X.680) and BER (X.690)
+ <ulink url="&url.asn.1.standards;">online</ulink>.
+ </para>
+ </tip>
+
<para>
- The interface is based loosely on that of the Sun Microsystems XDR routines.
+ The ODR interface is based loosely on that of the Sun Microsystems
+ XDR routines.
Specifically, each function which corresponds to an ASN.1 primitive
type has a dual function. Depending on the settings of the ODR
stream which is supplied as a parameter, the function may be used
either to encode or decode data. The functions that can be built
- using these primitive functions, to represent more complex datatypes, share
- this quality. The result is that you only have to enter the definition
- for a type once - and you have the functionality of encoding, decoding
- (and pretty-printing) all in one unit. The resulting C source code is
- quite compact, and is a pretty straightforward representation of the
- source ASN.1 specification. Although no ASN.1 compiler is supplied
- with &odr; at this time, it shouldn't be too difficult to write one, or
- perhaps even to adapt an existing compiler to output &odr; routines
- (not surprisingly, writing encoders/decoders using &odr; turns out
- to be boring work).
+ using these primitive functions, to represent more complex data types,
+ share this quality. The result is that you only have to enter the
+ definition for a type once - and you have the functionality of encoding,
+ decoding (and pretty-printing) all in one unit.
+ The resulting C source code is quite compact, and is a pretty
+ straightforward representation of the source ASN.1 specification.
</para>
<para>
In many cases, the model of the XDR functions works quite well in this
role.
In others, it is less elegant. Most of the hassle comes from the optional
- SEQUENCE memebers which don't exist in XDR.
+ SEQUENCE members which don't exist in XDR.
</para>
- <sect2><title>The Primitive ASN.1 Types</title>
+ <sect2 id="odr.primitive.asn1.types">
+ <title>The Primitive ASN.1 Types</title>
<para>
ASN.1 defines a number of primitive types (many of which correspond
roughly to primitive types in structured programming languages, such as C).
</para>
- <sect3><title>INTEGER</title>
+ <sect3 id="odr.integer"><title>INTEGER</title>
<para>
The &odr; function for encoding or decoding (or printing) the ASN.1
</para>
<synopsis>
-int odr_integer(ODR o, int **p, int optional, const char *name);
+ int odr_integer(ODR o, Odr_int **p, int optional, const char *name);
</synopsis>
<para>
- (we don't allow values that can't be contained in a C integer.)
+ The <literal>Odr_int</literal> is just a simple integer.
</para>
<para>
similar manners:
</para>
</sect3>
- <sect3><title>BOOLEAN</title>
+ <sect3 id="odr.boolean"><title>BOOLEAN</title>
<synopsis>
-int odr_bool(ODR o, bool_t **p, int optional, const char *name);
+int odr_bool(ODR o, Odr_bool **p, int optional, const char *name);
</synopsis>
</sect3>
- <sect3><title>REAL</title>
+ <sect3 id="odr.real"><title>REAL</title>
<para>
Not defined.
</para>
</sect3>
- <sect3><title>NULL</title>
+ <sect3 id="odr.null"><title>NULL</title>
<synopsis>
-int odr_null(ODR o, bool_t **p, int optional, const char *name);
+int odr_null(ODR o, Odr_null **p, int optional, const char *name);
</synopsis>
<para>
</para>
</sect3>
- <sect3><title>OCTET STRING</title>
+ <sect3 id="odr.octet.string"><title>OCTET STRING</title>
<synopsis>
typedef struct odr_oct
</synopsis>
</sect3>
- <sect3><title>BIT STRING</title>
+ <sect3 id="odr.bit.string"><title>BIT STRING</title>
<synopsis>
int odr_bitstring(ODR o, Odr_bitmask **p, int optional,
</synopsis>
<para>
- The functions are modelled after the manipulation functions that
+ The functions are modeled after the manipulation functions that
accompany the <literal>fd_set</literal> type used by the
<function>select(2)</function> call.
<literal>ODR_MASK_ZERO</literal> should always be called first on a
</para>
</sect3>
- <sect3><title>OBJECT IDENTIFIER</title>
+ <sect3 id="odr.object.identifier"><title>OBJECT IDENTIFIER</title>
<synopsis>
int odr_oid(ODR o, Odr_oid **p, int optional, const char *name);
</synopsis>
<para>
- The C OID represenation is simply an array of integers, terminated by
+ The C OID representation is simply an array of integers, terminated by
the value -1 (the <literal>Odr_oid</literal> type is synonymous with
- the <literal>int</literal> type).
- We suggest that you use the OID database module (see section
- <link linkend="oid">Object Identifiers</link>) to handle object identifiers
+ the <literal>short</literal> type).
+ We suggest that you use the OID database module (see
+ <xref linkend="tools.oid.database"/>) to handle object identifiers
in your application.
</para>
</sect3>
</sect2>
- <sect2><title id="tag-prim">Tagging Primitive Types</title>
+ <sect2 id="odr.tagging.primitive.types"><title>Tagging Primitive Types</title> <!-- tag.prim -->
<para>
The simplest way of tagging a type is to use the
- <function>odr_implicit_tag()</function> or
+ <function>odr_implicit_tag()</function> or
<function>odr_explicit_tag()</function> macros:
</para>
</para>
<screen>
- MyInt ::= [210] IMPLICIT INTEGER
+ MyInt ::= [210] IMPLICIT INTEGER
</screen>
<para>
</para>
<screen>
-int myInt(ODR o, int **p, int optional, const char *name)
+int myInt(ODR o, Odr_int **p, int optional, const char *name)
{
return odr_implicit_tag(o, odr_integer, p,
ODR_CONTEXT, 210, optional, name);
<para>
The function <function>myInt()</function> can then be used like any of
the primitive functions provided by &odr;. Note that the behavior of
- <function>odr_explicit()</function>
- and <function>odr_implicit()</function> macros
+ <function>odr_explicit_tag()</function>
+ and <function>odr_implicit_tag()</function> macros
act exactly the same as the functions they are applied to - they
respond to error conditions, etc, in the same manner - they
simply have three extra parameters. The class parameter may
</para>
</sect2>
- <sect2><title>Constructed Types</title>
+ <sect2 id="odr.constructed.types"><title>Constructed Types</title>
<para>
Constructed types are created by combining primitive types. The
<screen>
typedef struct MySequence
{
- int *intval;
- bool_t *boolval;
+ Odr_int *intval;
+ Odr_bool *boolval;
} MySequence;
-
+
int mySequence(ODR o, MySequence **p, int optional, const char *name)
{
if (odr_sequence_begin(o, p, sizeof(**p), name) == 0)
Note the 1 in the call to <function>odr_bool()</function>, to mark
that the sequence member is optional.
If either of the member types had been tagged, the macros
- <function>odr_implicit()</function> or <function>odr_explicit()</function>
+ <function>odr_implicit_tag()</function> or
+ <function>odr_explicit_tag()</function>
could have been used.
The new function can be used exactly like the standard functions provided
with &odr;. It will encode, decode or pretty-print a data value of the
You could, of course, name your structures, types, and functions any way
you please - as long as you're consistent, and your code is easily readable.
<literal>odr_ok</literal> is just that - a predicate that returns the
- state of the stream. It is used to ensure that the behaviour of the new
+ state of the stream. It is used to ensure that the behavior of the new
type is compatible with the interface of the primitive types.
</para>
</sect2>
- <sect2><title>Tagging Constructed Types</title>
+ <sect2 id="odr.tagging.constructed.types">
+ <title>Tagging Constructed Types</title>
<note>
<para>
- See section <link linkend="tag-prim">Tagging Primitive types</link>
- for information on how to tag the primitive types, as well as types
- that are already defined.
+ See <xref linkend="odr.tagging.primitive.types"/> for information on how to tag
+ the primitive types, as well as types that are already defined.
</para>
</note>
- <sect3><title>Implicit Tagging</title>
+ <sect3 id="odr.implicit.tagging">
+ <title>Implicit Tagging</title>
<para>
Assume the type above had been defined as
</para>
<screen>
-MySequence ::= [10] IMPLICIT SEQUENCE {
+MySequence ::= [10] IMPLICIT SEQUENCE {
intval INTEGER,
boolval BOOLEAN OPTIONAL
}
<para>
which overrides the tag of the type immediately following it. The
- macro <function>odr_implicit()</function> works by calling
+ macro <function>odr_implicit_tag()</function> works by calling
<function>odr_implicit_settag()</function> immediately
before calling the function pointer argument.
Your type function could look like this:
</para>
</sect3>
- <sect3><title>Explicit Tagging</title>
+ <sect3 id="odr.explicit.tagging"><title>Explicit Tagging</title>
<para>
Explicit tagging of constructed types is a little more complicated,
</para>
<screen>
-MySequence ::= [10] IMPLICIT SEQUENCE {
+MySequence ::= [10] IMPLICIT SEQUENCE {
intval INTEGER,
boolval BOOLEAN OPTIONAL
}
Notice that the interface here gets kind of nasty. The reason is
simple: Explicitly tagged, constructed types are fairly rare in
the protocols that we care about, so the
- aesthetic annoyance (not to mention the dangers of a cluttered
+ esthetic annoyance (not to mention the dangers of a cluttered
interface) is less than the time that would be required to develop a
better interface. Nevertheless, it is far from satisfying, and it's a
point that will be worked on in the future. One option for you would
- be to simply apply the <function>odr_explicit()</function> macro to
+ be to simply apply the <function>odr_explicit_tag()</function> macro to
the first function, and not
have to worry about <function>odr_constructed_*</function> yourself.
Incidentally, as you might have guessed, the
</sect3>
</sect2>
- <sect2><title>SEQUENCE OF</title>
+ <sect2 id="odr.sequence.of"><title>SEQUENCE OF</title>
<para>
- To handle sequences (arrays) of a apecific type, the function
+ To handle sequences (arrays) of a specific type, the function
</para>
<synopsis>
typedef struct MyArray
{
int num_elements;
- int **elements;
+ Odr_int **elements;
} MyArray;
</screen>
</screen>
</sect2>
- <sect2><title>CHOICE Types</title>
+ <sect2 id="odr.choice.types"><title>CHOICE Types</title>
<para>
The choice type is used fairly often in some ASN.1 definitions, so
</para>
<synopsis>
-int odr_choice(ODR o, Odr_arm arm[], void *p, void *whichp,
+int odr_choice(ODR o, Odr_arm arm[], void *p, void *whichp,
const char *name);
</synopsis>
<varlistentry><term>which</term>
<listitem><para>The value of the discriminator that corresponds to
- this CHOICE element. Typically, it will be a #defined constant, or
+ this CHOICE element. Typically, it will be a #defined constant, or
an enum member.</para></listitem>
</varlistentry>
<screen>
MyChoice ::= CHOICE {
untagged INTEGER,
- tagged [99] IMPLICIT INTEGER,
+ tagged [99] IMPLICIT INTEGER,
other BOOLEAN
}
</screen>
} which;
union
{
- int *untagged;
- int *tagged;
- bool_t *other;
+ Odr_int *untagged;
+ Odr_int *tagged;
+ Odr_bool *other;
} u;
};
</screen>
<screen>
int myChoice(ODR o, MyChoice **p, int optional, const char *name)
{
- static Odr_arm arm[] =
+ static Odr_arm arm[] =
{
{-1, -1, -1, MyChoice_untagged, odr_integer, "untagged"},
{ODR_IMPLICIT, ODR_CONTEXT, 99, MyChoice_tagged, odr_integer,
</para>
<para>
- The ASN.1 specifictions naturally requires that each member of a
+ The ASN.1 specifications naturally requires that each member of a
CHOICE have a distinct tag, so they can be told apart on decoding.
Sometimes it can be useful to define a CHOICE that has multiple types
that share the same tag. You'll need some other mechanism, perhaps
</sect2>
</sect1>
- <sect1><title>Debugging</title>
+ <sect1 id="odr.debugging"><title>Debugging</title>
<para>
The protocol modules are suffering somewhat from a lack of diagnostic
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document: "yaz.xml"
- sgml-local-catalogs: "../../docbook/docbook.cat"
+ sgml-local-catalogs: nil
sgml-namecase-general:t
End:
-->