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

Framed resources with independent graphs

Enable the opt-in feature alongside your codec directions:

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

framing::FramedCompressor writes an indexed container. Each resource is one ordinary OpenZL frame with its own graph and optional metadata. The outer container is mopenzl-specific: an ordinary OpenZL decoder accepts an extracted resource frame, not the complete container. The decoder reconstructs codecs from each frame; it needs no graph selection.

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let store = CompressorGraph::standard(StandardGraph::Store);
    let name = [MetadataField { code: *b"id", value: b"image.bin" }];
    let text = b"text text text text text text";
    let image = [0x42; 1024];
    let resources = [
        FramedResource::from(&text[..]), // default graph
        FramedResource {
            graph: Some(&store),
            metadata: &name,
            ..FramedResource::from(&image[..])
        },
    ];
    let mut encoder = FramedCompressor::new(Default::default())?;
    let bytes = encoder.compress(resources.as_slice().into())?;
    let mut decoder = FramedDecompressor::new(Default::default())?;
    let output = decoder.decompress(&bytes)?;
    assert_eq!(output.resources[0].outputs[0].content(), text);
    assert_eq!(output.resources[1].outputs[0].content(), image);
    Ok(())
}

For typed or multiple inputs, construct FramedResource::from(&inputs[..]) where inputs is a slice of mopenzl::input::Input. Decoded resources retain the original output types, widths and string lengths. A graph override lasts only for its resource. Convert a configured Compressor or Decompressor with FramedCompressor::from / FramedDecompressor::from to retain its backend, graph, retention, dictionaries or custom-codec registrations.

Without no_std, use encoder.framed_writer(sink) to add complete resources as they arrive, then finish to write the seek index. resource accepts complete borrowed inputs and queues one encoded resource. flush delivers queued bytes; finish returns the sink or a failure containing the writer for retry. Nothing is written on Drop. Reader methods return whole typed resources:

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

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut encoder = FramedCompressor::new(Default::default())?;
    let mut writer = encoder.framed_writer(Vec::new())?;
    writer.resource(FramedResource::from(&b"first"[..]))?;
    writer.resource(FramedResource::from(&b"second"[..]))?;
    let bytes = writer.finish().map_err(|failure| failure.error)?;

    let mut decoder = FramedDecompressor::new(Default::default())?;
    let mut reader = decoder.framed_seek_reader(Cursor::new(&bytes))?;
    assert_eq!(reader.resources().len(), 2);
    assert_eq!(reader.read_resource(1)?.outputs[0].content(), b"second");
    drop(reader);
    let mut sequential = decoder.framed_reader(&bytes[..]);
    while let Some(resource) = sequential.read_resource()? {
        println!("{} content bytes", resource.info.decoded_size);
    } // None validates the directory, footer and EOF.
    Ok(())
}

With alloc (including no_std), both codecs also offer borrowed native sessions. Drain an encoder session’s header with process(output, Process) until NeedsInput, submit resource, drain again, and finally use Finish until Finished. Decoder sessions consume wire bytes until a resource event; process the unconsumed suffix, and use DecodeOperation::Finish at EOF. Resource events are tentative until Finished validates the directory and footer.

FramingConfig supplies bounded resource count, metadata, frame, aggregate output and total wire sizes. Configure it through FramedEncodeConfig, FramedDecodeConfig, or reconfigure. Inputs are complete per resource, and each resource/frame is staged in memory. Choose resource boundaries explicitly for large streams. This first version supports resource metadata and whole resource seek; it does not mirror every mbrotli/mzstd container command.

See the wire format, lifecycle and limits.