Structured-data processing
The configuration concept (Chapter System configuration and Section Component configuration) of the framework relies on Genode's custom human-inclined data (HID) text format.
The parsing and generation of HID-formatted data is covered by the Node and Generator classes respectively. The Node utility operates directly on a text buffer that contains formatted data. There is no conversion step into an internal representation but the data is parsed on access, which alleviates the need for dynamic memory allocations.
Vice versa, the Generator utility covers the generating of HID-formatted data. The scope of a node is represented by a lambda function. Nested nodes are expressed by nested lambda functions. The nesting thereby mirrors the structure of the generated data. The Generator does not use an internal intermediate representation of the data but writes formatted text directly into the result buffer. The generation of HID-formatted output does not involve dynamic memory allocations.
A typical component imports parts of its internal state from HID-formatted input, most prominently its configuration. This import is not a one-off operation but may occur multiple times during the lifetime of the component. This raises the challenge of updating the component's internal data model from potentially changing input. The List_model provides a convenient and robust formalism to implement such partial model updates.
Parsing data
Genode's parser for structured data is provided by the Node class. Its primary use case is the provisioning of configuration information to low-level components. Consequently, it takes the following considerations into account:
- Low complexity
-
Because the parser is implicitly used in most components, it must not be complex to keep its footprint on the trusted computing base as small as possible.
- Free-standing
-
The parser must be able to operate without external dependencies such as a C runtime. Otherwise, each Genode-based system would inherit such dependencies.
- No dynamic memory allocations
-
The parser should not dynamically allocate memory to be usable in resource multiplexers and runtime environments where no anonymous memory allocations are allowed (Section Component-local heap partitioning).
- Robustness
-
When confronted with potentially malicious input, the parser must stay void of memory-safety risks, infinite loops, stack exhaustion, and other liveliness issues.
Other possible goals like expressive error messages, the support for more general use cases, and the adherence to standards are deliberately subordinated. The parser does not try to accommodate advanced data processing such as the validation of data against a schema, or mutating nodes in a DOM tree.
Generating data
Feeding data models
List-based data model created and updated from HID
The List_model defined at base/include/util/list_model.h stores a component-internal representation of HID-formatted content. It keeps sub nodes of a matching type in an internal list of C++ objects of type ELEM. An ELEM type carries two methods matches and type_matches, which define the relation of the elements to HID nodes. E.g.,
struct Item : List_model<Item>::Element
{
static bool type_matches(Node const &);
bool matches(Node const &) const;
...
};
The class function type_matches returns true if the specified HID node matches the Item type. It can thereby be used to control the creation of ELEM nodes by responding to specific node types while ignoring unrelated nodes.
The matches method returns true if the concrete element instance matches the given HID node. It is used to correlate existing ELEM objects with new versions of nodes to update the ELEM objects.
The functor arguments create_fn, destroy_fn, and update_fn for the update_from_node method define how objects are created, destructed, and updated. E.g.,
_list_model.update_from_node(node,
[&] (Node const &node) -> Item & {
return *new (alloc) Item(node); },
[&] (Item &item) { destroy(alloc, &item); },
[&] (Item &item, Node const &node) { item.update(node); }
);
The elements are ordered according to the order of HID nodes.
The list model is a container owning the elements. Before destructing a list model, its elements must be removed by calling update_from_node with an empty Node() as argument, which prompts the call of destroy_fn for each element.
Buffering
In some situations, it is convenient to keep a verbatim copy of HID-formatted input as part of the internal data model. As the Node is merely a light-weight utility operating on an ephemeral buffer, it does not capture a copy of the processed data. The Buffered_node utility bridges this gap by combining the Node with a memory allocator to keep a copy of the input in a dynamically allocated buffer.