#uninitialized-memory #memory #read-write #plain #ffi #utility #no-std

no-std memory_slice

A memory slice over potentialy uninitialized memory that allowes safe and borrow-checked, reads, writes and reinterpretation of data of any kind

3 releases

0.1.0-preview3 Sep 26, 2020
0.1.0-preview Sep 25, 2020

#296 in Memory management

MIT/Apache

36KB
436 lines

LICENSE LICENSE Documentation Crates.io Version

Memory-Slice

memory_slice that can alias any kind of data, and provides an API that allow user to read, write and borrow data of any type.

Features

  • Read and write data of any type with alignment check.
  • Write data in the buffer and get a smart pointer that will destroy the object in the buffer when the smart pointer is droped.
  • Use the static borrow-checker to ensure that no write can be performed on any borrowed data in the buffer.
  • Provides an API to create statically sized buffer on the stack with alignment requirement.
  • No std.

Example

Any kind of data can be viewed as constant memory slice:

use memory_slice::AsMemory;
let v: [u8;4] = [1,1,1,1];
//as_memory return a &memory_slice
assert_eq!(unsafe{v.as_memory().read::<i32>()},1 + (1<<8) + (1<<16) + (1<<24));

But only types that do not preserve any invariants are accessible as mutable memory slice:

This will compile:

use memory_slice::AsMutMemory;
let mut v: [u8;4] = [1,1,1,1];
//as_memory return a &memory_slice
v.as_mut_memory().write(16 as u16);

This will not compile:

use memory_slice::AsMutMemory;
use std::string::String;
let mut v = String::new();
//as_memory return a &memory_slice
v.as_mut_memory().write(16 as u16); //compilation failure

Mutable memory slices can be used to write information of any type while preserving borrow rules. The API provide also a smart pointer that will drop value created on the memory slice:

use memory_slice::{align,AsMutMemory,AsMemory};
// creates an array of 64 u8 aligned as 8: 
let mut buff = align!(8,[0 as u8;64]);

//the create an int inside the buffer and get a reference to it
let (padding, v1, remaining_buffer) = buff.as_mut_memory().write(42 as i32);
assert!(padding.is_empty());
//unsafe{buff[0]}; //error => cannot borrow buff as immutable

//use the remaining unitialized buffer to write an u64 in it:
let (padding, v2, remaining_buffer2) = remaining_buffer.write(42 as u64);
assert_eq!(padding.len(), 4);
//unsafe{remaing_buffer.read::<u8>()}; //error => cannot borrow remaining_buffer

//v1 and v2 are reference to the i32 and u64 created inside buff
assert_eq!(*v1 as u64, *v2);

{
    extern crate alloc;
    use alloc::borrow::ToOwned;

    //In what remains of the buffer, let's create a value that needs to be dropped:
    let (_padding, v3, _remaining) = remaining_buffer2.emplace("42".to_owned());

    //v3 is a smart pointer to the String created in the buffer that will drop
    //this string when it goes out of scope
    assert_eq!(*v1, v3.parse::<i32>().unwrap());
} //string refered by v3 is dropped

//buff is not anymore borrowed, so it is accessible:
assert_eq!(unsafe { buff.as_memory().read::<i32>() }, 42);

//memory slice can be indexed (!!less inoffensive than it looks)
unsafe{*buff.as_mut_memory()[2..4].as_mut_unchecked()=16 as u16};
assert_ne!(unsafe { buff.as_memory().read::<i32>() }, 42);

A macro named buffer is provided to create uninitialized buffer:

use memory_slice::buffer;
// create an uninitialized buffer of 64 bytes aligned as 8.
let mut buff = buffer!(64,8);
// buffer are dereferencable as memory_slice

//the create an int inside the buffer and get a reference to it
buff.write(42 as i32);

Dependencies

~1.5MB
~36K SLoC