OSSP CVS Repository

ossp - ossp-pkg/xds/docs/libxds.tex 1.3
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/xds/docs/libxds.tex 1.3
% -*- mode: LaTeX; fill-column: 75; -*-
%
% $Id: libxds.tex,v 1.3 2001/08/07 14:41:06 simons Exp $
%
\documentclass[a4paper,10pt,pointlessnumbers,bibtotoc]{scrartcl}
\usepackage[dvips,xdvi]{graphicx}
\usepackage{fancyvrb}
\typearea[2cm]{12}
\fussy

\begin{document}

\subject{Cable \& Wireless Application Development}
\title{XDS -- eXtensible Data Serialization}
\author{Peter Simons $<$simons@computer.org$>$}
\date{2001-08-01}
\maketitle

\section{Introduction}

In today's networked world, computer systems of all brands and flavours
communicate with each other. Unfortunately, these systems are far from
being identical: Many systems use different internal representations for
the same thing. Look at the (hexadecimal) number \$1234 for instance: On a
big endian machine, this number will be stored in memory the way you'd
intuitively expect: \$12~\$34 --- the more significant byte preceeds the
less significant one. On a little endian machine, though, the number \$1234
will be stored like this: \$34~\$12 --- exactly the other way round.

As a result, you cannot just write the number \$1234 to a socket and expect
the other end to understand it correctly, because if the endians differ,
the reader will read a different number than the writer sent. Things will
get even more complicated when you start exchanging floating point numbers,
for which about a dozen different encodings exist!

Solving these problems is the domain of libxds; its purpose is to encode
data in a way that allows this data to be exchanged between computer
systems of different types. Assume you'd want to reliably transfer the
value \$1234 from host A to host B. Then you would encode the value using
libxds, transfer the encoded data via the network, and decode the value
again at the other end. Every application that follows this process will
read the correct value no matter what native representation its hosting
platform uses internally.

\begin{figure}[tbh]
    \begin{center}
        \includegraphics[width=\textwidth]{data-exchange.eps}
        \caption{Data exchange using libxds}
        \label{data exchange}
    \end{center}
\end{figure}

There is a rich variety of applications for such a functionality: libxds
may be used to encode data before it is written to disk or read from the
disk, it may be used to encode data to be exchanged between processes over
the network, etc. Because of this variety, special attention has been paid
to the library design.

\paragraph{The library has been designed to be extensible.}
The functionality is split into a generic encoding and decoding framework
and a set of formatting engines. These engines can be plugged into the
framework at run-time to actually encode and decode data. Because of this
architecture, libxds can be customized to deploy any data format the
developer sees fit. Included in the distribution are formatting engines for
the XDR format specified in \cite{xdr} and for the XML format specified in
\cite{xml}.

\paragraph{The library is convenient to use.}
An arbitrary number of variables can be encoded or decoded with one single
function call. All memory management is done by libxds, the developer
doesn't need bother to allocate or to manage buffers for the encoded or
decoded data. Automatic buffer management can be switched off at run-time,
though, for maximum performance.

\paragraph{Performance.}
Since all transferred data has to wander through libxds, the library has
been written to encode and decode with maximim performance. The generic
encoding framework adds almost no run-time overhead to the encoding
process. If non-automatic buffer management has been selected, hardly
anything but the actual formatting engines is executed.

\paragraph{Robustness.}
In order to verify that the library is working correctly, a set of
regression tests is included in the distribution. The test suits will ---
among other things --- encode known values and compare the result with the
expected (correct) values. This ensures that libxds works correctly on any
platform.

\paragraph{Use standard formats.}
The supported formats XDR and XML and widely known and accepted formats,
which are most likely interoperable with other marshaling implementations.
For XDR for example, it would be possible to encode data with libxds and to
decode it with an entirely different XDR implementation or vice versa.

\paragraph{Portability.}
libxds has been written with portability in mind. Development took place on
FreeBSD, Linux and Solaris; other platforms has been used to test the
results. It is expected that libxds will compile and function on virtually
any POSIX.1-compilant system with a moderately modern ISO-C compiler. Gnu's
CC (gcc) is known to compile the library just fine. For maximum
portability, GNU autoconf has been used to determine the target system's
properties.

\section{Architecture of libxds}

\begin{figure}[htb]
    \begin{center}
        \includegraphics[width=\textwidth]{architecture.eps}
        \caption{Components of libxds}
        \label{libxds components}
    \end{center}
\end{figure}

The architecture of libxds is illustrated in figure~\ref{libxds
components}. libxds consists of three components: The generic encoding and
decoding framework, a set of formatting engines to encode and decode values
in a certain forman, and a run-time context, which is used to manage
buffers, registered engines, etc.

In order to use the library, the first thing the developer has to do is to
create a valid XDS context by calling {\sf xds\_init()}. The routine
requires one parameter that determines whether to operate in encoding- or
decoding mode. A context can be used for encoding or decoding only; it is
not possible to use the same context for both operations. Once a valid XDS
context has been obtained, the routine {\sf xds\_register()} can be used to
register an arbitrary number of formatting engines within the context.

A set of formatting engines has been included in the library. These
routines will handle any elementary datatype included in the ISO-C language
such as 32-bit integers, 64-bit integers, unsigned integers (of both 32-
and 64-bit), floating point numbers, strings and octet streams.

Once all required formatting engines are registered, the routines {\sf
xds\_encode()} or {\sf xds\_\-decode()} may be used to actually perform the
encoding or decoding process. Any data type for which a formatting engine
has been registered can be handled by the library.

This means, that it is possible for the developer to write custom
formatting engines for any data type he desires to use and to register them
in the context as long as these engines adhere to the {\sf xds\_engine\_t}
interface defined in {\sf xds.h}.

In particular it is possible to register meta formatting engines. That is a
formatting engine designed to encode or decode structures --- data types
which consist of several elementary data types. The formatting engine for
such a structure will simply re-use the existing engines in order to encode
or decode the whole structure. The clou here is that the meta engine
doesn't even need to know \emph{which} low-level formatting engines are
registered in order to use them. Hence, a meta engine may format the whole
structure in XDR, XML, or any other format without needing to know anything
about the details.

This topic is addressed in great detail in section~\ref{meta engines} of
this document, but before we come to that rather advanced topic, let us
start by studying two simple examples of how data is encoded and decoded
using libxds.

\section{Using the XDS library}

\subsection{Encoding}

The following example program will encode three variables using the XDR
formatting engines. The result of the process will then be written to the
standard output stream, which can be redirected to a file or piped into the
decoding program described in the next section. Just take a look at the
source code for a moment, we will then go on to discuss all relevant
sections line by line.

\begin{Verbatim}[numbers=left,fontsize=\small,frame=lines]
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <xds.h>

static void error_exit(int rc, const char* msg, ...)
    {
    va_list args;
    va_start(args, msg);
    vfprintf(stderr, msg, args);
    va_end(args);
    exit(rc);
    }

int main()
    {
    xds_t* xds;
    char*  buffer;
    size_t buffer_size;

    xds_int32_t  int32  = -42;
    xds_uint32_t uint32 = 0x12345678;
    const char*  string = "This is a test.";

    xds = xds_init(XDS_ENCODE);
    if (xds == NULL)
        error_exit(1, "Failed to initialize XDS context: %s\n", strerror(errno));

    if (xds_register(xds, "int32",  &xdr_encode_int32, NULL) != XDS_OK ||
        xds_register(xds, "uint32", &xdr_encode_uint32, NULL) != XDS_OK ||
        xds_register(xds, "string", &xdr_encode_string, NULL) != XDS_OK)
        error_exit(1, "Failed to register my encoding engines!\n");

    if (xds_encode(xds, "int32 uint32 string", int32, uint32, string) != XDS_OK)
        error_exit(1, "xds_encode() failed!\n");

    if (xds_getbuffer(xds, XDS_GIFT, (void**)&buffer, &buffer_size) != XDS_OK)
        error_exit(1, "getbuffer() failed.\n");

    xds_destroy(xds);

    write(STDOUT_FILENO, buffer, buffer_size);

    free(buffer);

    fprintf(stderr, "Encoded data:\n");
    fprintf(stderr, "\tint32   = %d\n", int32);
    fprintf(stderr, "\tuint32 = 0x%x\n", uint32);
    fprintf(stderr, "\tstring = \"%s\"\n", string);

    return 0;
    }
\end{Verbatim}

\paragraph{Lines 1--5.}
The program starts by including several system headers, which define the
prototypes for some routines we use. The most interesting header in our
case is of course {\sf xds.h} --- the header of libxds. Please note that
all declarations required to use libxds are included in that file.

\paragraph{Lines 7--13.}
The {\sf error\_exit()} routine is not relevant for the example; we just
define it to make the rest of the source code shorter and easier to read.

\paragraph{Lines 16--53.}
This is the interesting part: The {\sf main()} routine. This function will
create the variables to be encoded on the stack, assign values to them,
initialize the XDS library, use it to encode the values, and write the
result of the encoding process to the standard output stream. Read on for
further details.

\paragraph{Lines 26--28.}
First of all we have to obtain a XDS context for all further operation.
This is done by calling {\sf xds\_init()}. Since we intend to \emph{encode}
data, we initialize the context in encoding mode. The only other mode of
operation would be decoding mode, but this is demonstrated in the next
section.

All routines in libxds return a code from a small list of return codes
defined in {\sf xds.h}, but {\sf xds\_init()} is different: It will return
a pointer to an {\sf xds\_t} in case of success and {\sf NULL} in case of
failure. One reason why {\sf xds\_init()} would fail is because it can't
allocate the memory required to initialize the context. In this case, the
system variable {\sf errno} is set to {\sf ENOMEM}. Another reason why {\sf
xds\_init()} would fail is because the mode parameter is invalid, in which
case {\sf errno} woulde be set to {\sf EINVAL}. If libxds has been compiled
with assertions enabled, such an error would result in an assertion error,
terminating the program with a diagnostic message immediately.

\paragraph{Lines 30--33.}
Once we have obtained a valid XDS context, we register the formatting
engines we need. In this example, we'll encode a signed 32-bit integer, an
unsigned 32-bit integer, and a string. We'll be using XDR encoding in this
case, so the engines to register are {\sf xdr\_encode\_int32()}, {\sf
xdr\_encode\_uint32()}, and {\sf xdr\_encode\_string()}. (A complete list
of available formatting engines can be found in {\sf xds.h} or in
section~\ref{xdr}~and~\ref{xml}. Please note that we could switch the
deployed encoding format simply be using the corresponding {\sf
xml\_encode\_XXX()} engines here. We could even mix XDR and XML encoding as
we see fit but it's hard to think of a case where this would make sense.

As you can see in the code, the developer is free to choose a name he'd
like to register the engine under. These names may only contain
alphanumerical characters plus the hyphon (``\verb#-#'') and the underscore
(``\verb#_#''). You can choose any name you want, but it is recommended to
follow the naming scheme of the corresponding formatting engine. Why this
is recommended will be seen in section~\ref{meta engines}.

\paragraph{Lines 35--36.}
This is the place where the actual encoding takes place. As parameters,
{\sf xds\_encode()} requires a valid encoding context plus a format string
that describes how the following parameters are to be interpreted. While
the concept is obviously identical to {\sf sprintf()}, the syntax is
different. The format string may contain an arbitrary number of names,
which are delimited by an arbitrary number of any character that is not a
legal character for engine names. Thus you can delimit the names by colons,
blanks, or whatever you like.

For each valid engine name in the format string, a corresponding parameter
must follow. What these parameters mean depends on the engine you're using.
The engines provided with the libxds library will expect the value to
encode, but theoretically developers are free to write formatting engines
that expect virtually any kind of information here. More about this will
explained in section~\ref{meta engines}.

\paragraph{Lines 38--39.}
We have encoded all values we wanted to encode, now we can get the result
from the library. This happens by calling {\sf xds\_getbuffer()}. The
routine will store the buffer's address and length at the locations we
provided as parameters. Please note that we can choose whether we want the
buffer as a ``gift'' ({\sf XDS\_GIFT}) or as a ``loan'' ({\sf XDS\_LOAN}).

The buffer being a ``loan'' means that the buffer is still owned by the
library -- we're only allowed to peak at it. But any call to an libxds
routine may potentially modify the buffer or even change the buffers
location. Hence the result of a {\sf xds\_getbuffer()} call with loaning
semantics is only valid until the next libxds routine is called. After
that, it is invalid.

If we choose the gift semantics, the buffer we receive will be owned by us;
the library will not touch the buffer again. This means of course, that
we're responsible for {\sf free()}ing the buffer when we don't need it
anymore.

\paragraph{Line 41.}
Destroy the XDS context and all data associated with it. This is possible
because we requested the buffer as ``gift''; the buffer is not associated
with libxds anymore.

\paragraph{Line 43.}
Write the buffer with the encoded data to the standard output stream.

\paragraph{Line 45.}
Now that we don't need the buffer anymore, we have to return the memory it
uses to the system. libxds won't do that for us.

\paragraph{Lines 47--50.}
Write a short report of what we have done to the standard error channel.

\bigskip
Finally, let us compile and execute the example program shown above. For
convenience, it is included in the distribution under the name {\sf
docs/encode.c}. You can compile and execute the program as follows:

\begin{quote}
\begin{verbatim}
simons@dev13:~/libxds$ cd docs
simons@dev13:~/libxds/docs$ gcc -I.. encode.c -o encode -L.. -lxds
simons@dev13:~/libxds/docs$ ./encode >output
Encoded data:
        int32   = -42
        uint32 = 0x12345678
        string = "This is a test."
simons@dev13:~/libxds/docs$ ls -l output
-rw-r--r--  1 simons  simons  28 Aug  2 15:21 output
\end{verbatim}
\end{quote}

The result of executing the programm --- the file {\sf output} --- can be
displayed with {\sf hexdump(1)} or {\sf od(1)} and should look like this:

\begin{quote}
\begin{Verbatim}[fontsize=\small]
simons@dev13:~/libxds/docs$ hexdump -C output
00000000  ff ff ff d6 12 34 56 78  00 00 00 0f 54 68 69 73  |.....4Vx....This|
00000010  20 69 73 20 61 20 74 65  73 74 2e 00              | is a test..|
0000001c
\end{Verbatim}
\end{quote}

\noindent
We will also re-use this file in the next section, where we'll read it and
decode those values again.


\subsection{Decoding}

The following example program will read the result of the encoding example
shown in the previous section and decode the values back into the native
representation. Then it will print those values to the standard error
stream so that the user can see the values are correct. Please take a look
at the source now, we'll discuss all relevant details in the following
paragraphs.

\begin{Verbatim}[numbers=left,fontsize=\small,frame=lines]
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <xds.h>

static void error_exit(int rc, const char* msg, ...)
    {
    va_list args;
    va_start(args, msg);
    vfprintf(stderr, msg, args);
    va_end(args);
    exit(rc);
    }

int main()
    {
    xds_t* xds;
    char   buffer[1024];
    size_t buffer_len;
    int rc;

    xds_int32_t  int32;
    xds_uint32_t uint32;
    char*        string;

    buffer_len = 0;
    do
        {
        rc = read(STDIN_FILENO, buffer + buffer_len, sizeof(buffer) - buffer_len);
        if (rc < 0)
            error_exit(1, "read() failed: %s\n", strerror(errno));
        else if (rc > 0)
            buffer_len += rc;
        }
    while (rc > 0 && buffer_len < sizeof(buffer));

    if (buffer_len >= sizeof(buffer))
        error_exit(1, "Too much input data for our buffer.\n");

    xds = xds_init(XDS_DECODE);
    if (xds == NULL)
        error_exit(1, "Failed to initialize XDS context: %s\n", strerror(errno));

    if (xds_register(xds, "int32",  &xdr_decode_int32, NULL) != XDS_OK ||
        xds_register(xds, "uint32", &xdr_decode_uint32, NULL) != XDS_OK ||
        xds_register(xds, "string", &xdr_decode_string, NULL) != XDS_OK)
        error_exit(1, "Failed to register my decoding engines!\n");

    if (xds_setbuffer(xds, XDS_LOAN, buffer, buffer_len) != XDS_OK)
        error_exit(1, "setbuffer() failed.\n");

    if (xds_decode(xds, "int32 uint32 string", &int32, &uint32, &string) != XDS_OK)
        error_exit(1, "xds_decode() failed!\n");

    xds_destroy(xds);

    fprintf(stderr, "Decoded data:\n");
    fprintf(stderr, "\tint32   = %d\n", int32);
    fprintf(stderr, "\tuint32 = 0x%x\n", uint32);
    fprintf(stderr, "\tstring = \"%s\"\n", string);

    free(string);

    return 0;
    }
\end{Verbatim}

\paragraph{Lines 1--25.}
Include the required header files, define the {\sf error\_exit()} helper
function, and create the required variables on the stack.

\paragraph{Lines 27--39.}
These instructions will read an unspecified number of bytes from the
standard input stream --- as long as the input does not exceed the size of
the {\sf buffer} variable. In order to provide the program with the
apropriate input, redirect the standard input stream to the file {\sf
output} created in the previous section or connect the encoding and
decoding programs directly by a pipe.

\paragraph{Lines 41-43.}
Create a context for decoding the values. The semantics are identical to
those described in the previous section.

\paragraph{Lines 45--48.}
Register the decoding engines in the context. Please note that obviously
the decoding engines must correspond to the encoding engines used to create
the data we're about to process. Using, say, an XML engine to decode XDR
data will at best return with an error --- in the worst case, it will
return incorrect results!

\paragraph{Lines 50-51.}
Here we do not get a buffer from the library, we \emph{set} the buffer
we've read earlier in the context for decoding. Please note that we use
loan semantics in this case, not gift semantics. This is necessary because
{\sf buffer} has not been allocated by {\sf malloc()} --- the variable
lives on the stack. This means that we cannot give it to libxds because
libxds expects to be able to {\sf free()} the buffer when the context is
destroyed.

Loan semantics are fine, though, all we have to do is to take care that we
don't erase or modify the contents of {\sf buffer} while libxds operates on
it. The library itself will never touch the buffer in decode mode, no
matter whether loan or gift semantics have been chosen.

\paragraph{Lines 53--54.}
Here come the actual decoding of the buffer's contents using {\sf
xds\_decode()}. The syntax is identical to {\sf xds\_encode()}'s, the only
difference is that the decoding engines do not expect the values --- like
the encoding engines did --- but the location where to store the value.
Thus we pass the addresses of the apropriate variables here. If the routine
returns with {\sf XDS\_OK}, the decoded values will have been stored in
those locations.

It should be noted that the decoded string cannot trivially be returned
this way. Instead, {\sf xds\_decode()} will use {\sf malloc()} to allocate
a buffer barely large enough to hold the string. The address of that buffer
is then stored in the pointer {\sf string}. Of course this means that the
application has to {\sf free()} the string once it's not required anymore.

\paragraph{Line 56.}
We don't need the context anymore, so we destroy it and free all used
resources. This does not affect {\sf buffer} in any way because we used
loan semantics.

\paragraph{Lines 58-61.}
Print the decoded values to the standard error stream for the user to take
a look at them.

\paragraph{Line 63.}
Now that we don't need the contents of {\sf string} anymore, we must return
the buffer allocated in {\sf xds\_decode()} to the system.

\bigskip
Like the encoding program described earlier, the source code to this
program is included in the library distribution as {\sf docs/decode.c}. You
can compile and execute the program like this:

\begin{quote}
\begin{verbatim}
simons@dev13:~/libxds$ cd docs
simons@dev13:~/libxds/docs$ gcc -I.. decode.c -o decode -L.. -lxds
simons@dev13:~/libxds/docs$ ./decode <output
Decoded data:
        int32   = -42
        uint32 = 0x12345678
        string = "This is a test."
\end{verbatim}
\end{quote}

Of course we assume that the {\sf output} file has been created as
described in the previous section, otherwise you cannot trivially use the
example program. Alternatively, you could execute both programs like this:

\begin{quote}
\begin{Verbatim}[fontsize=\small]
simons@dev13:~/libxds/docs$ ./encode | ./decode
Encoded data:
        int32   = -42
        uint32 = 0x12345678
        string = "This is a test."
Decoded data:
        int32   = -42
        uint32 = 0x12345678
        string = "This is a test."
\end{Verbatim}
\end{quote}

\noindent
This will encode and decode the values without the need for a temporary
file.

\section{Extending the XDS library}
\label{meta engines}

\section{The XDS Framework}
\label{xds}

\subsection{xds\_t* xds\_init(xds\_mode\_t~\underline{mode});}

This routine creates and initializes a context for use with the XDS
library. The ``mode'' parameter may be either \textsf{XDS\_ENCODE} or
\textsf{XDS\_DECODE}, depending on whether you want to encode or to decode
data. If successful, xds\_init() returns a pointer to the XDS context
structure. In case of failure, though, xds\_init() will return
\textsf{NULL} and set \textsf{errno} to ENOMEM (failed to allocate internal
memory buffers) or EINVAL (``mode'' parameter was invalid).

A context obtained from xds\_init() should be destroyed by calling
xds\_destroy() when it is not needed any more.

\subsection{void xds\_destroy(xds\_t*~\underline{xds});}

xds\_destroy() will destroy an XDS context created by xds\_init(). Doing so
will return all resources associated with this context --- most notably the
memory used to buffer the results of encoding or decoding any values. A
context may not be used anymore after it has been destroyed.

\subsection{int xds\_register(xds\_t*~\underline{xds}, const~char*~\underline{name}, xds\_engine\_t~\underline{engine}, void*~\underline{engine\_context});}

This routine will register a formatting engine in the provided XDS context.
A ``formatting engine'' is potentially any function that fullfils the
following interface:

\begin{quote}
\begin{verbatim}
int engine(xds_t* xds, void* engine_context,
           void* buffer, size_t buffer_size, size_t* used_buffer_size,
           va_list* args);
\end{verbatim}
\end{quote}

By calling xds\_register(), the formatting engine ``engine'' will be
registered under the name ``name'' in the XDS context ``xds''. The last
parameter ``engine\_context'' may be specified as the user sees fit: It
will be passed when the engine is actually called and may be used to
implement an engine-specific context. Most engines will not need a context
of their own, in which case \textsf{NULL} should be used here.

Please note that until the user calls xds\_register() for an XDS context he
obtained from xds\_init(), no engines are registered for that context. Even
the engines included in the library distribution are not registered
automatically.

For engine names, any combination of the characters ``a--z'', ``A--Z'',
``0--9'', ``-'', and ``\_'' may be used; anything else is not a legal
engine name component.

xds\_register() may return the following return codes: \textsf{XDS\_OK}
	(everything went fine; the engine is registered now),
\textsf{XDS\_ERR\_INVALID\_ARG} (either ``xds'', ``name'', or ``engine''
	are \textsf{NULL} or ``name'' contains illegal characters for an engine
	name), or
\textsf{XDS\_ERR\_NO\_MEM} (failed to allocate internally required
	buffers).

\subsection{int xds\_unregister(xds\_t*~\underline{xds}, const~char*~\underline{name});}

xds\_unregister() will remove the formatting engine ``name'' from XDS
context ``xds''. The function will return \textsf{XDS\_OK} in case
everything went fine, \textsf{XDS\_ERR\_UNKNOWN\_ENGINE} in case the engine
``name'' is not registered in ``xds'', or \textsf{XDS\_ERR\_INVALID\_ARG}
if either ``xds'' or ``name'' are \textsf{NULL} or ``name'' contains
illegal characters for an engine name.

\subsection{int xds\_setbuffer(xds\_t*~\underline{xds}, xds\_scope\_t~\underline{flag}, void*~\underline{buffer}, size\_t~\underline{buffer\_len});}

\begin{figure}[tbh]
    \begin{center}
        \includegraphics[width=\textwidth]{setbuffer-logic.eps}
        \caption{xds\_setbuffer() modes of operation}
        \label{setbuffer logic}
    \end{center}
\end{figure}

This routine allows the user to control XDS' buffer handling: Calling it
will replace the buffer currently used in ``xds''. The address and size of
that buffer are passed to xds\_setbuffer() via the ``buffer'' and
``buffer\_len'' parameters. The ``xds'' parameter determines for which XDS
context the new buffer will be set. Furthermore, you can set ``flag'' to
either \textsf{XDS\_GIFT} or \textsf{XDS\_LOAN}.

A setting of \textsf{XDS\_GIFT} will tell libxds that the provided buffer
is now owned by the library and that it may be resized by calling
\textsf{realloc(3)}. Furthermore, the buffer is \textsf{free(3)}ed when
``xds'' is destroyed. If ``flag'' is \textsf{XDS\_GIFT} and ``buffer'' is
\textsf{NULL}, the xds\_setbuffer will simply allocate a buffer of its own
to be set in ``xds''. Please note that a buffer given to libxds as gift
\emph{must} have been allocated using \textsf{malloc(3)} --- it may not
life on the stack because libxds will try to free or to resize the buffer
as it sees fit.

Passing \textsf{XDS\_LOAN} via ``flag'' tells xds\_setbuffer() that the
buffer is owned by the application and that libxds should not free nor
resize the buffer in any case. In this mode, passing a buffer \textsf{NULL}
will result in an invalid-argument error.

\subsection{int xds\_getbuffer(xds\_t*~\underline{xds}, xds\_scope\_t~\underline{flag}, void**~\underline{buffer}, size\_t*~\underline{buffer\_len});}

This routine is the counterpart to xds\_setbuffer(): It will get the buffer
currently used in the XDS context ``xds''. The address of that buffer is
stored in the location ``buffer'' points to; the length of the buffer's
content will be stored in the location ``buffer\_len'' points to.

The ``flag'' argument may be set to either \textsf{XDS\_GIFT} or
\textsf{XDS\_LOAN}. The first setting means that the buffer is now owned by
the application and that libxds must not use it after this xds\_getbuffer()
call anymore; the library will instead allocate a new buffer for itself. Of
course this also means that the buffer will not be freed in xds\_destroy():
The application has to \textsf{free(3)} the buffer itself when it is not
needed anymore.

Setting ``flag'' to \textsf{XDS\_LOAN} tells libxds that the application
just wishes to peek into the buffer and will not modify it. The buffer is
still owned (and used) by libxds. Please note that the loaned address
returned by xds\_getbuffer() may become invalid change after any other
xds\_xxx() function call! If you need a reliable address, use
\textsf{XDS\_GIFT} mode.

The routine will return \textsf{XDS\_OK} (everything went fine) or
\textsf{XDS\_ERR\_INVALID\_ARG} (``xds'', ``buffer'' or ``buffer\_len'' are
\textsf{NULL} or ``flag'' is invalid) signifying success or failure
respectively.

Please note: It is perfectly legal for xds\_getbuffer() to return a buffer
of \textsf{NULL} and a buffer length of 0! This happens when
xds\_getbuffer() is called for an XDS context before a buffer has been
allocated.

\subsection{int xds\_vencode(xds\_t*~\underline{xds}, const~char*~\underline{fmt}, va\_list~\underline{args});}

This routine will encode one or several values using the apropriate
formatting engines registered in XDS context ``xds''. The parameter ``fmt''
contains a \textsf{sprintf(3)}-alike descriptions of the values to be
encoded; the actual values are provided in the varadic parameter ``args''.

The format for ``fmt'' is simple: Just provide the names of the engines to
be used for encode the apropriate value in ``args''. Any non-legal
engine-name character may be used as a delimiter. In order to encode two
32-bit integers followed by a 64-bit integer, the format string
\begin{quote}
\begin{verbatim}
int32 int32 int64
\end{verbatim}
\end{quote}
could be used. In case you don't like the blank, use the colon instead:
\begin{quote}
\begin{verbatim*}
int32:int32:int64
\end{verbatim*}
\end{quote}

Of course the names to be used here have to correspond to the names used to
register the formatting engines in ``xds'' earlier.

Every time xds\_vencode() is called, it will append the encoded data at the
end of the internal buffer stored in ``xds''. Thus, you can call
xds\_vencode() several times in order to encode several values, but you'll
still get all encoded values stored in one buffer. Calling xds\_setbuffer()
or xds\_getbuffer() at any point during the encoding will re-set the buffer
to the beginning. All values that have been encoded into that buffer
already will eventually be overwritten when xds\_encode() is called again.
Hence: Don't call xds\_setbuffer() or xds\_getbuffer() unless you actually
want to access the data stored in the buffer.

Also it should be noted that the data you have to provide for ``args''
depends entirely on what the deployed engines expect to find on the stack
--- there is no ``standard'' on what should be put on the stack here. The
XML and XDR engines included in the distribution will simply expect the
value to be encoded to be found on the stack, but other engines may act
differently. See section~\ref{meta engines} for an example of such an
engine.

xds\_vencode() will return any of the following return codes:
\textsf{XDS\_OK} (everything worked fine), \textsf{XDS\_ERR\_NO\_MEM}
(failed to allocate or to resize the internal buffer),
\textsf{XDS\_ERR\_OVER\-FLOW} (the internal buffer is too small but is not
owned by us), \textsf{XDS\_ERR\_INVALID\_ARG} (``xds'' or ``fmt'' are
\textsf{NULL}), \textsf{XDS\_ERR\_UNKNOWN\_ENGINE} (an engine name
specified in ``fmt'' is not registered in ``xds''),
\textsf{XDS\_ERR\_INVALID\_MODE} (``xds'' is initialized in decode mode),
or \textsf{XDS\_ERR\_UNKNOWN} (the formatting engine returned an
unspecified error).

\subsection{int xds\_encode(xds\_t*~\underline{xds}, const~char*~\underline{fmt}, \dots{});}

This routine is basically identical to xds\_vencode(), only that it uses a
different prototype syntax.

\subsection{int xds\_vdecode(xds\_t*~\underline{xds}, const~char*~\underline{fmt}, va\_list~\underline{args});}

This routine is almost identical to xds\_vencode(): It expects an XDS
context, a format string and a set of parameters for the formatting
engines, but xds\_vdecode() does not encode any data, it decodes the data
back into the native format. The format string again determines which
engines are to be called by the framework in order to decode the values
contained in the buffer. The native values will then be stored at the
locations found in the corresponding ``args'' entry. But please note that
the exact behavior of the decoding engines is not specified! The XML and
XDR engines included in this distribution expect a pointer to a location
where to store the decoded value, but other engines may vary.

xds\_vdecode() may return any of the following return codes:
\textsf{XDS\_OK} (everything went fine), \textsf{XDS\_ERR\_INVALID\_ARG}
(``xds'' or ``fmt'' are \textsf{NULL}), \textsf{XDS\_ERR\_TYPE\_MISMATCH}
(the format string says the next value is of type $A$, but that's not what
we found in the buffer), \textsf{XDS\_ERR\_UNKNOWN\_ENGINE} (an engine name
specified in ``fmt'' is not registered in ``xds''),
\textsf{XDS\_ERR\_INVALID\_MODE} (``xds'' has been initialized in encode
mode), \textsf{XDS\_ERR\_UNDER\-FLOW} (an engine tried to read $n$ byte from
the buffer, but we don't have that much data left), or
\textsf{XDS\_ERR\_UNKNOWN} (an engine returned an unspecified error).

\subsection{int xds\_decode(xds\_t*~\underline{xds}, const~char*~\underline{fmt}, \dots{});}

This routine is basically identical to xds\_vdecode(), only that it uses a
different prototype syntax.

\section{The XDR Engines}
\label{xdr}

\begin{tabular}{|c|c|c|c|} \hline
\bf Function Name     & \bf Expected ``args'' Data Type & \bf Input Size & \bf Output Size \\ \hline
xdr\_encode\_uint32()      & xds\_uint32\_t   & 4 byte   & 4 byte \\
xdr\_decode\_uint32()      & xds\_uint32\_t*  & 4 byte   & 4 byte \\[1ex]
xdr\_encode\_int32()       & xds\_int32\_t    & 4 byte   & 4 byte \\
xdr\_decode\_int32()       & xds\_int32\_t*   & 4 byte   & 4 byte \\[1ex]
xdr\_encode\_uint64()      & xds\_uint64\_t   & 4 byte   & 4 byte \\
xdr\_decode\_uint64()      & xds\_uint64\_t*  & 4 byte   & 4 byte \\[1ex]
xdr\_encode\_int64()       & xds\_int64\_t    & 4 byte   & 4 byte \\
xdr\_decode\_int64()       & xds\_int64\_t*   & 4 byte   & 4 byte \\[1ex]
xdr\_encode\_double()      & xds\_double\_t   & ?? byte  & ?? byte \\
xdr\_decode\_double()      & xds\_double\_t*  & ?? byte  & ?? byte \\[1ex]
xdr\_encode\_octetstream() & void*, size\_t   & variable & variable \\
xdr\_decode\_octetstream() & void**, size\_t* & variable & variable \\[1ex]
xdr\_encode\_string()      & char*            & variable & variable \\
xdr\_decode\_string()      & char**           & variable & variable \\ \hline
\end{tabular}

\section{The XML Engines}
\label{xml}

\subsection{Encoding engines}

\begin{tabular}{|c|c|c|c|} \hline
\bf Function Name     & \bf Expected ``args'' Data Type & \bf Input Size & \bf Output Size \\ \hline
xml\_encode\_uint32()      & xds\_uint32\_t   & ?? byte  & ?? byte \\
xml\_decode\_uint32()      & xds\_uint32\_t*  & ?? byte  & ?? byte \\[1ex]
xml\_encode\_int32()       & xds\_int32\_t    & ?? byte  & ?? byte \\
xml\_decode\_int32()       & xds\_int32\_t*   & ?? byte  & ?? byte \\[1ex]
xml\_encode\_uint64()      & xds\_uint64\_t   & ?? byte  & ?? byte \\
xml\_decode\_uint64()      & xds\_uint64\_t*  & ?? byte  & ?? byte \\[1ex]
xml\_encode\_int64()       & xds\_int64\_t    & ?? byte  & ?? byte \\
xml\_decode\_int64()       & xds\_int64\_t*   & ?? byte  & ?? byte \\[1ex]
xml\_encode\_double()      & xds\_double\_t   & ?? byte  & ?? byte \\
xml\_decode\_double()      & xds\_double\_t*  & ?? byte  & ?? byte \\[1ex]
xml\_encode\_octetstream() & void*, size\_t   & variable & variable \\
xml\_decode\_octetstream() & void**, size\_t* & variable & variable \\[1ex]
xml\_encode\_string()      & char*            & variable & variable \\
xml\_decode\_string()      & char**           & variable & variable \\ \hline
\end{tabular}

\newpage
\begin{thebibliography}{xxx}

\bibitem{xdr} RFC 1832: ``XDR: External Data Representation Standard'',
R.~Srinivasan, August~1995

\bibitem{xml} {\sf http://www.ossp.org/pkg/xds/xds-xml.dtd}

\end{thebibliography}

\end{document}

CVSTrac 2.0.1