Crate wavefst

Crate wavefst 

Source
Expand description

§wavefst

Crates.io Documentation License

Modern Rust reader and writer for the Fast Signal Trace (FST) waveform format.

wavefst streams FST data without copying, stays compatible with the original C libfst implementation, and layers ergonomic APIs on top of the low-level block layout. Readers, writers, benchmarks, and fuzz targets all live in the same crate so you can inspect existing traces, build new ones, or validate interoperability from a single dependency.


§Highlights

  • Full block coverage – header, geometry, hierarchy, blackout, and every value-change variant (FST_BL_VCDATA*) are decoded and encoded symmetrically.
  • Zero-copy iteration – value changes stream straight from decoded buffers with alias handling, initial frames, and timestamps resolved for you.
  • Pluggable compression – raw, zlib, LZ4, and FastLZ logic is reusable between reader and writer pipelines, each gated behind a feature flag.
  • Async, SIMD, serde – optional helpers wrap the synchronous APIs for async I/O, fast ASCII→bit packing, and serialisable hierarchy/value-change snapshots.
  • Tooling ready – Criterion benches, libFuzzer harnesses, and integration tests are included to keep regressions in check.

§Installation

cargo add wavefst

The default feature set enables gzip/zlib (gzip), LZ4 (lz4), memory mapping (mmap), and the SSE2 packed-bit fast path (simd). Disable them with --no-default-features and opt back into the ones you need.


§Quick Start

§Reading a trace

use wavefst::{ReaderBuilder, SignalValue};

fn dump_changes(path: &str) -> wavefst::Result<()> {
    let file = std::fs::File::open(path)?;
    let mut reader = ReaderBuilder::new(file).build()?;

    while let Some(mut block) = reader.next_value_changes()? {
        while let Some(event) = block.next() {
            let event = event?;
            println!("t={} handle={} value={:?}", event.timestamp, event.handle, event.value);
        }
    }
    Ok(())
}

§Writing a trace

use std::borrow::Cow;
use wavefst::{
    ChainCompression, FstWriter, GeomEntry, Header, ScopeType, SignalValue, TimeCompression,
    VarDir, VarType,
};

fn build_example(path: &str) -> wavefst::Result<()> {
    let file = std::fs::File::create(path)?;
    let mut writer = FstWriter::builder(file)
        .chain_compression(ChainCompression::Lz4)
        .time_compression(TimeCompression::Zlib)
        .build()?;

    writer.begin_scope(ScopeType::VcdModule, "tb", None)?;
    let bit = writer.add_variable(VarType::VcdWire, VarDir::Implicit, "bit", GeomEntry::Fixed(1))?;
    let vec = writer.add_variable(VarType::VcdWire, VarDir::Implicit, "vec", GeomEntry::Fixed(8))?;
    writer.end_scope()?;

    let mut header = Header::default();
    header.version = "wavefst-demo".into();
    header.vc_section_count = 1;
    header.end_time = 20;
    writer.write_header(header)?;

    writer.emit_change(0, bit, SignalValue::Bit('0'))?;
    writer.emit_change(10, bit, SignalValue::Bit('1'))?;
    writer.emit_change(12, vec, SignalValue::Vector(Cow::Borrowed("10101010")))?;
    writer.finish()?;
    Ok(())
}

§Feature Flags

FeatureDefaultDescription
gzipEnable zlib/deflate support (hierarchy and VC blocks, optional z-wrapper).
lz4Support LZ4-compressed hierarchy blocks and value-change chains.
fastlz⛔️Add FastLZ decompression/compression for value-change chains.
parallel⛔️Use Rayon to decode chain payloads in parallel while keeping results sorted.
serde⛔️Provide serialisable hierarchy and value-change snapshots (serde_support).
mmapExpose the memory-mapped reader backend (io::MemoryMap).
async⛔️Include buffered async wrappers (async_support) built on tokio.
simdUse SSE2 to accelerate ASCII vector packing (falls back to scalar elsewhere).

Disable defaults with --no-default-features and enable the subset you need, for example:

cargo add wavefst --no-default-features --features "gzip parallel"

§Performance

§Criterion benches (release profile, Apple M-series, macOS 25.0.0)

BenchmarkRawZlibLZ4
reader_next_value_changes32.3 µs41.7 µs34.2 µs
writer_emit_change115 µs194 µs125 µs

Run with:

cargo bench

§Comparison with C libfst

Using the upstream fstReaderIterBlocks helper (compiled with clang -std=c99 -O2):

ScenarioRust (µs)C helper (µs)Speed-up
baseline, raw50.9335 551×6 590
baseline, wrapped57.06 436×113
wide, raw130.32 933×22.5
wide, wrapped65.32 191×33.6

Debug builds show similar ratios (×4.6–×5.6). Event counters matched exactly between both implementations.


§Async, SIMD, and serde helpers

  • wavefst::async_support::{AsyncReader, AsyncWriter} buffer async sources/sinks using tokio before delegating to the synchronous codecs. Useful when you cannot block the reactor thread.
  • wavefst::serde_support (behind serde) snapshots hierarchy trees and value changes as owned data structures that plug directly into serde_json, CBOR, etc.
  • simd enables an SSE2 fast path for ASCII vector packing; non-x86 targets automatically use the scalar implementation.

§Tooling

  • Testscargo test (add --features "async gzip serde simd" to exercise optional paths).
  • Benchescargo bench compares reader/writer throughput across compression modes.
  • Docsdoc/fst_format.md describes the binary format; doc/rust_crate_design.md covers the crate layout and design notes (both ASCII safe).

§Roadmap

  • Optional CLI (wavefst-tool) for inspecting traces and converting to other formats.
  • Additional fixtures that exercise mixed compression, alias chains, and multi-block timelines.
  • Configurable logging hooks (tracing) for long-running ingest jobs.

Contributions, bug reports, and ideas are very welcome. File an issue or open a pull request with a reproduction trace if you hit a corner case.


§License

Licensed under the modified MIT License. See LICENSE for details.

Re-exports§

pub use block::BlackoutBlock;
pub use block::BlackoutEvent;
pub use block::GeomEntry;
pub use block::GeomInfo;
pub use block::Header;
pub use block::HierarchyBlock;
pub use block::ScopeEntry;
pub use block::TimeSection;
pub use block::VarEntry;
pub use block::VcBlock;
pub use compression::Compressor;
pub use compression::Decompressor;
pub use compression::NullCompressor;
pub use compression::NullDecompressor;
pub use error::Error;
pub use error::Result;
pub use reader::ChainIndex;
pub use reader::ChainSlot;
pub use reader::FstReader;
pub use reader::ReaderBuilder;
pub use reader::ReaderOptions;
pub use reader::VcBlockMeta;
pub use writer::ChainCompression;
pub use writer::FstWriter;
pub use writer::ScopeId;
pub use writer::TimeCompression;
pub use writer::WriterBuilder;
pub use writer::WriterOptions;
pub use types::*;

Modules§

block
Block-level data structures mapping raw FST sections into typed records. Structured representations of the different FST block types.
compression
Compression backends used for value-change and hierarchy payloads. Compression backends used by value change and hierarchy blocks.
encoding
Encoding helpers such as variable-length integer codecs. Encoding helpers (varints, zig-zag encoding, etc.).
error
Shared error and result types.
io
I/O backends (buffered and memory-mapped). I/O backends used by the reader and writer implementations.
reader
Streaming reader front-end for FST files. High-level streaming reader for FST files.
types
Enumerations and value abstractions used across the crate. Core type definitions used across the crate.
util
Miscellaneous helpers consumed by readers and writers.
writer
Streaming writer for constructing FST traces. Incremental writer producing FST output streams.