6. Run-Time Library

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.

6.1 Bitstream Class

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.

Constructors
Bitstream(const char *filename, Bitstream_t mode);
Create a bistream using the file name 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);
Create a bistream using the file descriptor 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.
Public Methods
int getbits(int n);
Reads n bits from the bitstream and returns them as an integer. Only supported for input bitstreams.
 
int nextbits(int n);
Obtains the next 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);
Output the unsigned integer value bits using n bits to the bitstream. Returns the value of bits. Only supported for output bitstreams.
 
int skipbits(int n);
Skip the next n bits from the bitstream. Only supported for input bitstreams.
 
int align(int n);
Aligns a bitstream to the closest following bitstream position that is a multiple of 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);
This is the equivalent of 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);
This is the equivalent of 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);
This is the equivalent of getbits, nextbits, and putbits, for long double quantities. In this implementation, long doubles are assumed to be 64 bits.
 
int getpos(void);
Returns the current position of a bitstream. Supported in both input and output bitstreams.
 
int atend(void);
Returns non-zero value of the input bitstream has reached and end-of-file condition. Not supported for output bitstreams.
 
int geterror(void);
Return the last error recorded by the bitstream. See Error Handling below for more information on the return values.
 
char* const geterror(void);
Return a string describing the last error recorded by the bitstream. See Error Handling below for more information on the return values.
Public Member Variables

None.

Error Handling

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_NONE
No error has been detected.
 
E_END_OF_DATA
An end-of-file condition has been detected.
 
E_INVALID_ALIGNMENT
An invalid alignment argument (not a multiple of 8) has been given to the align() method.
 
E_READ_FAILED
A reading operation on the input file has failed.
 
E_WRITE_FAILED
An output operation on the output file has failed.

A 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.

Error
This is the base exception type. You can use the methods geterror() and getmsg() to identify the particular error signalled. These methods can be used on both the exception object or the original Bitstream object.
 
EndOfData
An end-of-date condition has been detected (E_END_OF_DATA).
 
InvalidAlignment
An invalid argument (not a multiple of 8) has been given to the align() method (E_INVALID_ALIGNMENT).
 
ReadFailed
A reading operation on the input file has failed (E_READ_FAILED).
 
WriteFailed
An output operation on the output file has failed (E_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.

6.2 flerror Function

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.

6.3 trace Function

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.

6.4 Rebuilding the Library

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.

UNIX Platforms

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

Win32 Platforms

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.