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 (tag0x07) - Lists:
{:list, [...]}- instruction argument lists (tag0x17) - Float registers:
{:fr, N}- float arithmetic registers (tag0x27) - Allocation lists:
{:alloc, [...]}- allocation info (tag0x37) - Extended literals:
{:literal, N}- large literal indices (tag0x47) - Typed registers:
{:tr, reg, type}- type-annotated registers (tag0x57)
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
- The BEAM Book: https://blog.stenmans.org/theBeamBook/
- OTP source:
erts/emulator/beam/beam_load.c - OTP source:
lib/compiler/src/beam_asm.erl
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
@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.
@type decode_result() :: {compact_term(), binary()}
@type encode_result() :: binary()
Functions
@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>>}
@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}]
@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>>
Check if encode(decode(binary)) == binary for a single term.
Useful for testing roundtrip correctness.
Example
iex> CTF.roundtrip?(<<0x03>>)
true