| Crates.io | mf4-rs |
| lib.rs | mf4-rs |
| version | 1.0.0 |
| created_at | 2025-11-29 14:26:53.42653+00 |
| updated_at | 2025-11-29 14:26:53.42653+00 |
| description | mf4-rs is a Rust library for working with ASAM MDF 4 (Measurement Data Format) files. |
| homepage | |
| repository | https://github.com/dmagyar-0/mf4-rs |
| max_upload_size | |
| id | 1956820 |
| size | 392,415 |
mf4-rs is a minimal Rust library for working with ASAM MDF 4 (Measurement Data Format) files. It supports parsing existing files as well as writing new ones through a safe API, implementing a subset of the MDF 4.1 specification sufficient for simple data logging and inspection tasks.
The codebase is organized into distinct layers:
src/api/)MDF - Main entry point for parsing files from diskChannelGroup - Wrapper providing ergonomic access to channel group metadataChannel - High-level channel representation with value decodingsrc/writer/)write_record) and batch operations (write_records)src/blocks/)src/parsing/)src/)cut.rs - Time-based file cutting functionalitymerge.rs - File merging utilitieserror.rs - Centralized error handlingindex.rs - MDF file indexing system for fast metadata-based accessMemory-Mapped File Access: The parser uses memmap2 to avoid loading entire files into memory, enabling efficient handling of large measurement files.
Lazy Evaluation: Channel groups, channels, and values are created as lightweight wrappers that decode data only when accessed.
Builder Pattern: The writer uses closure-based configuration for channels and channel groups, allowing flexible setup while maintaining type safety.
Block Linking: The MDF format uses address-based linking between blocks. The writer maintains a position map to update links after blocks are written.
# Build the project
cargo build
# Run all tests
cargo test
# Run specific test file
cargo test --test api
The project includes simplified examples in the examples/ directory:
write_file.rs - Comprehensive example of writing MDF files with multiple channelsread_file.rs - Demonstrates parsing and inspecting MDF filesindex_operations.rs - Shows advanced indexing, byte-range reading, and conversion resolutionmerge_files.rs - Merging multiple MF4 filescut_file.rs - Time-based file cuttingpython_equivalent.rs - Comparison with Python functionalityRun them with:
cargo run --example write_file
cargo run --example read_file
cargo run --example index_operations
use mf4_rs::writer::MdfWriter;
use mf4_rs::blocks::common::DataType;
use mf4_rs::parsing::decoder::DecodedValue;
let mut writer = MdfWriter::new("output.mf4")?;
writer.init_mdf_file()?;
let cg = writer.add_channel_group(None, |_| {})?;
// Create master channel (usually time)
let time_ch_id = writer.add_channel(&cg, None, |ch| {
ch.data_type = DataType::FloatLE;
ch.name = Some("Time".to_string());
ch.bit_count = 64;
})?;
writer.set_time_channel(&time_ch_id)?; // Mark as master channel
// Add data channels with master as parent
writer.add_channel(&cg, Some(&time_ch_id), |ch| {
ch.data_type = DataType::UnsignedIntegerLE;
ch.name = Some("DataChannel".to_string());
ch.bit_count = 32;
})?;
writer.start_data_block_for_cg(&cg, 0)?;
writer.write_record(&cg, &[
DecodedValue::Float(1.0), // Time
DecodedValue::UnsignedInteger(42), // Data
])?;
writer.finish_data_block(&cg)?;
writer.finalize()?;
use mf4_rs::api::mdf::MDF;
let mdf = MDF::from_file("input.mf4")?;
for group in mdf.channel_groups() {
println!("channels: {}", group.channels().len());
for channel in group.channels() {
let values = channel.values()?;
// Process values...
}
}
The library includes a powerful indexing system that allows you to:
ByteRangeReader trait (local files, HTTP, S3, etc.)// Create an index from an MDF file
let index = MdfIndex::from_file("data.mf4")?;
// Save index to JSON for later use
index.save_to_file("data_index.json")?;
// Later: load index and read specific channel data
let loaded_index = MdfIndex::load_from_file("data_index.json")?;
// Option 1: Use built-in file reader
let mut file_reader = FileRangeReader::new("data.mf4")?;
let channel_values = loaded_index.read_channel_values(0, 1, &mut file_reader)?;
// Option 2: Use HTTP range reader (production)
let mut http_reader = HttpRangeReader::new("https://cdn.example.com/data.mf4".to_string());
let channel_values = loaded_index.read_channel_values(0, 1, &mut http_reader)?;
mf4-rs includes high-performance Python bindings generated using pyo3. This allows you to use the library's features directly from Python with minimal overhead.
You can install the package directly using pip or uv (requires a Rust compiler):
pip install .
# or
uv pip install .
For development, you can use maturin:
# Install maturin
pip install maturin
# Build and install in current environment
maturin develop --release
Check the python_examples/ directory for complete scripts:
write_file.py - Creating MDF filesread_file.py - Reading and inspecting filesindex_operations.py - Using the indexing systemimport mf4_rs
# Writing a file
writer = mf4_rs.PyMdfWriter("output.mf4")
writer.init_mdf_file()
group = writer.add_channel_group("MyGroup")
# Add channels
time_ch = writer.add_time_channel(group, "Time")
data_ch = writer.add_float_channel(group, "Data")
# Write data
writer.start_data_block(group)
writer.write_record(group, [
mf4_rs.create_float_value(0.1), # Time
mf4_rs.create_float_value(42.0) # Data
])
writer.finish_data_block(group)
writer.finalize()
# Reading a file
mdf = mf4_rs.PyMDF("output.mf4")
for group in mdf.channel_groups():
print(f"Group: {group.name}, Channels: {group.channel_count}")
mf4-rs is designed for high performance:
write_records for batch operations instead of multiple write_record callsNote: Previous benchmarks have been removed as they are being updated.
nom - Binary parsing combinatorsbyteorder - Endianness handlingmemmap2 - Memory-mapped file I/Omeval - Mathematical expression evaluation for formula conversionsthiserror - Error handling derive macros