3 releases
0.1.0-preview3 | Sep 26, 2020 |
---|---|
0.1.0-preview | Sep 25, 2020 |
#424 in Memory management
36KB
436 lines
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
~37K SLoC