-
Notifications
You must be signed in to change notification settings - Fork 1
/
thin.rs
177 lines (139 loc) · 4.94 KB
/
thin.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#![feature(unsize)]
//! Let's create a thin pointer!
//!
//! This is a completely different design that the one in the RFC, using a middle-pointer.
mod thin {
use std::{
alloc::{self, Layout},
cmp,
fmt::{self, Debug, Formatter},
marker::{PhantomData, Unsize},
mem,
ops::{Deref, DerefMut},
ptr::{self, NonNull},
};
use rfc2580::{self, Pointee};
/// ThinBox.
///
/// A thin pointer, regardless of T.
pub struct ThinBox<T: ?Sized + Pointee> {
ptr: WithHeader<T::MetaData>,
_marker: PhantomData<fn(T) -> T>,
}
impl<T: Pointee> ThinBox<T> {
pub fn new(mut value: T) -> Self {
let meta = rfc2580::into_non_null_parts(NonNull::from(&mut value)).0;
let ptr = WithHeader::new(meta, value).expect("No allocation failure");
ThinBox { ptr, _marker: PhantomData }
}
}
impl<T: ?Sized + Pointee> ThinBox<T> {
pub fn new_unsize<S>(mut value: S) -> Self
where S: Unsize<T>
{
let meta = rfc2580::into_non_null_parts(NonNull::from(&mut value as &mut T)).0;
let ptr = WithHeader::new(meta, value).expect("No allocation failure");
ThinBox { ptr, _marker: PhantomData }
}
}
impl<T: ?Sized + Debug + Pointee> Debug for ThinBox<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", &*self)
}
}
impl<T: ?Sized + Pointee> Deref for ThinBox<T> {
type Target = T;
fn deref(&self) -> &T {
let pointer = rfc2580::from_non_null_parts(self.meta(), self.data());
unsafe { &*pointer.as_ptr() }
}
}
impl<T: ?Sized + Pointee> DerefMut for ThinBox<T> {
fn deref_mut(&mut self) -> &mut T {
let pointer = rfc2580::from_non_null_parts(self.meta(), self.data());
unsafe { &mut *pointer.as_ptr() }
}
}
impl<T: ?Sized + Pointee> Drop for ThinBox<T> {
fn drop(&mut self) {
unsafe {
let value: &mut T = &mut *self;
let value: *mut T = value as *mut T;
self.ptr.drop::<T>(value);
}
}
}
//
// Implementation details.
//
impl<T: ?Sized + Pointee> ThinBox<T> {
fn meta(&self) -> T::MetaData {
// Safety:
// - NonNull and valid.
unsafe { *self.ptr.header().as_ptr() }
}
fn data(&self) -> NonNull<u8> { self.ptr.value() }
}
struct WithHeader<H>(NonNull<u8>, PhantomData<H>);
impl<H> WithHeader<H> {
fn new<T>(header: H, value: T) -> Option<WithHeader<H>> {
let layout = Self::alloc_layout(mem::size_of::<T>(), mem::align_of::<T>());
let aligned_header_size = Self::aligned_header_size(layout.align());
unsafe {
let ptr = NonNull::new(alloc::alloc(layout))?;
// Safety:
// - The size is at least `aligned_header_size`.
let ptr = NonNull::new_unchecked(ptr.as_ptr().offset(aligned_header_size as isize));
let result = WithHeader(ptr, PhantomData);
ptr::write(result.header().as_ptr(), header);
ptr::write(result.value().as_ptr().cast(), value);
Some(result)
}
}
// Safety:
// - Assumes that `value` can be dereferenced.
unsafe fn drop<T: ?Sized>(&self, value: *mut T) {
let layout = Self::alloc_layout(mem::size_of_val(&*value), mem::align_of_val(&*value));
let aligned_header_size = Self::aligned_header_size(layout.align());
ptr::drop_in_place::<T>(value as *mut T);
alloc::dealloc(self.0.as_ptr().offset(-(aligned_header_size as isize)), layout);
}
fn header(&self) -> NonNull<H> {
// Safety:
// - At least `size_of::<H>()` bytes are allocated ahead of the pointer.
unsafe { NonNull::new_unchecked(self.0.as_ptr().offset(-(Self::header_size() as isize)) as *mut H) }
}
fn value(&self) -> NonNull<u8> { self.0 }
//
// Implementation Details
//
fn header_size() -> usize { mem::size_of::<H>() }
fn alloc_layout(value_size: usize, value_align: usize) -> Layout {
assert!(Self::header_size() <= usize::MAX / 2 + 1);
let alloc_align = cmp::max(mem::align_of::<H>(), value_align);
let alloc_size = Self::aligned_header_size(alloc_align) + value_size;
// Safety:
// - `align` is not zero and a power of two, as guaranteed by `mem::align_of`.
// - The size can be rounded up to `align` without overflow, or `H` is way too big.
unsafe { Layout::from_size_align_unchecked(alloc_size, alloc_align) }
}
fn aligned_header_size(alloc_align: usize) -> usize { cmp::max(Self::header_size(), alloc_align) }
}
} // mod thin.
use std::mem;
use thin::ThinBox;
//
// Showing it off.
//
fn main() {
{
let thin = ThinBox::new(42i32);
assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin));
println!("{:?}", thin);
}
{
let thin_slice = ThinBox::<[i32]>::new_unsize([1, 2, 3, 4]);
assert_eq!(mem::size_of::<*const i32>(), mem::size_of_val(&thin_slice));
println!("{:?}", thin_slice);
}
}