Skip to content

Commit 5ade3fe

Browse files
committed
Add a ThinBox library as a libcore test for pointer metadata APIs
1 parent 21ceebf commit 5ade3fe

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

Diff for: library/core/tests/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
#![feature(slice_group_by)]
8080
#![feature(trusted_random_access)]
8181
#![deny(unsafe_op_in_unsafe_fn)]
82+
#![cfg_attr(not(bootstrap), feature(unsize))]
8283

8384
extern crate test;
8485

Diff for: library/core/tests/ptr.rs

+112
Original file line numberDiff line numberDiff line change
@@ -555,3 +555,115 @@ fn from_raw_parts() {
555555
assert_eq!(ptr::from_raw_parts_mut(address, 5), slice_ptr.as_ptr());
556556
assert_eq!(NonNull::from_raw_parts(NonNull::new(address).unwrap(), 5), slice_ptr);
557557
}
558+
559+
#[test]
560+
#[cfg(not(bootstrap))]
561+
fn thin_box() {
562+
let foo = ThinBox::<dyn Display>::new(4);
563+
assert_eq!(foo.to_string(), "4");
564+
drop(foo);
565+
let bar = ThinBox::<dyn Display>::new(7);
566+
assert_eq!(bar.to_string(), "7");
567+
568+
// A slightly more interesting library that could be built on top of metadata APIs.
569+
//
570+
// * It could be generalized to any `T: ?Sized` (not just trait object)
571+
// if `{size,align}_of_for_meta<T: ?Sized>(T::Metadata)` are added.
572+
// * Constructing a `ThinBox` without consuming and deallocating a `Box`
573+
// requires either the unstable `Unsize` marker trait,
574+
// or the unstable `unsized_locals` language feature,
575+
// or taking `&dyn T` and restricting to `T: Copy`.
576+
577+
use std::alloc::*;
578+
use std::marker::PhantomData;
579+
580+
struct ThinBox<T>
581+
where
582+
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
583+
{
584+
ptr: NonNull<DynMetadata<T>>,
585+
phantom: PhantomData<T>,
586+
}
587+
588+
impl<T> ThinBox<T>
589+
where
590+
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
591+
{
592+
pub fn new<Value: std::marker::Unsize<T>>(value: Value) -> Self {
593+
let unsized_: &T = &value;
594+
let meta = metadata(unsized_);
595+
let meta_layout = Layout::for_value(&meta);
596+
let value_layout = Layout::for_value(&value);
597+
let (layout, offset) = meta_layout.extend(value_layout).unwrap();
598+
// `DynMetadata` is pointer-sized:
599+
assert!(layout.size() > 0);
600+
// If `ThinBox<T>` is generalized to any `T: ?Sized`,
601+
// handle ZSTs with a dangling pointer without going through `alloc()`,
602+
// like `Box<T>` does.
603+
unsafe {
604+
let ptr = NonNull::new(alloc(layout))
605+
.unwrap_or_else(|| handle_alloc_error(layout))
606+
.cast::<DynMetadata<T>>();
607+
ptr.as_ptr().write(meta);
608+
ptr.cast::<u8>().as_ptr().add(offset).cast::<Value>().write(value);
609+
Self { ptr, phantom: PhantomData }
610+
}
611+
}
612+
613+
fn meta(&self) -> DynMetadata<T> {
614+
unsafe { *self.ptr.as_ref() }
615+
}
616+
617+
fn layout(&self) -> (Layout, usize) {
618+
let meta = self.meta();
619+
Layout::for_value(&meta).extend(meta.layout()).unwrap()
620+
}
621+
622+
fn value_ptr(&self) -> *const T {
623+
let (_, offset) = self.layout();
624+
let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
625+
ptr::from_raw_parts(data_ptr.cast(), self.meta())
626+
}
627+
628+
fn value_mut_ptr(&mut self) -> *mut T {
629+
let (_, offset) = self.layout();
630+
// FIXME: can this line be shared with the same in `value_ptr()`
631+
// without upsetting Stacked Borrows?
632+
let data_ptr = unsafe { self.ptr.cast::<u8>().as_ptr().add(offset) };
633+
from_raw_parts_mut(data_ptr.cast(), self.meta())
634+
}
635+
}
636+
637+
impl<T> std::ops::Deref for ThinBox<T>
638+
where
639+
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
640+
{
641+
type Target = T;
642+
643+
fn deref(&self) -> &T {
644+
unsafe { &*self.value_ptr() }
645+
}
646+
}
647+
648+
impl<T> std::ops::DerefMut for ThinBox<T>
649+
where
650+
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
651+
{
652+
fn deref_mut(&mut self) -> &mut T {
653+
unsafe { &mut *self.value_mut_ptr() }
654+
}
655+
}
656+
657+
impl<T> std::ops::Drop for ThinBox<T>
658+
where
659+
T: ?Sized + Pointee<Metadata = DynMetadata<T>>,
660+
{
661+
fn drop(&mut self) {
662+
let (layout, _) = self.layout();
663+
unsafe {
664+
drop_in_place::<T>(&mut **self);
665+
dealloc(self.ptr.cast().as_ptr(), layout);
666+
}
667+
}
668+
}
669+
}

0 commit comments

Comments
 (0)