Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Codecs

A codec is the decoder half of a node: every standard OpenZL transform or coder has a node (the encoder side, a StandardNode) and a decoder in this crate, and both live behind one Cargo feature. This page lists them. Wire formats, invariants and the deviations on malformed data are in decoder codecs; how nodes are wired into graphs is in graphs.

Types are serial (bytes), struct (fixed-width records), numeric (little-endian integers) and string. “Since” is the first format version that has the node. The crate writes versions 21 to 27, so a node with a “since” of 21 or less exists at every version you can select; nodes introduced at 23, 24, 26 or 27 (lz4, lz, the bit-split family, partition, sentinel, sparse_num, pivco_huffman) are not available when you pick an older format version.

Features

all-codecs (in default) enables every feature below. Without a codec’s feature the encoder never writes it (a chunk that would use it is stored; a graph that runs its node fails, falling back only in permissive mode) and the decoder reports frames that use it as DecodeError::Unsupported. The decoder parses the chunk layout in full either way, so the error names the missing codec before any stream is decoded.

FeatureNodesCodec IDs
custom-codecapplication-defined nodes implementing CustomCodecapplication-defined u32 IDs in a separate namespace
zstdzstd22
conversionthe type conversions and string separation5-14
deltadelta_int1
zigzagzigzag3
divide-bydivide_by53
quantizequantize_offsets, quantize_lengths25, 26
range-packrange_pack35
bitsplitbitsplit_*, bit_split63
float-deconstructfloat32_, bfloat16_, float16_deconstruct33
sparse-numsparse_num, sparse_num_auto66
merge-sortedmerge_sorted43
constantconstant_serial, constant_fixed44, 45
prefixprefix46
transposetranspose_split2, 4, 30-32
splitsplitN, splitN_struct, splitN_num, split_byrange40, 47, 48
split-by-structsplit_by_struct41
dispatchdispatchN_byTag, dispatch_string42, 54
concatconcat_serial, _num, _struct, _string55, 57-59
dedupdedup_num, dedup_num_trusted56
interleaveinterleave_string61
mux-lengthsmux_lengths65
sentinelsentinel_byte, sentinel_num18
bitpackbitpack_serial, bitpack_int27, 28
bitunpackbitunpack34
flatpackflatpack29
tokenizetokenize_*36-38
parse-intparse_int60
partitionpartition64
entropyfse_v2, huffman_v2, huffman_struct_v2, fse_ncount49-52
pivco-huffmanpivco_huffman67
lz4lz462
lzlz19
field-lzfield_lz24

Feature names with a hyphen are Cargo features (field-lz); the library selects them independently, so features = ["std", "decompression", "zstd", "delta"] decodes only those codecs. Several standard graphs need more than one feature (the entropy graph uses most coders); a missing one makes the graph fail at run time. The scripts/check_codec_features.py script builds every combination.

Nodes

StandardNode::X below is the variant name for the registry ID; run a node from a function graph with GraphContext::run_node, or in a chain with GraphBuilder::pipeline. “Key parameters” are local parameters (see parameterized nodes) whose IDs are the reference’s; nodes not listed take none that matter.

Numeric transforms

IDStandardNodeFeatureSinceInOutWhat it does and key parameters
2DeltaIntdelta3numericnumericReplaces values with differences from their predecessor; the first value goes in the header
4Zigzagzigzag3numericnumericMaps signed-looking values to small unsigned ones
31DivideBydivide-by16numericnumericDivides every value by a common divisor. Copy parameter params::divide_by::DIVIDE_BY (112): the divisor as 8 little-endian bytes; absent or zero means the greatest common divisor
28RangePackrange-pack8numericnumericRebases values on their minimum into a narrower width
43, 44QuantizeOffsets, QuantizeLengthsquantize3numericnumeric, serialSplits values into a small code and extra bits (LZ offsets and lengths)
45-47, 75BitsplitTop8, BitsplitFp, BitsplitBf16, BitSplitbitsplit24numericvariable numericSplits each number into bit-field streams. Copy parameter 701 sets the field widths of BitSplit
6-8Float32Deconstruct, Bfloat16Deconstruct, Float16Deconstructfloat-deconstruct4 (5 for the 16-bit ones)numericstruct, serialSplits floats into sign+fraction and exponent streams
54, 55SparseNum, SparseNumAutosparse-num26numericnumeric, numericMostly-one-value data: the exceptions and their positions. Copy parameter params::sparse_num::DOMINANT_VALUE (132, 8 bytes) gives the dominant value of SparseNumAuto
50, 51SentinelByte, SentinelNumsentinel24numericnumeric, numericExceptional values behind a sentinel. Copy parameter params::sentinel::INDICES (130) holds the exception indices, params::sentinel::VALUE (131) the sentinel
29MergeSortedmerge-sorted9numericnumeric, numericMerges up to 64 sorted runs into one sorted stream plus a bitset
48Partitionpartition24numericnumeric, serialBucket IDs plus offsets within value-range partitions. Int parameter params::partition::PRESET (120) picks a preset; copy parameter params::partition::CUSTOM (121) holds a custom start value and partition sizes
53MuxLengthsmux-lengths24numeric, numericserial, numericMultiplexes the two length streams of lz
63, 64BitpackSerial, BitpackIntbitpack3serial / numericserialPacks values into the fewest bits the maximum needs
27Bitunpackbitunpack6serialnumericReads bit-packed bytes as numbers of a chosen bit width (the inverse of BitpackInt’s packing)
38ParseIntparse-int19stringnumericParses canonical decimal strings as 64-bit integers

Dictionary-style transforms

IDStandardNodeFeatureSinceInOutWhat it does and key parameters
40-42TokenizeStruct, TokenizeNumeric, TokenizeStringtokenize8 (11 for strings)struct / numeric / stringalphabet, numeric indicesAlphabet plus an index per element. Int parameter params::tokenize::SORT (0) non-zero sorts the alphabet
72, 73TokenizeSorted, TokenizeStringSortedtokenize8 / 11numeric / stringalphabet, numeric indicesThe same with sorting preset
65Flatpackflatpack3serialserial, serialAlphabet of at most 256 symbols plus packed indices
30Prefixprefix11stringstring, numericShared prefixes with the previous string, as match sizes plus suffixes
70, 71ConstantSerial, ConstantFixedconstant11serial / structone elementA stream of one repeated element, as the element and a count
37, 74DedupNum, DedupNumTrusteddedup16variable numericnumericIdentical inputs stored once. Int parameter 9438 = 1 skips the verification (DedupNumTrusted)

Splitting, merging and routing

IDStandardNodeFeatureSinceInOutWhat it does and key parameters
66-68SplitN, SplitNStruct, SplitNNumsplit9 (15 for struct and numeric)serial / struct / numericvariable same typeCuts into consecutive segments. Segment sizes are a reference parameter; run it with GraphContext::run_split_node
49SplitByrangesplit24numericvariable numericSplits by value range. Int parameter params::split::BYRANGE_MIN_SEGMENT_SIZE (324) is the minimum segment size
69SplitByStructsplit-by-struct9serialvariable structOne stream per field of a repeated record. Copy parameter 387 holds the field sizes
5DispatchNByTagdispatch9serialtags, sizes, variable serialRoutes segments to streams by tag; run it with GraphContext::run_dispatch_node and DispatchInstructions
32DispatchStringdispatch16stringindices, variable stringRoutes strings to streams by index; GraphContext::run_dispatch_string_node. Int parameter 47 is the number of outputs
33-36ConcatSerial, ConcatNum, ConcatStruct, ConcatStringconcat16-18variable same typesizes, concatenationConcatenates inputs, remembering their sizes (used by clustering)
39InterleaveStringinterleave20variable stringstringRound-robin interleaving of string streams
3TransposeSplittranspose11structvariable serialColumn-wise bytes of the records

Type conversions

The conversion feature covers IDs 10-26 and 57. They change the type of a stream, not its bytes, and appear in frames whenever typed data meets a serial-only codec.

IDStandardNodeInOutNotes
10ConvertStructToSerialstructserial
11, 12ConvertNumToStructLe, ConvertNumToSerialLenumericstruct / serial
13-16ConvertSerialToStruct, ...Struct2, ...Struct4, ...Struct8serialstructInt parameter 1 is the record width; the 2/4/8 variants preset it
17, 18ConvertStructToNumLe, ConvertStructToNumBestructnumericBig-endian from format 21
19-22ConvertSerialToNum8, ...NumLe16, ...NumLe32, ...NumLe64serialnumeric
23-25ConvertSerialToNumBe16, ...Be32, ...Be64serialnumericFormat 21 and newer
26SeparateStringComponentsstringserial (content), numeric (lengths)
57SetStringLensserialstringLengths come from LocalParams::with_string_lengths

Entropy coders

IDStandardNodeFeatureSinceInOutWhat it does
58FseV2entropy15serialnumeric counts, serial bitsFinite State Entropy
59HuffmanV2entropy15serialnumeric weights, serial bitsHuffman (zstd’s huf)
60HuffmanStructV2entropy15struct (width 2)numeric weights, serial bitsHuffman over 16-bit symbols
61FseNcountentropy15numericserialSerializes FSE count tables
87PivcoHuffmanpivco-huffman27serialnumeric weights, serial bitsPivoted-code Huffman

These come from OpenZL’s own FSE and Huffman fork, not from mzstd.

LZ and general-purpose coders

IDStandardNodeFeatureSinceInOutWhat it does and key parameters
62Zstdzstd3serialserialZstandard magicless frame via mzstd. Integer parameters are raw ZSTD_c_* IDs, named by params::zstd (COMPRESSION_LEVEL, WINDOW_LOG, …), applied after the global level; params::zstd::FORMAT and params::zstd::CONTENT_SIZE_FLAG are refused, as in the reference
86Lz4lz423serialserialLZ4 blocks (fast, high-compression and optimal parsers ported from lz4 1.10.0). Int parameter params::lz4::COMPRESSION_LEVEL_OVERRIDE (0) overrides the level
52Lzlz24serialliterals, offsets, literal lengths, match lengthsThe generic LZ codec; the Lz graph entropy-codes the four streams
9FieldLzfield-lz3structliterals, tokens, offsets, extra literal and match lengthsLZ over fixed-width fields. Int parameter params::field_lz::COMPRESSION_LEVEL_OVERRIDE (181) overrides the compression level (1-3 fast finder, 4 and up greedy)
use mopenzl::graph::params::{divide_by, zstd};
use mopenzl::graph::{GraphBuilder, LocalParams, StandardGraph, StandardNode};
use mopenzl::input::{Input, Numbers};
use mopenzl::{Compressor, Decompressor, EncoderConfig};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut builder = GraphBuilder::new();
    // divide_by with an explicit divisor of 1000, then zstd at level 19.
    let divide = builder.parameterize_node(
        StandardNode::DivideBy,
        LocalParams::default().with_bytes(divide_by::DIVIDE_BY, 1000u64.to_le_bytes().to_vec()),
    )?;
    let zstd19 = builder.parameterize_node(
        StandardNode::Zstd,
        LocalParams::default().with_int(zstd::COMPRESSION_LEVEL, 19),
    )?;
    let to_bytes = builder.static_graph(zstd19, &[StandardGraph::Store.into()])?;
    let start = builder.pipeline(
        &[divide, StandardNode::ConvertNumToSerialLe.into()],
        to_bytes,
    )?;
    let graph = builder.build(start)?;

    let values: Vec<u8> = (0..4096u32)
        .flat_map(|i| (i * 1000).to_le_bytes())
        .collect();
    let numbers = Numbers::try_from((&values[..], 4))?;
    let mut compressor = Compressor::builder(EncoderConfig::default())
        .with_graph(graph)
        .build()?;
    let frame = compressor.compress_inputs(&[Input::from(numbers)])?;
    let outputs = Decompressor::default().decompress_typed(&frame)?;
    assert_eq!(outputs[0].content(), values);
    Ok(())
}

Custom codecs

Application-defined codecs are supported for compression and decompression at format versions 21 to 27. The custom-codec feature is included in all-codecs and enabled by default; it also works with no_std and alloc.

Implement custom_codec::CustomCodec:

  • signature() declares the encoder’s input types, mandatory outputs and variable ports. Streams can be serial bytes, fixed-width records, numbers or strings. Numeric content is little-endian.
  • encode() reads borrowed CodecInput views, commits outputs through EncoderContext::emit or emit_strings, and can send a private header with send_header. Every mandatory output must be committed exactly once.
  • decode() receives the encoder’s output streams and private header, then restores the original inputs through DecoderContext::emit or emit_strings. The context validates widths, string lengths and the per-stream output budget.

Register the encoder with GraphBuilder::custom_codec(id, codec) and use the returned NodeId in a static graph, pipeline or function graph. Register the matching decoder with Decompressor::builder(config).with_custom_codec(id, codec)?. Both sides must agree on the wire ID, signature and encoded representation. Custom u32 IDs occupy a separate namespace from standard codec IDs; duplicate custom decoder IDs are rejected.

Custom nodes support parameterization and the regular one-shot and streaming APIs. Minimum-stream-size and store-on-expansion rules still apply: a small or expanding chunk may be stored instead of using the custom codec. Callbacks and signatures remain registered across operations and buffer trimming.

A frame carries the custom ID and its data, so the receiving application must register the decoder implementation. An unregistered ID, or a build without custom-codec, reports DecodeError::Unsupported { feature: Unsupported::CustomCodec { id } }. Custom dictionary materializers are not implemented.

See the API example for a complete implementation and registration example, and the custom codec specification for ownership, wire format, validation and error propagation.

Legacy codecs

Legacy codecs whose newest format version is below 21 (rolz, fastlz, zstd_fixed and the deprecated FSE and Huffman nodes) are not registered and decode as Unsupported::Codec.