Last active
December 20, 2016 12:15
-
-
Save diwic/42f5eb598389fa4546f0ea857147086e to your computer and use it in GitHub Desktop.
Safe alloca / stack slice abstraction - I hope?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::marker::PhantomData; | |
use std::{ptr, ops, slice, mem}; | |
#[inline(always)] | |
fn stack_reserve<T>(count: usize) -> *mut T { | |
// This is for test purposes only - real version should call an alloca intrinsic instead | |
let mut v = Vec::with_capacity(count); | |
let z = v.as_mut_ptr(); | |
mem::forget(v); | |
z | |
} | |
pub struct StackSlice<'a, T>(*mut T, usize, PhantomData<(T, &'a mut ())>); | |
impl<'a, T> StackSlice<'a, T> { | |
// Only to be called by macro | |
pub unsafe fn make<'b>(z: &'b mut *mut T, count: usize) -> StackSlice<'b, T> { | |
StackSlice(*z, count, PhantomData) | |
} | |
} | |
macro_rules! stack_slice { | |
// This macro creates a stack slice and initializes it with "val" | |
($i: ident = [$val: expr; $count: expr]) => { | |
let c = $count; | |
let mut secret_variable = stack_reserve(c); | |
for idx in 0..c { | |
let v = $val; | |
unsafe { ptr::write(secret_variable.offset(idx as isize), v) } | |
} | |
let mut $i = unsafe { StackSlice::make(&mut secret_variable, c) }; | |
$i = $i; // Hide unused_mut warning | |
}; | |
// This macro creates a stack slice and initializes it with values from an | |
// ExactSizeIterator. | |
($i: ident: [$t: ty] = $iter: expr) => { | |
let mut iter: &mut ::std::iter::ExactSizeIterator<Item=$t> = &mut $iter; | |
let c = iter.len(); | |
let mut secret_variable = stack_reserve(c); | |
for idx in 0..c { | |
let v = iter.next().unwrap(); | |
unsafe { ptr::write(secret_variable.offset(idx as isize), v) } | |
} | |
let mut $i = unsafe { StackSlice::make(&mut secret_variable, c) }; | |
$i = $i; // Hide unused_mut warning | |
}; | |
} | |
impl<'a, T> ops::Deref for StackSlice<'a, T> { | |
type Target = [T]; | |
fn deref(&self) -> &[T] { unsafe { slice::from_raw_parts(self.0, self.1) } } | |
} | |
impl<'a, T> ops::DerefMut for StackSlice<'a, T> { | |
fn deref_mut(&mut self) -> &mut [T] { unsafe { slice::from_raw_parts_mut(self.0, self.1) } } | |
} | |
impl<'a, T> Drop for StackSlice<'a, T> { | |
fn drop(&mut self) { | |
for qq in &mut **self { unsafe { ptr::drop_in_place(qq) }} | |
} | |
} | |
fn test() { | |
stack_slice!(f = [String::from("Mooh"); 12]); | |
f[0] = "Hi".into(); | |
assert_eq!(&*f[0], "Hi"); | |
assert_eq!(&*f[11], "Mooh"); | |
stack_slice!(g: [i32] = (0..57)); | |
assert_eq!(g.len(), 57); | |
assert_eq!(g[15], 15); | |
} | |
/* | |
// Uncomment this function to see that compilation fails when we try to | |
// return a stack slice from a function. | |
fn compile_fail<'a>() -> StackSlice<'a, u8> { | |
stack_slice!(f = [5u8; 14]); | |
f | |
} | |
*/ | |
fn main () { | |
test(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment