The run-time library provides low-level bitstream I/O facilities, as well as functions for reporting bitstream syntax errors and outputting trace information. Although the Flavor translator could directly output the required code, using a thin library provides significant benefits in terms of flexibility. For example, it is impossible to anticipate all possible I/O structures that might be needed by applications (network-based, multi-threaded, multiple buffers, etc.). Attempting to provide a universal solution would be futile. By separating this layer from the core translator, programmers have the option of replacing parts of, or the entire layer with their own code. The only requirement is that this custom code provides an identical interface to the one needed by the translator.
We provide a complete run-time library that supports file and file descriptor-based input and output, as well as error reporting and tracing functions. As the source code for the library is included in its entirety, customization can be performed quite easily. Note that the library is just about 600 lines of code, demonstrating the simplicity of the interface. We also provide information on how to rebuild the library, if needed.
In the following, we document the components of the run-time library. Note that only
part of each component is used by the translator. For example, the constructor for the
bitstream I/O class is irrelevant for the translated code, but of course an implementation
is required in order to have a fully functional class. We thus indicate interfaces
required by the translator in a red color, while the rest are
shown in a green color. These interfaces need to be provided
by any custom code that interfaces to the flavorc-generated code.
The Bitstream class provides basic bitstream I/O facilities, in terms of reading or writing bits. It also provides buffering, and construction using either a file name or a file descriptor.
Note that this class only supports bistream I/O for quantities of length up to 32 bits
(except for double or long double quantities which have length
64 bits). This ensures that all bitstream manipulation is performed using integers, and is
thus very fast.
For error reporting, two modes are supported: exception based, and traditional error reporting. In the former case, C++ exceptions are defined and thrown to indicate error conditions. In the latter case, it is the responsibility of the user to inquire about the current error status. The exception-based scheme is used by default in Win32 distributions. For UNIX distributions, the traditional scheme is employed by default as almost all C++ compiler vendors do not yet support C++ exception handling.
The error reporting mode is controlled by the defined constant USE_EXCEPTION,
which can be found in the port.h file of the library source code.
Bitstream(const char *filename, Bitstream_t mode);filename. mode
determines if it is an input (BS_INPUT) or output (BS_OUTPUT)
bitstream. A Bitstream object cannot simultaneously support input and output
modes. If the constructor fails, an error message will be printed to stderr
and the program will terminate.Bitstream(int fd, Bitstream_t mode);fd. mode
determines if it is an input (BS_INPUT) or output (BS_OUTPUT)
bitstream. A Bitstream object cannot simultaneously support input and output
modes. If the constructor fails, an error message will be printed to stderr
and the program will terminate.int getbits(int n);n bits from the bitstream and returns them as an integer. Only
supported for input bitstreams.int nextbits(int n);n bits from the bitstream and returns them as an integer,
but does not advance the bitstream pointer. Only supported for input bitstreams.int putbits(unsigned int bits, int n);bits using n bits to the
bitstream. Returns the value of bits. Only supported for output bitstreams.int skipbits(int n);n bits from the bitstream. Only supported for input
bitstreams.int align(int n);n.
Note that n must be a multiple of 8. Supported in both input and output
bitstreams.float getfloat(void);
float nextfloat(void);
float putfloat(void);getbits, nextbits, and putbits,
for float quantities. In this implementation, floats are assumed to be 32
bits.double getdouble(void);
double nextdouble(void);
double putdouble(void);getbits, nextbits, and putbits,
for double quantities. In this implementation, doubles are assumed to be 64
bits.long double getldouble(void);
long double nextldouble(void);
long double putldouble(void);getbits, nextbits, and putbits,
for long double quantities. In this implementation, long doubles are assumed
to be 64 bits.int getpos(void);int atend(void);int geterror(void);char* const geterror(void);None.
A bitstream maintains as state information the value of the last error message
detected. This information is described by an Error_t
enumeration. The following values are defined.
E_NONEE_END_OF_DATAE_INVALID_ALIGNMENTalign()
method.E_READ_FAILEDE_WRITE_FAILEDA user can query the value of the last error recorded using the geterror()
method. A text message describing the error can be obtained using the getmsg()
method.
In addition to the above interface, the Bitstream class can also support
C++ exceptions for error handling. Support for exceptions is controlled by the USE_EXCEPTION
defined constant, which can be found in the file port.h of the source code
distribution. By default, only the Win32 distribution supports exceptions as almost all
UNIX C++ compilers do not yet support them. If your UNIX compiler is an exception, you can
activate this feature (by editing port.h in src/lib) and rebuilding the run-time library.
The following exception classes are defined. All support the geterror()
and getmsg() interfaces described above.
Errorgeterror() and getmsg()
to identify the particular error signalled. These methods can be used on both the
exception object or the original Bitstream object.EndOfDataE_END_OF_DATA).InvalidAlignmentalign()
method (E_INVALID_ALIGNMENT).ReadFailedE_READ_FAILED).WriteFailedE_WRITE_FAILED).Exceptions is the preferred mechanism for error handling as the traditional error reporting mechanism will delay error reporting to the user's code.
The flerror function is used by the translator to report bitstream syntax errors. Currently, these errors refer to parsable variables with expected values that do not match the value that was read from the bitstream. The function is declared as follows.
void flerror(char* fmt, ...)
The ellipses (...) at the end of the declaration indicate a variable
number of arguments. The fmt argument is a string containing formatting
information similarly to a printf() statement.
If an alternate implementation is provided in the user's code, the version present in
the library will be ignored by the linker. The user can also change the name of the
function using the -E command line option of the translator. The interface,
however, must be the same.
In order to produce bitstream traces, the translator inserts calls to a set of trace()
functions in the get() method. Two such functions are used; one for tracing
quantities that are compatible with integer types, and one for quantities that are
compatible with double types.
Here are the declarations used in the code generated by the translator.
void trace(int pos, int size, unsigned int val, char* fmt,
...);
void trace(int pos, int size, double val, char* fmt, ...);
The first argument is the position of the first bit of the traced quantity in the
bitstream. The second argument is the length of the quantity in bits. Next, we have the
actual value, either as an integer or a double. Finally, the fmt argument is
a string containing a text description of the traced quantity, including formatting
information similarly to a printf() statement. The ellipses (...)
indicate a variable number of arguments, which describes potential additional values
needed by the fmt description.
You will need to rebuild the library if you make any changes, or if you want to use a different C++ compiler than the one used to prepare this distribution. This is necessary because of different name mangling algorithms used by different C++ compiler implementations. For Win32 distributions, the Microsoft Visual C++ Version 5.0 compiler is used, while for UNIX distributions we use the GNU C++ compiler.
Make sure that you first run the configure script in the top directory of
the distribution. You should then move to the src subdirectory, and run make.
The library will be built, and both library and include files will be automatically copied
to their installation directories. Here are the commands.
$ cd flavor-root-directory
$ ./configure
$ cd src
$ make
There are two ways to build the library in Win32 platforms, depending on if you use command-line or visual tools. If you use command-line tools, go to the library source code in the directory src/lib of the distribution, and type:
nmake -f lib.mak CFG="lib - Win32 Release"
This will compile the library and install both library and include files in their installation directories.
If you use the Microsoft Developer Studio environment, load either the flavor.dsw
workshop file (located in the root directory) and select the lib project, or
load directly the project file src\lib\lib.dsp. Select "Win 32 -
Release" as the active project configuration and then build the project. This will
compile the library and install both library and include files in their installation
directories.