abstract class BinData

Overview

Declarative reader/writer for structured binary data.

Subclass BinData, declare the wire layout with the field / bit_field / group / endian DSL, and the class learns how to (de)serialize itself from any IO:

class Packet < BinData
  endian big

  field size : UInt16, value: -> { payload.size }
  field payload : Bytes, length: -> { size }
end

packet = io.read_bytes(Packet) # decode
io.write_bytes(packet)         # encode

See #field for the supported field types and their options.

Direct Known Subclasses

Defined in:

bindata.cr
bindata/exceptions.cr

Constant Summary

BIT_PARTS = [{more => {UInt8, nil}, tag_number => {UInt8, nil}}, {tag_class => {UInt8, nil}, constructed => {UInt8, nil}, tag_number => {UInt8, nil}}, {long => {UInt8, nil}, length_indicator => {UInt8, nil}}] of Nil
CUSTOM_TYPES = [BER, ASN1::BER, ASN1::BER::ExtendedIdentifier, ASN1::BER::Identifier, ASN1::BER::Length] of BinData.class
INDEX = [2]
RESERVED_NAMES = ["inherited", "included", "extended", "method_missing", "method_added", "finished", "new", "inspect", "read", "write", "to_io", "from_io", "to_slice", "from_slice", "to_s", "bit_fields", "parent", "endian", "field", "bits", "enum_bits", "bool", "bit_field", "group", "remaining_bytes", "skip", "before_serialize", "after_deserialize", "custom", "enum_field", "array", "variable_array", "string", "bytes", "uint8", "uint8be", "uint8le", "int8", "int8be", "int8le", "uint16", "uint16be", "uint16le", "int16", "int16be", "int16le", "uint32", "uint32be", "uint32le", "int32", "int32be", "int32le", "uint64", "uint64be", "uint64le", "int64", "int64be", "int64le", "uint128", "uint128be", "uint128le", "int128", "int128be", "int128le", "float32", "float32be", "float32le", "float64", "float64be", "float64le"]

Names the per-type shortcut macro (generated in inherited) must never take, otherwise defining a subclass whose underscored name matches one of these would clobber a Crystal hook, a DSL macro, or a public method globally.

NOTE when adding a new DSL macro to this class, add its name here too, otherwise a subclass named after it will silently clobber it.

Class Method Summary

Macro Summary

Instance Method Summary

Class Method Detail

def self.bit_fields #

[View source]
def self.from_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian) #

Reads an instance from io (IO#read_bytes entry point). The type's declared endian is used; format is accepted but not applied.


[View source]
def self.from_slice(bytes : Slice, format : IO::ByteFormat = IO::ByteFormat::SystemEndian) #

Decodes an instance from a byte slice.

The declared endian of the type is used; the format argument is accepted for IO interoperability but does not override it.


[View source]

Macro Detail

macro __add_enum_field(name, cls, onlyif, verify, value, encoding, enum_type) #

this needs to be split out so we can resolve the enum base_type


[View source]
macro __build_methods__ #

[View source]
macro after_deserialize(&block) #

Registers a callback run on the instance just after it is read, e.g. to expose a friendlier representation of the raw fields.


[View source]
macro array(name, length, onlyif = nil, verify = nil, value = nil) #

DEPRECATED Use #field instead


[View source]
macro before_serialize(&block) #

Registers a callback run on the instance just before it is written, e.g. to derive raw fields from a friendlier representation.


[View source]
macro bit_field(onlyif = nil, verify = nil, endian = nil, &block) #

Groups bits / bool fields that are not byte-aligned. The total number of bits declared in the block must be divisible by 8. Use only when fields share a byte; byte-aligned values should be plain fields.

Bit fields follow the class endian: little byte-swaps the bitfield's bytes (the bitfield is read/written as a little-endian integer, fields taken from its most significant bit), while big / network / system / no declaration are big-endian. Pass endian: :little / :big to override a single bit field. Declare endian before the bit_field for the class default to apply.

Accepts the same onlyif / verify callbacks as field.


[View source]
macro bits(size, name, value = nil, default = nil) #

Declares a size-bit field inside a bit_field block (1 to 128 bits).

The accessor is typed as the smallest unsigned integer that holds size bits. A name : EnumType declaration exposes the value as that enum.

bit_field do
  bits 5, reserved
  bits 2, input : Inputs = Inputs::HDMI
end

[View source]
macro bool(name, default = false) #

Declares a single-bit boolean field inside a bit_field block.


[View source]
macro bytes(name, length, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro custom(name, onlyif = nil, verify = nil, value = nil) #

DEPRECATED Use #field instead


[View source]
macro endian(format) #

Sets the default byte order for every field of the type.

Accepts little, big, network (an alias for big-endian) or system. Individual fields may still override it with the field ..., endian: option.

class Header < BinData
  endian big
end

[View source]
macro enum_bits(size, name) #

DEPRECATED Use #bits instead


[View source]
macro enum_field(size, name, onlyif = nil, verify = nil, value = nil) #

DEPRECATED Use #field instead


[View source]
macro field(type_declaration, onlyif = nil, verify = nil, value = nil, length = nil, read_next = nil, encoding = nil, endian = nil) #

Declares a binary field from a type declaration (name : Type [= default]).

The supported field types are:

  • integers (UInt8..UInt128, Int8..Int128) and floats (Float32/Float64)
  • String — null-terminated, or fixed-size with length: (and optional encoding:)
  • Bytes — requires a length: callback
  • Enum types — require a default value
  • Array/Set — require length: (fixed) or read_next: (variable)
  • any other BinData / IO-serializable type (custom field)

Options (all accept a Proc, evaluated against the instance):

  • onlyif — read/write the field only when the callback returns true
  • verify — raise BinData::VerificationException unless the callback returns true
  • value — compute the field's value just before writing (e.g. a length/checksum)
  • length — element/byte count for sized Bytes/String/Array/Set
  • read_next — keep reading array elements while the callback returns true
  • encoding — string encoding for fixed-size String fields
  • endian — override the type's byte order for this field (numeric fields)
field size : UInt16, value: -> { text.bytesize }
field text : String, length: -> { size }

[View source]
macro float32(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro float32be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro float32le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro float64(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro float64be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro float64le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro group(name, onlyif = nil, verify = nil, value = nil, &block) #

Declares a nested, isolated group of fields as its own BinData class.

The group exposes a parent accessor for callbacks that need data from the enclosing type, and accepts the same onlyif / verify / value options as field. Useful for related or optional sub-structures.

group :header, onlyif: -> { start == 0xFF } do
  field version : UInt8
end

[View source]
macro int128(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int128be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int128le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int16(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int16be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int16le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int32(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int32be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int32le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int64(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int64be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int64le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int8(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int8be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro int8le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro remaining_bytes(name, onlyif = nil, verify = nil, default = nil) #

Reads every remaining byte of the IO (until EOF) into a Bytes field. Must be the last field. Works with any IO, including streaming ones (sockets, pipes). Accepts onlyif / verify callbacks.


[View source]
macro skip(length, onlyif = nil, verify = nil) #

Reads and discards length bytes (advancing the IO) without storing them, for sections you don't need to keep. There is no accessor. On write the region is emitted as length zero bytes, so the structure round-trips to the same size. Accepts onlyif / verify callbacks.

field section_size : UInt32
skip -> { section_size - 4 }

[View source]
macro string(name, onlyif = nil, verify = nil, length = nil, value = nil, encoding = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint128(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint128be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint128le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint16(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint16be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint16le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint32(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint32be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint32le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint64(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint64be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint64le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint8(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint8be(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro uint8le(name, onlyif = nil, verify = nil, value = nil, default = nil) #

DEPRECATED Use #field instead


[View source]
macro variable_array(name, read_next, onlyif = nil, verify = nil, value = nil) #

DEPRECATED Use #field instead


[View source]

Instance Method Detail

def __format__ : IO::ByteFormat #

[View source]
def read(io : IO) : IO #

Reads the fields of this instance from io, in declaration order, and returns io. Raises BinData::ParseError (or BinData::VerificationException) on malformed input.


[View source]
def to_io(io : IO, format : IO::ByteFormat = IO::ByteFormat::SystemEndian) #

Writes this instance to io (IO#write_bytes entry point). The type's declared endian is used; format is accepted but not applied.


[View source]
def to_s(io) #

[View source]
def to_slice #

Encodes this instance to a freshly allocated byte slice.


[View source]
def write(io : IO) #

Writes the fields of this instance to io in declaration order. Raises BinData::WriteError (or BinData::VerificationException) on failure.


[View source]