| Crates.io | wavefst |
| lib.rs | wavefst |
| version | 0.1.0 |
| created_at | 2025-10-25 13:45:24.870998+00 |
| updated_at | 2025-10-25 13:45:24.870998+00 |
| description | Modern Rust implementation of the Fast Signal Trace (FST) binary waveform format. |
| homepage | |
| repository | https://github.com/0xtaruhi/wavefst |
| max_upload_size | |
| id | 1900239 |
| size | 265,249 |
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.
FST_BL_VCDATA*) are decoded and encoded symmetrically.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.
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(())
}
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 | Default | Description |
|---|---|---|
gzip |
✅ | Enable zlib/deflate support (hierarchy and VC blocks, optional z-wrapper). |
lz4 |
✅ | Support 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). |
mmap |
✅ | Expose the memory-mapped reader backend (io::MemoryMap). |
async |
⛔️ | Include buffered async wrappers (async_support) built on tokio. |
simd |
✅ | Use 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"
| Benchmark | Raw | Zlib | LZ4 |
|---|---|---|---|
reader_next_value_changes |
32.3 µs | 41.7 µs | 34.2 µs |
writer_emit_change |
115 µs | 194 µs | 125 µs |
Run with:
cargo bench
libfstUsing the upstream fstReaderIterBlocks helper (compiled with clang -std=c99 -O2):
| Scenario | Rust (µs) | C helper (µs) | Speed-up |
|---|---|---|---|
| baseline, raw | 50.9 | 335 551 | ×6 590 |
| baseline, wrapped | 57.0 | 6 436 | ×113 |
| wide, raw | 130.3 | 2 933 | ×22.5 |
| wide, wrapped | 65.3 | 2 191 | ×33.6 |
Debug builds show similar ratios (×4.6–×5.6). Event counters matched exactly between both implementations.
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.cargo test (add --features "async gzip serde simd" to exercise optional paths).cargo bench compares reader/writer throughput across compression modes.doc/fst_format.md describes the binary format; doc/rust_crate_design.md covers the
crate layout and design notes (both ASCII safe).wavefst-tool) for inspecting traces and converting to other formats.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.
Licensed under the modified MIT License. See LICENSE for details.