Skip to content

Instantly share code, notes, and snippets.

@mikeyhew
Last active May 31, 2017 20:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikeyhew/635afa3155440b1e2e677070bfe6fed2 to your computer and use it in GitHub Desktop.
Save mikeyhew/635afa3155440b1e2e677070bfe6fed2 to your computer and use it in GitHub Desktop.
A Vec-like data structure for trait objects
#![feature(alloc, unsize)]
extern crate alloc;
use alloc::raw_vec::RawVec;
use std::marker::Unsize;
use std::{ptr, mem};
use std::vec;
use std::nikomatzakis::Referent;
pub struct DSTVec<T: Referent + ?Sized> {
// these aren't actual pointers, they're offets + meta, used to construct a
// pointer on the fly
pointers: Vec<(usize, T::Meta)>,
data: RawVec<u8>,
used_bytes: usize,
}
impl<T: ?Sized> DSTVec<T> {
pub fn new() -> DSTVec<T> {
let data = RawVec::new();
DSTVec {
pointers: Vec::new(),
data: data,
used_bytes: 0,
}
}
pub fn push<U: Unsize<T>>(&mut self, value: U) {
let align = mem::align_of::<U>();
let gap = self.used_bytes + align - (self.used_bytes % align);
let size = mem::size_of::<U>();
self.data.reserve(self.used_bytes, gap + size);
self.pointers.reserve(1);
let offset = self.used_bytes + gap;
let (_, meta) = Referent::disassemble(&value);
unsafe {
let back = self.data.ptr().offset(offset as isize);
ptr::copy_nonoverlapping(&value, back as *mut U, 1);
}
self.pointers.push((offset, meta));
self.used_bytes += gap + size;
mem::forget(value);
}
}
impl<T> Drop for DSTVec<T> {
fn drop(&mut self) {
for &(offset, meta) in self.pointers.iter() {
unsafe {
let pointer = Referent::assemble(self.data.ptr().offset(offset), meta);
ptr::drop_in_place(pointer);
}
}
}
}
/// this implementation would be improved with &move references, too.
impl<T: ?Sized> IntoIterator for DSTVec<T> {
type Item = *mut T;
type IntoIter = IntoIter;
fn into_iter(mut self) -> IntoIter {
let pointers = mem::replace(&mut self.pointers, Vec::new());
let data = mem::replace(&mut self.data, RawVec::new());
IntoIter {
pointers: pointers.into_iter(),
data: data,
}
}
}
pub struct IntoIter<T: ?Sized> {
pointers: vec::IntoIter<(usize, T::Meta)>,
data: RawVec<u8>,
}
impl<T: ?Sized> Iterator for IntoIter {
type Item = *mut T;
fn next(&mut self) -> Option<*mut T> {
self.pointers.next().map(|(offset, meta)| {
unsafe {
Referent::assemble(self.data.ptr().offset(offset), meta)
}
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.pointers.size_hint()
}
}
impl ExactSizeIterator for IntoIter {}
impl Drop for IntoIter {
fn drop(&mut self) {
let offsets_with_metas = mem::replace(&mut self.pointers, Vec::new().into_iter());
for (offset, meta) in offsets_with_metas {
unsafe {
let pointer = Referent::assemble(self.data.ptr().offset(offset), meta);
ptr::drop_in_place(pointer);
}
}
}
}
pub trait FnOnceUnsafe {
unsafe fn call_once_unsafe(&mut self);
}
impl<F: FnOnce()> FnOnceUnsafe for F {
unsafe fn call_once_unsafe(&mut self) {
ptr::read(self)()
}
}
type Callbacks = DSTVec<FnOnceUnsafe>;
#[test]
fn test_closures() {
let mut foo = 5;
let mut bar = 10;
let mut callbacks = Callbacks::new();
callbacks.push(|| {
foo = 20;
});
callbacks.push(|| {
bar = 30;
});
for callback in callbacks {
unsafe {
(*callback).call_once_unsafe();
}
}
assert_eq!(foo, 20);
assert_eq!(bar, 30);
}
fn main() {
let mut callbacks = Callbacks::new();
callbacks.push(|| {
println!("Hello");
});
callbacks.push(|| {
println!("World!");
});
for callback in callbacks {
unsafe {
(*callback).call_once_unsafe();
}
}
}
@mikeyhew
Copy link
Author

This gist has been moved into a repository

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment