Encode and decode BEAM compact term format.

The compact term format is used in the Code chunk of BEAM files to efficiently encode instruction arguments. This module provides functions to decode these bytes into Elixir terms and encode terms back to bytes.

Supported Features

  • Tags 0-6: Literal, Integer, Atom, X-reg, Y-reg, Label, Character
  • Small values (0-15): Single-byte encoding
  • Medium values (16-2047): Two-byte encoding
  • Large values (2048+): Multi-byte encoding
  • Negative integers: Two's complement encoding with sign extension
  • Extended tag 7: Extended formats (see below)

Extended Formats

Extended formats (tag 7) provide additional encoding capabilities:

  • Float literals: {:float, F} - 8-byte IEEE 754 double (tag 0x07)
  • Lists: {:list, [...]} - instruction argument lists (tag 0x17)
  • Float registers: {:fr, N} - float arithmetic registers (tag 0x27)
  • Allocation lists: {:alloc, [...]} - allocation info (tag 0x37)
  • Extended literals: {:literal, N} - large literal indices (tag 0x47)
  • Typed registers: {:tr, reg, type} - type-annotated registers (tag 0x57)

Format Reference

Each encoded argument starts with a tag byte:

Bits: VVVV_0TTT  (small value, 1 byte total)
      VVVVV_1TTT (medium value, 2 bytes total)

Tags:

  • 0: Literal table index
  • 1: Integer
  • 2: Atom table index
  • 3: X register
  • 4: Y register
  • 5: Label (jump target)
  • 6: Character (unicode codepoint)
  • 7: Extended format

Examples

iex> CTF.decode(<<0x03>>)
{{:x, 0}, <<>>}

iex> CTF.encode({:x, 5})
<<0x53>>

iex> CTF.decode_all(<<0x03, 0x04, 0x05>>)
[{:x, 0}, {:y, 0}, {:f, 0}]

References

Summary

Functions

Decode a single compact term from binary.

Decode all terms from a binary until exhausted.

Encode an Elixir term to compact binary format.

Check if encode(decode(binary)) == binary for a single term.

Types

compact_term()

@type compact_term() ::
  {:x, non_neg_integer()}
  | {:y, non_neg_integer()}
  | {:f, non_neg_integer()}
  | {:atom, non_neg_integer()}
  | {:literal, non_neg_integer()}
  | {:integer, integer()}
  | {:char, non_neg_integer()}
  | {:float, float()}
  | {:fr, non_neg_integer()}
  | {:tr, compact_term(), non_neg_integer()}
  | {:list, [compact_term()]}
  | {:alloc, [{compact_term(), compact_term()}]}
  | {:extended, byte(), non_neg_integer()}

A decoded compact term.

decode_result()

@type decode_result() :: {compact_term(), binary()}

encode_result()

@type encode_result() :: binary()

Functions

decode(arg)

@spec decode(binary()) :: decode_result()

Decode a single compact term from binary.

Returns {decoded_term, remaining_binary}.

Examples

iex> CTF.decode(<<0x03, 0xFF>>)
{{:x, 0}, <<0xFF>>}

iex> CTF.decode(<<0x53, 0xAB>>)
{{:x, 5}, <<0xAB>>}

decode_all(binary)

@spec decode_all(binary()) :: [compact_term()]

Decode all terms from a binary until exhausted.

Returns a list of decoded terms.

Example

iex> CTF.decode_all(<<0x03, 0x04, 0x05>>)
[{:x, 0}, {:y, 0}, {:f, 0}]

encode(arg1)

@spec encode(compact_term()) :: encode_result()

Encode an Elixir term to compact binary format.

Examples

iex> CTF.encode({:x, 0})
<<0x03>>

iex> CTF.encode({:x, 5})
<<0x53>>

iex> CTF.encode({:x, 100})
<<0x0B, 0x64>>

roundtrip?(binary)

@spec roundtrip?(binary()) :: boolean()

Check if encode(decode(binary)) == binary for a single term.

Useful for testing roundtrip correctness.

Example

iex> CTF.roundtrip?(<<0x03>>)
true