From aaa789644306bafb9d19b476f0a50807b0b981f0 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 27 Dec 2025 14:51:13 +0100 Subject: [PATCH 1/5] Initial commit with task details Adding CLAUDE.md with task information for AI processing. This file will be removed when the task is complete. Issue: https://github.com/linksplatform/mem-rs/issues/20 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a3350e9 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +Issue to solve: https://github.com/linksplatform/mem-rs/issues/20 +Your prepared branch: issue-20-f2de19c1454e +Your prepared working directory: /tmp/gh-issue-solver-1766843472317 + +Proceed. \ No newline at end of file From 3b0622d223b8537a8a5f757960b07b125bf4c5be Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 27 Dec 2025 14:55:15 +0100 Subject: [PATCH 2/5] Add detailed README.md documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create comprehensive README with: - Project overview and features description - Installation instructions with nightly Rust note - Usage examples: Global allocator, FileMapped, TempFile - Generic RawMem trait usage and type-erased memory examples - API reference tables for RawMem trait and memory types - Error handling documentation - Related projects section Fixes #20 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..d76c046 --- /dev/null +++ b/README.md @@ -0,0 +1,185 @@ +# platform-mem + +[![Crates.io](https://img.shields.io/crates/v/platform-mem.svg)](https://crates.io/crates/platform-mem) +[![License](https://img.shields.io/crates/l/platform-mem.svg)](LICENSE) + +A Rust library for low-level memory management with unified interface for allocator-backed and memory-mapped file storage. + +## Overview + +`platform-mem` provides the `RawMem` trait that abstracts over different memory backends: + +- **Allocator-based memory** (`Global`, `System`, `Alloc`) - uses Rust's allocator API +- **Memory-mapped files** (`FileMapped`, `TempFile`) - uses `mmap` for persistent or temporary file-backed storage + +This allows writing generic code that works with any memory backend, making it easy to switch between heap allocation and file-mapped storage. + +## Features + +- **Unified `RawMem` trait** - common interface for growing, shrinking, and accessing memory +- **Type-erased memory** via `ErasedMem` - enables dynamic dispatch with `Box>` +- **Memory-mapped files** - persistent storage with automatic page management +- **Temporary file storage** - anonymous file-backed memory that's cleaned up on drop +- **Safe growth operations** - `grow_filled`, `grow_zeroed`, `grow_from_slice`, and more +- **Thread-safe** - all memory types implement `Send + Sync` + +## Installation + +Add to your `Cargo.toml`: + +```toml +[dependencies] +platform-mem = "0.1" +``` + +**Note:** This crate requires nightly Rust for the `allocator_api` feature. + +```bash +rustup override set nightly +``` + +## Usage + +### Basic Example with Global Allocator + +```rust,ignore +#![feature(allocator_api)] + +use platform_mem::{Global, RawMem}; + +fn main() -> Result<(), platform_mem::Error> { + let mut mem = Global::::new(); + + // Grow memory and fill with value + mem.grow_filled(10, 42)?; + assert_eq!(mem.allocated(), &[42u64; 10]); + + // Grow more from a slice + mem.grow_from_slice(&[1, 2, 3])?; + assert_eq!(mem.allocated().len(), 13); + + // Shrink by 5 elements + mem.shrink(5)?; + assert_eq!(mem.allocated().len(), 8); + + Ok(()) +} +``` + +### Memory-Mapped File Storage + +```rust,ignore +#![feature(allocator_api)] + +use platform_mem::{FileMapped, RawMem}; + +fn main() -> Result<(), platform_mem::Error> { + // Create memory mapped to a file + let mut mem = FileMapped::::from_path("data.bin")?; + + // Data persists across program runs + unsafe { + mem.grow_zeroed(1000)?; + } + + // Modify the memory + mem.allocated_mut()[0] = 123; + + Ok(()) +} +``` + +### Temporary File Storage + +```rust,ignore +#![feature(allocator_api)] + +use platform_mem::{TempFile, RawMem}; + +fn main() -> Result<(), platform_mem::Error> { + // Anonymous temporary file - cleaned up on drop + let mut mem = TempFile::::new()?; + + mem.grow_from_slice(b"hello world")?; + assert_eq!(mem.allocated(), b"hello world"); + + Ok(()) +} +``` + +### Generic Code with `RawMem` + +```rust,ignore +#![feature(allocator_api)] + +use platform_mem::RawMem; + +fn process_data>(mem: &mut M) -> Result<(), platform_mem::Error> { + mem.grow_filled(100, 0)?; + + for (i, slot) in mem.allocated_mut().iter_mut().enumerate() { + *slot = i as u32; + } + + Ok(()) +} +``` + +### Type-Erased Memory with `ErasedMem` + +```rust,ignore +#![feature(allocator_api)] + +use platform_mem::{ErasedMem, Global, RawMem}; + +fn main() { + // Use dynamic dispatch when the memory type isn't known at compile time + let mem: Box + Send + Sync> = + Box::new(Global::::new()); +} +``` + +## API Overview + +### `RawMem` Trait + +The core trait providing memory operations: + +| Method | Description | +|--------|-------------| +| `allocated()` | Returns a slice of the initialized memory | +| `allocated_mut()` | Returns a mutable slice of the initialized memory | +| `grow(addition, fill)` | Grows memory by `addition` elements with custom initialization | +| `shrink(cap)` | Shrinks memory by `cap` elements | +| `grow_filled(cap, value)` | Grows and fills with cloned values | +| `grow_zeroed(cap)` | Grows and zero-initializes (unsafe for non-zeroable types) | +| `grow_from_slice(src)` | Grows and copies from a slice | +| `grow_with(addition, f)` | Grows and initializes with a closure | + +### Memory Types + +| Type | Description | +|------|-------------| +| `Global` | Uses Rust's global allocator | +| `System` | Uses the system allocator | +| `Alloc` | Generic over any `Allocator` | +| `FileMapped` | Memory-mapped file storage | +| `TempFile` | Temporary file-backed memory | + +## Error Handling + +The crate defines an `Error` enum with these variants: + +- `CapacityOverflow` - Requested capacity exceeds `isize::MAX` bytes +- `OverGrow` - Tried to grow more than available space +- `AllocError` - Allocator failed to allocate/reallocate +- `System` - I/O error from file operations + +## License + +This project is released into the public domain under the [Unlicense](LICENSE). + +## Related Projects + +- [doublets-rs](https://github.com/linksplatform/doublets-rs) - Doublet links data structure using this memory library +- [LinksPlatform](https://github.com/linksplatform) - The Links Platform organization From 6da3dbe1c260afae5fe773ca8760d914439558c9 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 27 Dec 2025 14:55:59 +0100 Subject: [PATCH 3/5] Revert "Initial commit with task details" This reverts commit aaa789644306bafb9d19b476f0a50807b0b981f0. --- CLAUDE.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index a3350e9..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,5 +0,0 @@ -Issue to solve: https://github.com/linksplatform/mem-rs/issues/20 -Your prepared branch: issue-20-f2de19c1454e -Your prepared working directory: /tmp/gh-issue-solver-1766843472317 - -Proceed. \ No newline at end of file From a56409ea00408631bf0e2d57713b84f6f7680b39 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 27 Dec 2025 15:09:15 +0100 Subject: [PATCH 4/5] Fix MaybeUninit API for modern nightly Rust MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MaybeUninit::write_slice_cloned and MaybeUninit::slice_assume_init_mut functions were removed in recent nightly Rust. These have been replaced with inherent methods on [MaybeUninit] slices that are now stable in Rust 1.93.0: - MaybeUninit::write_slice_cloned(slice, src) -> slice.write_clone_of_slice(src) - MaybeUninit::slice_assume_init_mut(slice) -> slice.assume_init_mut() Also removed stabilized feature flags: - unchecked_math (stable since 1.79.0) - maybe_uninit_slice (stable since 1.93.0) - inline_const (stable since 1.79.0) - maybe_uninit_write_slice (stable since 1.93.0) Added example scripts in experiments/ to verify README examples work correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- Cargo.toml | 12 ++++++++++++ experiments/readme_example1.rs | 22 ++++++++++++++++++++++ experiments/readme_example2.rs | 14 ++++++++++++++ experiments/readme_example3.rs | 26 ++++++++++++++++++++++++++ src/lib.rs | 4 ---- src/raw_mem.rs | 12 ++++++------ src/raw_place.rs | 2 +- 7 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 experiments/readme_example1.rs create mode 100644 experiments/readme_example2.rs create mode 100644 experiments/readme_example3.rs diff --git a/Cargo.toml b/Cargo.toml index b2f498e..0e16487 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,15 @@ thiserror = "1.0" paste = "1.0" quickcheck = "1.0" quickcheck_macros = "1.0" + +[[example]] +name = "readme_example1" +path = "experiments/readme_example1.rs" + +[[example]] +name = "readme_example2" +path = "experiments/readme_example2.rs" + +[[example]] +name = "readme_example3" +path = "experiments/readme_example3.rs" diff --git a/experiments/readme_example1.rs b/experiments/readme_example1.rs new file mode 100644 index 0000000..4e9b046 --- /dev/null +++ b/experiments/readme_example1.rs @@ -0,0 +1,22 @@ +#![feature(allocator_api)] + +use platform_mem::{Global, RawMem}; + +fn main() -> Result<(), platform_mem::Error> { + let mut mem = Global::::new(); + + // Grow memory and fill with value + mem.grow_filled(10, 42)?; + assert_eq!(mem.allocated(), &[42u64; 10]); + + // Grow more from a slice + mem.grow_from_slice(&[1, 2, 3])?; + assert_eq!(mem.allocated().len(), 13); + + // Shrink by 5 elements + mem.shrink(5)?; + assert_eq!(mem.allocated().len(), 8); + + println!("Example 1 passed: Basic memory operations work!"); + Ok(()) +} diff --git a/experiments/readme_example2.rs b/experiments/readme_example2.rs new file mode 100644 index 0000000..79fb491 --- /dev/null +++ b/experiments/readme_example2.rs @@ -0,0 +1,14 @@ +#![feature(allocator_api)] + +use platform_mem::{TempFile, RawMem}; + +fn main() -> Result<(), platform_mem::Error> { + // Anonymous temporary file - cleaned up on drop + let mut mem = TempFile::::new()?; + + mem.grow_from_slice(b"hello world")?; + assert_eq!(mem.allocated(), b"hello world"); + + println!("Example 2 passed: TempFile storage works!"); + Ok(()) +} diff --git a/experiments/readme_example3.rs b/experiments/readme_example3.rs new file mode 100644 index 0000000..92026b9 --- /dev/null +++ b/experiments/readme_example3.rs @@ -0,0 +1,26 @@ +#![feature(allocator_api)] + +use platform_mem::RawMem; + +fn process_data>(mem: &mut M) -> Result<(), platform_mem::Error> { + mem.grow_filled(100, 0)?; + + for (i, slot) in mem.allocated_mut().iter_mut().enumerate() { + *slot = i as u32; + } + + Ok(()) +} + +fn main() -> Result<(), platform_mem::Error> { + let mut mem = platform_mem::Global::::new(); + process_data(&mut mem)?; + + // Verify the data + for (i, slot) in mem.allocated().iter().enumerate() { + assert_eq!(*slot, i as u32); + } + + println!("Example 3 passed: Generic code with RawMem works!"); + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index a28b03c..446d51e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,8 @@ #![feature( allocator_api, - unchecked_math, - maybe_uninit_slice, slice_ptr_get, ptr_as_uninit, - inline_const, slice_range, - maybe_uninit_write_slice, unboxed_closures, fn_traits )] diff --git a/src/raw_mem.rs b/src/raw_mem.rs index 66e4138..ab1dcd3 100644 --- a/src/raw_mem.rs +++ b/src/raw_mem.rs @@ -58,7 +58,7 @@ pub trait RawMem { /// # Safety /// Caller must guarantee that `fill` makes the uninitialized part valid for - /// [`MaybeUninit::slice_assume_init_mut`] + /// [`assume_init_mut`](prim@slice#method.assume_init_mut) /// /// ### Incorrect usage /// ```no_run @@ -232,7 +232,7 @@ pub trait RawMem { let Range { start, end } = slice::range(range, ..self.allocated().len()); unsafe { self.grow(end - start, |_, (within, uninit)| { - MaybeUninit::write_slice_cloned(uninit, &within[start..end]); + uninit.write_clone_of_slice(&within[start..end]); }) } } @@ -243,7 +243,7 @@ pub trait RawMem { { unsafe { self.grow(src.len(), |_, (_, uninit)| { - MaybeUninit::write_slice_cloned(uninit, src); + uninit.write_clone_of_slice(src); }) } } @@ -396,9 +396,9 @@ pub mod uninit { // SAFETY: this raw slice will contain only initialized objects // that's why, it is allowed to drop it. unsafe { - ptr::drop_in_place(MaybeUninit::slice_assume_init_mut( - self.slice.get_unchecked_mut(..self.init), - )); + ptr::drop_in_place( + self.slice.get_unchecked_mut(..self.init).assume_init_mut(), + ); } } } diff --git a/src/raw_place.rs b/src/raw_place.rs index 1dfa8a6..646ac8d 100644 --- a/src/raw_place.rs +++ b/src/raw_place.rs @@ -73,7 +73,7 @@ impl RawPlace { self.len = cap; // `len` is same `cap` only if `uninit` was init - MaybeUninit::slice_assume_init_mut(uninit) + uninit.assume_init_mut() } pub fn shrink_to(&mut self, cap: usize) { From 1d553ebb42fba95b53cb9bcfac6fd0491b891b20 Mon Sep 17 00:00:00 2001 From: konard Date: Sat, 27 Dec 2025 15:39:58 +0100 Subject: [PATCH 5/5] Fix documentation link for std::mem::zeroed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rustdoc warning for an unresolved link to `mem::zeroed` has been fixed by changing it to `std::mem::zeroed` which is the correct path. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/raw_mem.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw_mem.rs b/src/raw_mem.rs index ab1dcd3..4cdaade 100644 --- a/src/raw_mem.rs +++ b/src/raw_mem.rs @@ -128,7 +128,7 @@ pub trait RawMem { } /// # Safety - /// [`Item`] must satisfy [initialization invariant][inv] for [`mem::zeroed`] + /// [`Item`] must satisfy [initialization invariant][inv] for [`std::mem::zeroed`] /// /// [`Item`]: Self::Item /// [inv]: MaybeUninit#initialization-invariant