oais-structure-kaitai notes

This module bridges Kaitai Struct onto the common StructureNode model. It uses the Kaitai Struct Java runtime io.kaitai:kaitai-struct-runtime:0.11 (MIT licence, from Maven Central), and was checked against classes generated by the Kaitai Struct compiler 0.11.

How a format is described

A Kaitai Struct description is a .ksy file (YAML): a seq of fields, each with an id and a type (s4, u1, f8, str, a user-defined type, ...), plus size, encoding, terminator, repeat (eos, expr, until), if, switch-on for variant records, computed instances, and endian at the top level. It is built for reverse-engineering arbitrary binary formats; the public format gallery (https://formats.kaitai.io) has hundreds of real specifications. See src/main/ksy for this module's two examples: a binary "point" record and a CSV file.

Unlike DFDL and DRB, a .ksy is not read at run time. The Kaitai Struct compiler (ksc) translates it ahead of time into source code - here, a Java class - and that class is what parses the bytes. So:

What Kaitai Struct is used for

Beyond this project's two small examples, Kaitai Struct is mostly used to describe and reverse-engineer existing binary formats. The gallery's specifications include:

Any of the gallery's .ksy files can be compiled and used with this adapter in the same way as the examples here.

Galleries of examples

Generating the classes

After editing a .ksy, regenerate with the Kaitai Struct compiler (0.11, https://kaitai.io), from src/main/ksy:

kaitai-struct-compiler -t java --java-package info.oais.infomodel.structure.kaitai.generated --debug --outdir ../java point2d.ksy csv_points.ksy

--debug is what makes the generated classes record where each field was read from, which this adapter turns into byte positions (see below). Leave it out and everything else still works, just without positions.

How the adapter works

Writing data back

KaitaiStructureRepInfo is a WritableStructureRepInfo, for classes compiled with Kaitai Struct's read-write mode (kaitai-struct-compiler -w, Java and Python only, 0.11 and later); a read-only class is refused with that explanation. write(dataObject, changes) reads the data, fetches the lazily read instances, sets the changed values through the generated setters (path steps name fields as in the .ksy, sample_count, or as the accessor does, sampleCount; [n] picks an element of a repeated field), has each changed object check itself (_check(), which refuses e.g. a repeat count that no longer matches its list, or a string that no longer fits its size), and writes everything with _write. Kaitai writes into a stream of a fixed size, and an element repeated or sized to the end of the data must end exactly where the stream does, so the writer starts from the data's own size and adjusts it by what Kaitai reports. Unused bytes inside a type of stated size are written as zeros.

Known limitations