Bithenge Library
This page gives an overview of the Bithenge library API and internals. Detailed documentation can be found in the source code’s Doxygen comments.
Conventions
Names
All public functions and types have names starting with bithenge_
.
Error handling
Almost all Bithenge functions return an integer error code. It will be EOK on
success or an error code from errno.h
on failure. Even if an error occurs,
functions will still free or dereference their arguments as documented.
Reference counting
Nodes, expressions, transforms, and scopes use reference counting. For
instance, functions that produce a node (through a bithenge_node_t **
parameter) create a new reference to the node; you are responsible for ensuring
the reference count is eventually decremented. The reference count can be
incremented with bithenge_xxx_inc_ref
and decremented with
bithenge_xxx_dec_ref
.
If a function’s documentation says it “takes [ownership of] a reference” to an object, the function guarantees the object’s reference count will eventually be decremented, even if an error occurs. Therefore, if you create an object only to immediately pass it to such a function, you do not need to change its reference count.
Polymorphism
Blob nodes, internal nodes, expressions, and transforms are polymorphic. We
will use transforms as an example, but the others are similar. Each transform
implementation has its own struct
, including a bithenge_transform_t
member,
and its own static bithenge_transform_ops_t
instance. When a transform is
created, it calls bithenge_transform_init
on the bithenge_transform_t
and
gives it a pointer to the bithenge_transform_ops_t
instance. It then returns
a pointer to the bithenge_transform_t
to the caller. When the caller uses a
function on the bithenge_transform_t *
, it automatically looks in the
bithenge_transform_ops_t
for the transform‐specific implementation.
Main types
Nodes
Integer, boolean, and string nodes are trivial. Blob and internal nodes are polymorphic; each node has its own functions to access its contents. This means calculating the node’s contents can be delayed until the contents are needed.
Transforms
The primary method of transforms is apply
, which applies a transform to an
input tree and creates an output tree. When a transform takes a blob node as
input, it is sometimes necessary to determine the prefix of a given blob that
can be used as input to the transform; the method prefix_length
can be used
for this. Alternatively, a prefix_apply
method can do both at once,
substituting for or supplementing apply
and prefix_length
. All three of
these methods take a scope; see below.
Expressions
The only method of expressions is evaluate
, which evaluates the expression in
a scope to create an output node. Expressions can be considered similar to
transforms, except that they have no input node.
Scopes
Scopes keep track of all information needed by transforms and expressions other than transforms’ input trees. This includes parameters, nodes being created, input nodes, and error messages.
Main functions
Aside from functions directly related to the types above, Bithenge has several other important functions:
bithenge_parse_script
parses a Bithenge script file to create a transform.bithenge_node_from_source
creates a node based on a string, such asblock:bd/initrd
.bithenge_print_node
prints nodes in human‐readable formats.
The test.c program included with Bithenge is a simple demonstration of all of these functions.
Testing
In HelenOS, Bithenge can be tested by running batch test.bdsh
in the
/src/bithenge
directory. If it runs to the end and prints "Success!",
Bithenge worked correctly on the example files.
In Linux, more advanced testing is possible. Compile with
COVERAGE=y FAILURE=y make
and run test.sh
with Valgrind installed. Bithenge
will use failure injection to test much of the error handling code, in addition
to the main paths.