Skip to content

Instantly share code, notes, and snippets.

@delbonis
Last active September 20, 2019 16:22
Show Gist options
  • Save delbonis/81ab4c57f3ab9ee6ddf5c61f54a8eff7 to your computer and use it in GitHub Desktop.
Save delbonis/81ab4c57f3ab9ee6ddf5c61f54a8eff7 to your computer and use it in GitHub Desktop.
context memory hacks?
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