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

Framing containers

Package independent resources with metadata and a checked seek index. Each resource contains one ordinary OpenZL frame, with its own graph and serial, typed or multiple inputs. The outer format belongs to mopenzl; an ordinary OpenZL decoder reads an extracted frame rather than the complete container.

Enable framing

mopenzl = { version = "0.2", features = ["framed"] }

The book follows the repository’s main branch; use this checkout or a Git dependency for features not yet in a crates.io release. framed is opt-in, outside default and all-codecs. With compression it exposes the framed encoder; with decompression, the framed decoder. Owned results and borrowed native sessions work with alloc; std adds writer, sequential reader and seek reader adapters. no_std removes those transport adapters.

The container has one resource profile and a mandatory directory/footer. Resource commands take complete borrowed inputs; there are no byte-oriented resource writers, hidden resources, padding or repeated metadata. Split large input into resources explicitly. See format and lifecycle.

One container, two resources

Use the default graph for text and Store for already encoded binary data. Graph overrides apply only to their resource and are restored after failure.

use mopenzl::framing::{FramedCompressor, FramedDecompressor, FramedResource};
use mopenzl::graph::{CompressorGraph, StandardGraph};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let text = b"notes for a compressed resource\n".repeat(200);
    let binary = [1, 2, 3, 4, 5];
    let store = CompressorGraph::standard(StandardGraph::Store);
    let resources = [
        FramedResource::from(text.as_slice()),
        FramedResource { graph: Some(&store), ..FramedResource::from(&binary[..]) },
    ];
    let mut encoder = FramedCompressor::new(Default::default())?;
    let container = encoder.compress(resources.as_slice().into())?;
    let mut decoder = FramedDecompressor::new(Default::default())?;
    let decoded = decoder.decompress(&container)?;
    assert_eq!(decoded.resources.len(), 2);
    assert_eq!(decoded.resources[0].outputs[0].content(), text);
    assert_eq!(decoded.resources[1].outputs[0].content(), binary);
    Ok(())
}

compress_into appends transactionally to a reusable Vec and returns the container’s range. Raw slice/uninitialized output methods belong to the ordinary codec; framed output uses Vec, a native output session or a writer. FramedResource::from(&inputs[..]) accepts typed/multiple Input values. See graphs and typed resources.

Write resources and metadata

Use a writer when complete resources arrive one after another. Attach borrowed metadata directly to each resource command, then finish the enclosing container. Metadata codes are uppercase ASCII pairs, id (unique UTF-8 name within one resource’s fields), or mt (unique eight-byte signed little-endian timestamp).

use mopenzl::framing::{
    FramedCompressor, FramedDecompressor, FramedEncodeConfig, FramedResource,
    FramingConfig, MetadataField,
};
use mopenzl::graph::{CompressorGraph, StandardGraph};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = FramedEncodeConfig {
        framing: FramingConfig { max_resources: 16, ..Default::default() },
        ..Default::default()
    };
    let fields = [MetadataField { code: *b"id", value: b"notes.txt" }];
    let store = CompressorGraph::standard(StandardGraph::Store);
    let mut encoder = FramedCompressor::new(config)?;
    let mut writer = encoder.framed_writer(Vec::new())?;
    writer.resource(FramedResource {
        metadata: &fields,
        ..FramedResource::from(&b"first part\nsecond part\n"[..])
    })?;
    writer.resource(FramedResource {
        graph: Some(&store),
        ..FramedResource::from(&[1, 2, 3, 4][..])
    })?;
    let container = writer.finish().map_err(|failure| failure.error)?;
    let decoded = FramedDecompressor::new(Default::default())?.decompress(&container)?;
    assert_eq!(decoded.resources[0].outputs[0].content(), b"first part\nsecond part\n");
    assert_eq!(decoded.resources[0].metadata[0].value, b"notes.txt");
    assert_eq!(decoded.resources[1].outputs[0].content(), [1, 2, 3, 4]);
    Ok(())
}

No Drop performs I/O. resource drains older output before accepting its command; an error means this resource was not accepted, so retrying that command does not duplicate it. try_finish retains the writer after an I/O error; consuming finish returns FramedFinishError with writer and error fields. See the retry state machine.

id is a name, not an extraction path or a globally unique lookup key. Framing computes/verifies metadata and directory XXH3 checksums, while inner OpenZL checksums follow encoder/decoder configuration. These detect damage and do not authenticate content.

Indexed resource reads

Open the footer and directory on a Read + Seek source, then decode only a selected whole resource. A File can replace this example’s Cursor.

use mopenzl::framing::{FramedCompressor, FramedDecompressor, FramedResource};
use std::io::Cursor;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resources = [
        FramedResource::from(&b"first"[..]),
        FramedResource::from(&b"second"[..]),
    ];
    let container = FramedCompressor::new(Default::default())?
        .compress(resources.as_slice().into())?;
    let mut decoder = FramedDecompressor::new(Default::default())?;
    let mut reader = decoder.framed_seek_reader(Cursor::new(container))?;
    assert_eq!(reader.resources().len(), 2);
    let selected = reader.read_resource(1)?;
    assert_eq!(selected.outputs[0].content(), b"second");
    Ok(())
}

Indices are zero-based usize values, independent of metadata names. Opening validates the directory; reading validates that resource’s header, metadata, size and inner frame. It returns owned typed outputs rather than a Read adapter. The container extends from the source’s current position to EOF; offsets remain relative for a container embedded after a prefix. External dictionary bundles come from the decoder’s configured BundleStore. See indexed decoder mechanics.

Limits and streaming decoding

FramingConfig bounds resources, metadata per resource, encoded frame bytes, aggregate decoded content and total wire bytes. FramedDecodeConfig::decoder adds raw decoder input/output limits and checksum policy for each frame.

For arriving input, FramedDecompressor::start yields owned whole-resource events; framed_reader adapts a Read source through read_resource. Continue until Finished or None to validate the directory, footer and EOF. Earlier resources remain tentative until then. There is no automatic raw-frame detection or dictionary reference to an earlier resource. See Sans I/O decoding and dictionary bundles.