mmap-chunker-core
Zero-dependency data chunking engine with native memory-mapped I/O and a stable C ABI.
Why
Splitting large files into record-delimited chunks is a common task in data pipelines, log processing, and ETL workloads. Most solutions either copy data unnecessarily or pull in heavy dependencies. This library provides:
- Zero-copy chunk views backed by OS-level memory mapping
- Zero runtime dependencies — pure Rust with direct syscall FFI
- Language-agnostic C ABI — usable from C, Python, Go, C#, and any language with FFI
- Three planning modes: delimiter-aware chunking, fixed-size chunking, and record-aligned N-way partitioning
Features
- Targets Windows and POSIX platforms (Linux, macOS)
- Windows and Linux are validated in CI; macOS validation now included
- POSIX
mmap/ WindowsCreateFileMappingW - Configurable single-byte delimiter (newline, comma, tab, pipe, NUL, etc.)
- Multi-byte delimiter support (e.g.,
b"\r\n"for CRLF,b"\r\n\r\n"for HTTP-style) — Rust and C ABI - Zero-copy
CChunkView— chunk pointers reference the mapped file directly MADV_SEQUENTIALhint for sequential scan throughput- Panic containment at all FFI boundaries
- Thread-safe chunk retrieval after scan
- Immutable input contract with documented file-mutation semantics
Architecture
┌──────────┐ C ABI ┌──────────────────┐
│ C / Go / │◄────────────►│ mmap-chunker-core │
│ Python │ │ │
│ C# │ │ open ─► mmap │
│ │ │ scan ─► chunks │
│ │ │ get ─► view │
│ │ │ free │
└──────────┘ └──────────────────┘
C API
// Discover library version and capabilities
uint32_t ver = ;
uint32_t caps = ;
// Open and scan a file
CEngineHandle *h = ;
if
size_t count = ;
// For CRLF or another binary pattern, use pointer + length (ABI v1.3):
// const uint8_t delimiter[] = {'\r', '\n'};
// count = mmap_engine_scan_chunks_pattern(h, 64 * 1024, delimiter, 2);
// or: mmap_engine_scan_fixed(h, 4096) — fixed-size mode
// or: mmap_engine_partition_records(h, 4, '\n') — N-way partition planning
for
;
Rust Usage
use MmapChunker;
// ── Indexed (random access) ─────────────────────────────────
// Pre-computes chunk boundaries → O(1) random access by index.
// O(number_of_chunks) heap metadata (~16 bytes per chunk).
let mut file = unsafe ;
let count = file.scan_delimited;
let third = file.get_chunk;
// Iterate all
for i in 0..count
// ── Streaming (low memory) ─────────────────────────────────
// Yields chunks sequentially without building a boundary Vec.
// O(1) state (~40 bytes on 64-bit) regardless of file size.
// Ideal for single-pass consumers, pipelines, and large files.
let file = unsafe ;
for chunk in file.delimited_cursor
// ── Other scan modes ────────────────────────────────────────
let mut file = unsafe ;
// Fixed-size chunks (no delimiter)
let n = file.scan_fixed;
let block = file.get_chunk;
// Record-aligned N-way partitioning
let parts = file.partition_records;
for i in 0..parts
// Multi-byte delimiters (CRLF, HTTP-style, custom separators)
let mut file = unsafe ;
let n = file.scan_delimited_pattern;
let chunk = file.get_chunk;
// Lazy cursor with multi-byte delimiter
let file = unsafe ;
for chunk in file.delimited_cursor_pattern
Scanner primitives (standalone, no mmap)
use scanner;
let data = b"aaa\nbbb\nccc\nddd\n";
// 1. Eager delimiter-aware chunking — returns Vec<(usize, usize)>
let chunks = find_chunk_boundaries;
// 2. Lazy delimiter cursor — yields &[u8] slices on demand
let slices: = new.collect;
// 3. Multi-byte delimiter scanner — e.g., CRLF, HTTP-style separators
let chunks = find_chunk_boundaries_pattern;
// 4. Lazy multi-byte cursor
let slices: = new.collect;
// 5. Fixed-size chunking — O(1) arithmetic layout, zero scan cost
let count = fixed_chunk_count;
let bounds = fixed_chunk_bounds;
// 6. Record-aligned N-way partitioning — for parallel consumers
let partitions = find_partition_boundaries;
Prebuilt Libraries (C / Python / Go / FFI)
Prebuilt native libraries are published on GitHub Releases for every tagged version. Each platform archive contains the C header, dynamic library, static library, and licenses.
| Platform | Archive | Contents |
|---|---|---|
| Linux x86_64 | mmap-chunker-core-{ver}-x86_64-unknown-linux-gnu.tar.gz |
.so, .a |
| Linux aarch64 | mmap-chunker-core-{ver}-aarch64-unknown-linux-gnu.tar.gz |
.so, .a |
| macOS x86_64 | mmap-chunker-core-{ver}-x86_64-apple-darwin.tar.gz |
.dylib, .a |
| macOS arm64 | mmap-chunker-core-{ver}-aarch64-apple-darwin.tar.gz |
.dylib, .a |
| Windows x86_64 | mmap-chunker-core-{ver}-x86_64-pc-windows-msvc.zip |
.dll, .dll.lib, .lib |
# Python with ctypes (download archive, extract, load)
= # or .dll / .dylib
=
assert == 0x00010003
// C: compile against extracted archive
// cc -I staging/include/ -L staging/lib/ -lmmap_chunker_core your_program.c
uint32_t ver = ;
See mmap_chunker.h for the complete C API reference with threading and safety contracts.
Safety Contract
- Handle owns all resources: mmap, chunk metadata. Freed with
mmap_engine_free. - Chunk views borrow from handle: valid until
mmap_engine_free. Use-after-free is undefined. - Immutable input: The file must not be truncated or overwritten while the handle is live.
- Panic isolation: All FFI boundaries catch panics.
mmap_engine_freeaborts on panic (no return value for error). - Threading: Single-threaded open/scan/free. Multi-threaded chunk retrieval after scan.
File Mutation Contract
The engine provides a read-only view of the file at mapping time. If another process truncates or overwrites the file:
- POSIX: May deliver
SIGBUSor return zero-filled pages - Windows: Mapped view may become invalid (access violation)
Recommendation: Treat the input file as immutable for the handle lifetime.
Benchmarks
# I/O benchmark (mmap vs fs::read)
# Cursor vs eager time-to-first-chunk + full traversal
Time-to-first-chunk (TFC) advantage with lazy cursor on JSONL/log data (64 KiB chunks, release build, 7-sample p50):
| File Size | Eager TFC | Lazy TFC | Speedup | Chunks |
|---|---|---|---|---|
| 100 KB | 139 ns | 8.5 ns | 16x | 2 |
| 1 MB | 554 ns | 10 ns | 55x | 16 |
| 10 MB | 2,780 ns | 10 ns | 278x | 153 |
Full traversal converges as file size grows (both do equivalent scan work). Lazy is 2.2x faster at 1 MB and 1.3x faster at 10 MB (Vec allocation overhead). I/O benchmark runs on 1 MB–64 MB files with 64 KB–1 MB chunk sizes.
Build
Outputs:
target/release/mmap_chunker_core.dll(Windows)target/release/libmmap_chunker_core.so(Linux/macOS)target/release/libmmap_chunker_core.a(static library)
Tests
The suite covers delimiter semantics, cursor equivalence, fixed-size chunking, partitioning, C ABI behavior, and edge cases.
Companion test suites:
- 30 external C ABI assertions via
examples/c_consumer.c(CI-validated on Linux and macOS) - 53 Python ctypes integration tests (local, companion module)
Limitations
- Full-file mapping only (no windowed mmap). Very large files may exhaust address space.
- No copy-on-write or mutable access. Read-only mapping.
- No regex delimiters. Multi-byte delimiters supported (e.g.,
b"\r\n",b"\r\n\r\n").
Roadmap
- SIMD-accelerated byte search (runtime dispatch)
License
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT license (LICENSE-MIT)
at your option.