FIX.Parser (fix_message v0.1.0)

Copy Markdown

A recursive binary-matching FIX parser.

Provides two layers:

  • frame/1 and parse_message/2 — message framing for a byte stream: validates the BeginString(8)/BodyLength(9) structure and the CheckSum(10) trailer, and splits complete messages off the buffer.

  • parse/2 — tokenizes a complete message of tag=value<SOH> fields into {header, body, trailer} sections. Length-prefixed data fields (RawData, EncodedText, ...) are sliced byte-exactly, so their values may contain the SOH delimiter.

Which tags are length-prefixed data fields, and which belong to the standard header and trailer, is determined by a FIX.Dictionary, defaulting to FIX.Dictionary.FIX44. Pass a custom dictionary to handle counterparty-defined data fields.

Summary

Functions

Splits one complete FIX message off the front of buffer.

Tokenizes FIX fields from binary into {header, body, trailer} sections.

Frames and tokenizes one complete FIX message off the front of buffer.

Types

fields()

@type fields() :: FIX.Message.fields()

frame_error()

@type frame_error() :: :garbled | :checksum_mismatch

sections()

@type sections() :: {header :: fields(), body :: fields(), trailer :: fields()}

Functions

frame(buffer)

@spec frame(binary()) ::
  {:ok, message :: binary(), rest :: binary()}
  | :incomplete
  | {:error, frame_error()}

Splits one complete FIX message off the front of buffer.

Validates that the message starts with BeginString(8) followed by BodyLength(9), that the full body and trailer are present, and that CheckSum(10) matches. The BeginString value itself is not restricted to a particular FIX version.

Returns:

  • {:ok, message, rest}message is the complete framed message (a sub-binary of buffer) and rest is whatever follows it.
  • :incompletebuffer is a valid prefix of a message; read more bytes.
  • {:error, :garbled}buffer cannot be the start of a valid message.
  • {:error, :checksum_mismatch} — well-framed, but CheckSum(10) is wrong.

parse(binary, dictionary \\ FIX.Dictionary.FIX44)

@spec parse(binary(), module()) :: {:ok, sections()} | {:error, :garbled}

Tokenizes FIX fields from binary into {header, body, trailer} sections.

Returns {:ok, {header, body, trailer}} when the entire input parses, where each section is a list of {tag, value} tuples in wire order (tags are integers, values are binaries), or {:error, :garbled} if any part of the input cannot be consumed.

The split is positional and forward-only, driven by the dictionary's header_tag?/1 and trailer_tag?/1: fields accumulate into the header until the first non-header tag, then into the body until the first trailer tag, then into the trailer. A header tag appearing after the body has started stays in the body. Concatenating the sections reconstructs the full field stream.

Data fields are length-prefixed by their companion field (e.g. RawDataLength(95) precedes RawData(96)) and are sliced by the declared byte count, so their values may contain SOH. A length field not followed by its companion data field with exactly the declared number of bytes is garbled. Both halves of a pair land in the section chosen by the length tag. The companion pairs are defined by dictionary (see FIX.Dictionary).

parse_message(buffer, dictionary \\ FIX.Dictionary.FIX44)

@spec parse_message(binary(), module()) ::
  {:ok, sections(), rest :: binary()} | :incomplete | {:error, frame_error()}

Frames and tokenizes one complete FIX message off the front of buffer.

Combines frame/1 and parse/1: returns {:ok, sections, rest} for a complete, checksum-valid message whose fields all parsed, where sections is the {header, body, trailer} split (see parse/2) and rest is the remainder of the buffer (any pipelined messages after this one).

Returns :incomplete when more bytes are needed, and {:error, reason} otherwise. A message that frames correctly but contains unparseable fields is {:error, :garbled}.