Kaitai Struct - Optional block or attribute

516 views Asked by At

My system has to be able to parse two types of very similar input data.

  • If the data comes from a queue it has the following structure:
record
record
...
record
  • If the data comes from a file it has the following structure:
header
record
record
...
record

My current code is as follows:

seq:
  - id: file_header
    type: file_header

  - id: record
    type: record
    repeat: eos

types:
  file_header:
    seq:
      - id: main_version
        type: u1
      - id: sub_version
        type: u1
      - id: spare
        type: str
        size: 30
        encoding: UTF-8

  record:
    seq:
      - id: event_id
        type: u2
        # enum: event_types
      - id: event_length
        type: u4
      - id: enb_id
        type: u4
      - id: cell_id
        type: u1
      - id: call_id
        type: u4
      - id: date_time
        type: date_time_record
      - id: spare
        type: str
        size: 2
        encoding: UTF-8
      - id: crnti
        type: u2
      - id: body
        size: event_length - 21

My idea is to create only one .ksy file that works for both approaches. How can I get it? It would basically be making file_header optional, but I don't see a way to do it. Can somebody please help me on this?

1

There are 1 answers

1
Petr Pucil On

Affiliate disclaimer: I'm a Kaitai Struct maintainer (see my GitHub profile).

My idea is to create only one .ksy file that works for both approaches. How can I get it?

You can define a boolean parameter is_file on the top-level type and pass true when the data comes from a file, otherwise false. Like this:

params:
  - id: is_file
    type: bool
seq:
  - id: file_header
    type: file_header
    if: is_file

  - id: record
    type: record
    repeat: eos
types:
  # ...

Note that the is_file param is mandatory, and you won't be able to instantiate the class without passing a value in it. For that reason, the fromFile(…​) helper method will no longer be available, and you'll need to create the parser object normally using new (or its closest equivalent in your target language).

I don't know what language you're targeting, but in C++, C#, Lua, PHP and Python come the custom params first (before _io, _root and _parent) and in Java, JavaScript and Nim second. For example, in Python you would do:

from kaitaistruct import KaitaiStream
from io import BytesIO

data = b"\x00\x01\x02..."
data_come_from_file = True
f = YourFormat(data_comes_from_file, KaitaiStream(BytesIO(data)))

In Java, for instance:

import io.kaitai.struct.ByteBufferKaitaiStream;

byte[] data = new byte[] { 0x00, 0x01, 0x02, ... };
boolean dataComeFromFile = true;
f = new YourFormat(new ByteBufferKaitaiStream(data), dataComesFromFile)

Check the generated code if unsure.