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
/*!
provides a `Vec`-like data structure for dynamically sized types, storing them all together in a single buffer.
# Questions
1. I tried to make this a generic struct, `DSTVec<T: ?Sized>`, but that didn't work because I couldn't cast a `&T`
to a raw `TraitObject`, so had to fall back to a macro. Is there a way to get around that? Maybe someday there will
be an API to split a pointer into its (address, the_rest) components, in which case I think a generic struct would
work. Then this implementation would also work for slices, which is pretty cool.
2. When I try to use the `declare_dstvec` macro from another crate, the compiler complains that it's using unstable
features, and that I need to declare them at the top of that crate, too. Is there a way around that?
3. The test doesn't pass. In particular, I think Callbacks::push requires that its argument is 'static. How to fix?
4. The closures being pushed to `callbacks` in `main` don't actually capture anything from their environment, so they're
basically just function pointers and are zero-sized. I tried tweaking things so that they do capture something, and I
realized that it didn't actually work. So that's fun...
*/
#![feature(alloc, raw, unsize)]
extern crate alloc;
pub use alloc::raw_vec::RawVec;
pub use std::marker::Unsize;
pub use std::raw::TraitObject;
#[macro_export]
macro_rules! declare_dstvec {
($name:ident, $Trait:ty) => {
use ::std::{ptr, mem};
use $crate::Unsize;
use $crate::RawVec;
use $crate::TraitObject;
use ::std::vec;
pub struct $name {
pointers: Vec<(usize, *mut ())>,
data: RawVec<u8>,
used_bytes: usize,
}
impl $name {
pub fn new() -> $name {
let data = RawVec::new();
$name {
pointers: Vec::new(),
data: data,
used_bytes: 0,
}
}
pub fn push<U: Unsize<$Trait>>(&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);
unsafe {
let back = self.data.ptr().offset((gap + size) as isize);
ptr::copy_nonoverlapping(&value, back as *mut U, 1);
}
let offset = self.used_bytes + gap;
let vtable = unsafe {
let fat_pointer = &value as &$Trait;
let trait_object: TraitObject = mem::transmute(fat_pointer);
trait_object.vtable
};
self.pointers.push((offset, vtable));
self.used_bytes += gap + size;
mem::forget(value);
}
}
impl Drop for $name {
fn drop(&mut self) {
for &(offset, vtable) in self.pointers.iter() {
unsafe {
let pointer = construct_fat_pointer(self.data.ptr(), offset, vtable);
ptr::drop_in_place(pointer);
}
}
}
}
impl IntoIterator for $name {
type Item = *mut $Trait;
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 {
pointers: vec::IntoIter<(usize, *mut ())>,
data: RawVec<u8>,
}
impl Iterator for IntoIter {
type Item = *mut $Trait;
fn next(&mut self) -> Option<*mut $Trait> {
self.pointers.next().map(|(offset, vtable)| {
unsafe {
construct_fat_pointer(self.data.ptr(), offset, vtable)
}
})
}
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_vtables = mem::replace(&mut self.pointers, Vec::new().into_iter());
for (offset, vtable) in offsets_with_vtables {
unsafe {
let pointer = construct_fat_pointer(self.data.ptr(), offset, vtable);
ptr::drop_in_place(pointer);
}
}
}
}
unsafe fn construct_fat_pointer(base: *mut u8, offset: usize, vtable: *mut ()) -> *mut $Trait {
mem::transmute(TraitObject {
data: base.offset(offset as isize) as *mut (),
vtable: vtable,
})
}
};
}
use std::ptr;
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)()
}
}
mod callbacks {
use super::FnOnceUnsafe;
declare_dstvec!(Callbacks, FnOnceUnsafe);
}
use self::callbacks::Callbacks;
#[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