#memory-mapping #mmap #amount #file #generated #helper #handles

no-std lightweight-mmap

Simple memory mapping helpers for Rust, with minimal amount of code generated

4 releases (2 breaking)

new 0.3.0 Nov 3, 2024
0.2.1 Nov 3, 2024
0.2.0 Nov 2, 2024
0.1.0 Nov 2, 2024

#90 in Memory management

Download history 283/week @ 2024-10-28

283 downloads per month

Custom license

83KB
2K SLoC

lightweight-mmap

Crates.io Docs.rs CI

About

Simple memory mapping helpers for Rust, with minimal amount of code generated.

This crate provides the facilities for opening a file and mapping it to memory with the minimal amount of code generated.

Motivation

This crate is for absolute freaks like me who wish to save 3-8KB of code size in their binaries; with a thin limited wrapper providing complete zero overhead abstraction. Since std and 3rd party mmap crates will compile and run a small amount of code which may not be needed for your use case.

If you have more advanced needs than present in this crate, consider using std for opening file handles and a library like memmap2-rs and mmap-rs for mapping.

The API surface here is driven by sewer56-archives-nx and any other projects of mine which need mmap in a tiny package.

Characteristics

  • Minimal code size overhead
  • Platform-native file handles
  • Read-only and read-write memory mappings
  • Support for offset and length in mappings
  • Supports zero sized mappings.
  • Supports mappings to unaligned file offsets.
  • Cross-platform compatibility
  • Thread-safe (Send but not Sync)
  • All opened handles can be accessed by multiple processes (Linux behaviour)

Crate Features

  • std (default): Enables standard library support
  • mmap (default): Enables memory map operations, adding mmap cache info to handles on some platforms. Without this, library can only be used for opening raw file handles.
  • no-format: Reduces binary size by skipping core::fmt formatting machinery as much as possible. Uses itoa and nanokit crates for minimal formatting.
  • trim-file-lengths: Ensures memory maps cannot exceed file size by trimming mapping length. Adds a small overhead to map open time.

To use without standard library:

[dependencies]
lightweight-mmap = { version = "x.y.z", default-features = false }

To minimize binary size:

[dependencies]
lightweight-mmap = { version = "x.y.z", features = ["no-format"] }

Whether you should use no-format should depend on whether you already use core::fmt elsewhere in your library/binary. Check with cargo bloat. If you don't, it's best to use no-format to reduce binary size.

Platform Support

The crate is tested and supported on:

Windows

  • x86_64-pc-windows-msvc
  • i686-pc-windows-msvc
  • aarch64-pc-windows-msvc

Linux

  • x86_64-unknown-linux-gnu
  • i686-unknown-linux-gnu
  • aarch64-unknown-linux-gnu
  • armv7-unknown-linux-gnueabihf

macOS

  • x86_64-apple-darwin
  • aarch64-apple-darwin

Android

  • x86_64-linux-android
  • i686-linux-android

For other platforms, level of support is unknown.

Examples

File Handles

Open a read-only file handle to an existing file:

let handle = ReadOnlyFileHandle::open("assets/test_file.txt").unwrap();

Open a read-write file handle to an existing file:

let handle = ReadWriteFileHandle::open("assets/test_file.txt").unwrap();

Create a new file with pre-allocated size:

let handle = ReadWriteFileHandle::create_preallocated("assets/test_file.txt", 1024).unwrap();

This will create a new file or overwrite an existing file.

Memory Mapping

Create a read-only memory mapping:

use lightweight_mmap::{ReadOnlyFileHandle, ReadOnlyMmap};

// Open the file
let handle = ReadOnlyFileHandle::open("data.bin").unwrap();

// Map 1024 bytes starting at offset 4096
let mapping = ReadOnlyMmap::new(&handle, 4096, 1024).unwrap();

// Access the mapped memory
let data = mapping.as_slice();

Create a read-write memory mapping:

use lightweight_mmap::{ReadWriteFileHandle, ReadWriteMmap};

// Open the file
let handle = ReadWriteFileHandle::open("data.bin").unwrap();

// Map 1024 bytes starting at offset 4096
let mapping = ReadWriteMmap::new(&handle, 4096, 1024).unwrap();

// Access and modify the mapped memory
unsafe {
    let data = core::slice::from_raw_parts_mut(mapping.data(), mapping.len());
    data[0] = 42;
}

Note: Memory mappings cannot outlive their file handles (compiler should ensure this), and the mapped memory should be accessed carefully to avoid data races.

Memory Advice

Provide hints to the operating system about how memory mapped regions will be accessed:

use lightweight_mmap::{ReadOnlyFileHandle, ReadOnlyMmap, MemoryAdvice};

// Open and map the file
let handle = ReadOnlyFileHandle::open("data.bin").unwrap();
let mapping = ReadOnlyMmap::new(&handle, 0, 1024).unwrap();

// Indicate we'll access this memory soon
mapping.advise(MemoryAdvice::WILL_NEED);

// Indicate sequential access pattern
mapping.advise(MemoryAdvice::SEQUENTIAL);

// Combine multiple hints
mapping.advise(MemoryAdvice::WILL_NEED | MemoryAdvice::SEQUENTIAL);

Available advice flags:

  • WILL_NEED: Indicates that the application expects to access the memory soon
  • SEQUENTIAL: Indicates that memory access will be sequential from lower to higher addresses
  • RANDOM: Indicates that memory access will be random (non-sequential)

Note: These are hints and may be ignored by the operating system. Not all hints are supported on all platforms. On Windows, only WILL_NEED has an effect.

Development

For information on how to work with this codebase, see README-DEV.MD.

License

Licensed under MIT.

Learn more about Reloaded's general choice of licensing for projects..

Dependencies

~0.3–8.5MB
~77K SLoC