Last active
September 20, 2019 16:22
-
-
Save delbonis/81ab4c57f3ab9ee6ddf5c61f54a8eff7 to your computer and use it in GitHub Desktop.
context memory hacks?
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::mem; | |
struct AllocInfo { | |
ptr: *mut (), | |
free_fn: fn(*mut ()), | |
} | |
pub struct Context { | |
allocs: Vec<AllocInfo>, | |
} | |
impl Context { | |
pub fn new() -> Context { | |
Context { allocs: Vec::new() } | |
} | |
pub fn save<'c, 'd, T>(&'c mut self, v: T) -> &'d mut T | |
where | |
T: 'static, | |
'd: 'c, | |
{ | |
// Move v to the heap and then get it as a pointer. | |
let b = Box::new(v); | |
let raw_ptr = Box::into_raw(b) as *mut (); | |
// Save the descriptor that lets us free the allocs later. | |
self.allocs.push(AllocInfo { | |
ptr: raw_ptr, | |
free_fn: rebox_and_drop::<T>, | |
}); | |
// Now convert the pointer into our magic ref and return it. | |
unsafe { mem::transmute(raw_ptr) } | |
} | |
pub fn num_allocs(&self) -> usize { | |
self.allocs.len() | |
} | |
} | |
impl Drop for Context { | |
fn drop(&mut self) { | |
for a in &self.allocs { | |
(a.free_fn)(a.ptr) | |
} | |
} | |
} | |
/// This exists so that we can get a reference to a function that takes an | |
/// untyped pointer and put it in a box of some type, then drop it. Safer than | |
/// doing some hacks with ptr::drop_in_place. | |
fn rebox_and_drop<T>(p: *mut ()) { | |
unsafe { | |
// Reconstruct the box, then drop it. | |
let b: Box<T> = Box::from_raw(mem::transmute(p)); | |
mem::drop(b); | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::Context; | |
#[test] | |
fn test_basic() { | |
// init things out of order, this might confuse it? | |
let s1v = String::from("foobar"); | |
let mut ctx = Context::new(); | |
let s2 = ctx.save(String::from("barfoo")); | |
let s1 = ctx.save(s1v); | |
assert_eq!(s1[..3], s2[3..]); | |
assert_eq!(s2[..3], s1[3..]); | |
} | |
/// This tests that stack allocations made in a deeper stack frame are moved | |
/// into the context properly and can be used outside safely. | |
#[test] | |
fn test_move_out() { | |
let mut ctx = Context::new(); | |
let s1 = rearrange_str(&mut ctx, "foobar"); | |
let s2 = rearrange_str(&mut ctx, "barfoo"); | |
assert_eq!(s1[..3], s2[3..]); | |
assert_eq!(s2[..3], s1[3..]); | |
} | |
fn rearrange_str<'c, 'd>(ctx: &'c mut Context, v: &str) -> &'d String { | |
let s = String::from(v); | |
ctx.save(s) | |
} | |
#[test] | |
fn test_move_ctx() { | |
let (ctx, s) = mvctx_make_ctx(); | |
assert_eq!(ctx.num_allocs(), 1); | |
assert_eq!(&s[..], "foobar"); | |
} | |
fn mvctx_make_ctx<'d>() -> (Context, &'d String) { | |
let mut ctx = Context::new(); | |
let s = ctx.save(String::from("foobar")); | |
(ctx, s) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment