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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
#![no_std]
#![feature(raw)]
#![feature(specialization)]

#![allow(incomplete_features)]
#![deny(missing_docs)]

//! An (extended) implementation of the proposal of [RFC 2580](https://github.com/rust-lang/rfcs/pull/2580).
//!
//! #   Purpose.
//!
//! The purpose of this crate is dual:
//!
//! 1.  The ability to take apart a fat pointer, and put it back together later.
//! 2.  The ability to do so for any pointer, be it a pointer to trait, slice, or a regular `Sized` type.
//!
//! The second ability, in particular, is not brushed upon by RFC 2580 as far as I can tell, yet is critical in
//! enabling uniform handling of `*const T` regardless of the nature of `T`.
//!
//! #   Usage.
//!
//! The traits defined by this crate -- `Pointee` and `MetaData` -- are already implemented for all types, and are
//! generally not expected to be used by themselves apart from bounds.
//!
//! Instead, the high-level API of this crate are the two functions:
//!
//! -   `into_raw_parts`, which splits a pointer into meta-data and pointer-to-data parts.
//! -   `from_raw_parts`, which joins back the two parts to recreate a (possibly fat) pointer.
//!
//! #   Safety.
//!
//! This crate operates entirely in terms of pointers; the operations it performs are safe even in the presence of
//! dangling pointers, or null pointers.
//!
//! In return, whether the pointers it recreates are safe to dereference or not hinges entirely on whether the pointer
//! to data used to recreate the full pointer is safe to dereference.
//!
//! #   Example.
//!
//! A simple example showing how to take apart a pointer and recreate it.
//!
//! ```
//! use rfc2580::{from_raw_parts, into_raw_parts};
//!
//! let mut array = [1; 4];
//! let slice = &mut array[..];
//! let (meta, data) = into_raw_parts(slice as *mut [i32]);
//!
//! let reconstituted = from_raw_parts(meta, data);
//!
//! assert_eq!(slice as *mut [i32], reconstituted);
//! ```

use core::{marker::PhantomData, mem, ptr::NonNull, raw::TraitObject};

/// SizedMetaData.
///
/// The meta-data information associated to a pointer to a Sized T.
///
/// This is a Zero-Sized Type, as there is no such meta-data information. It is useful for uniform handling of T
/// regardless of sizedness.
pub struct SizedMetaData<T>(PhantomData<fn(T) -> T>);

impl<T> Clone for SizedMetaData<T> {
    fn clone(&self) -> Self { *self }
}

impl<T> Copy for SizedMetaData<T> {}


/// DynMetaData.
///
/// The meta-data information associated to a pointer to a trait object.
pub struct DynMetaData<T: ?Sized>(*mut (), PhantomData<fn(T) -> T>);

impl<T: ?Sized> Clone for DynMetaData<T> {
    fn clone(&self) -> Self { *self }
}

impl<T: ?Sized> Copy for DynMetaData<T> {}


/// SliceMetaData.
///
/// The meta-data information associated to a pointer to a slice object.
pub struct SliceMetaData<T: ?Sized>(usize, PhantomData<fn(T) -> T>);

impl<T: ?Sized> Clone for SliceMetaData<T> {
    fn clone(&self) -> Self { *self }
}

impl<T: ?Sized> Copy for SliceMetaData<T> {}


/// MetaData.
///
/// A trait representing the MetaData associated to a pointer.
pub trait MetaData: Sized {
    /// The type of the pointee of which `self` represents the meta-data.
    type Pointee : ?Sized;

    /// Joins a meta-data and data-pointer parts into a pointer to the appropriate type.
    fn assemble(&self, data: *mut u8) -> *mut Self::Pointee;

    /// Splits any pointer into its meta-data and data-pointer parts.
    fn disassemble(ptr: *mut Self::Pointee) -> (Self, *mut u8);
}

impl<T: ?Sized> MetaData for DynMetaData<T> {
    type Pointee = T;

    fn assemble(&self, data: *mut u8) -> *mut T {
        assert!(mem::size_of::<*mut T>() == mem::size_of::<TraitObject>());

        let object = TraitObject { data: data as *mut (), vtable: self.0 as *mut () };

        unsafe { mem::transmute_copy(&object) }
    }

    fn disassemble(ptr: *mut T) -> (Self, *mut u8) {
        assert!(mem::size_of::<*mut T>() == mem::size_of::<(usize, usize)>());

        let object: TraitObject = unsafe { mem::transmute_copy(&ptr) };

        (DynMetaData(object.vtable, PhantomData), object.data as *mut u8)
    }
}

//  The slice specialization. It is dodgy, avoiding the use of from_raw_parts to
//  avoid materializing a reference in case the pointer is dangling or null.
impl<T> MetaData for SliceMetaData<T> {
    type Pointee = [T];

    fn assemble(&self, data: *mut u8) -> *mut [T] {
        debug_assert!(mem::size_of::<*mut [T]>() == mem::size_of::<(usize, usize)>());

        unsafe { mem::transmute_copy(&(data, self.0)) }
    }

    fn disassemble(ptr: *mut [T]) -> (Self, *mut u8) {
        debug_assert!(mem::size_of::<*mut [T]>() == mem::size_of::<(usize, usize)>());

        let (data, len): (usize, usize) = unsafe { mem::transmute_copy(&ptr) };

        (SliceMetaData(len, PhantomData), data as *mut u8)
    }
}

impl<T> MetaData for SizedMetaData<T> {
    type Pointee = T;

    fn assemble(&self, data: *mut u8) -> *mut T {
        data as *mut T
    }

    fn disassemble(ptr: *mut T) -> (Self, *mut u8) {
        (SizedMetaData(PhantomData), ptr as *mut u8)
    }
}


/// Pointee.
///
/// A trait associating the appropriate meta-data to a given pointer type.
pub trait Pointee {
    /// The type of the meta-data associated to pointers to `Self`.
    type MetaData: MetaData<Pointee = Self> + Clone + Copy;
}

impl<T: ?Sized> Pointee for T {
    default type MetaData = DynMetaData<T>;
}

impl<T> Pointee for [T] {
    type MetaData = SliceMetaData<T>;
}

impl<T> Pointee for T {
    type MetaData = SizedMetaData<T>;
}


/// Splits any pointer into its meta-data and data-pointer parts.
///
/// For the `NonNull` equivalent, see `into_non_null_parts`.
///
/// #   Examples
///
/// ```
/// use rfc2580::into_raw_parts;
///
/// let array = [1; 4];
/// let slice = &array[..];
/// let (meta, data) = into_raw_parts(slice as *const [i32] as *mut [i32]);
///
/// assert_eq!(slice.as_ptr() as *mut u8, data);
/// ```
pub fn into_raw_parts<T: ?Sized + Pointee>(ptr: *mut T) -> (<T as Pointee>::MetaData, *mut u8) {
    <T as Pointee>::MetaData::disassemble(ptr)
}


/// Joins a meta-data and data-pointer parts into a pointer to the appropriate type.
///
/// For the `NonNull` equivalent, see `from_non_null_parts`.
///
/// #   Examples
///
/// ```
/// use rfc2580::{from_raw_parts, into_raw_parts};
///
/// let array = [1; 4];
/// let slice = &array[..];
/// let (meta, data) = into_raw_parts(slice as *const [i32] as *mut [i32]);
///
/// let reconstituted = from_raw_parts(meta, data);
///
/// assert_eq!(slice as *const [i32] as *mut [i32], reconstituted);
/// ```
pub fn from_raw_parts<T: ?Sized, M: MetaData<Pointee = T>>(meta: M, ptr: *mut u8) -> *mut T {
    meta.assemble(ptr)
}


/// Splits any pointer into its meta-data and data-pointer parts.
///
/// For the raw pointer equivalent, see `into_raw_parts`.
///
/// #   Examples
///
/// ```
/// use core::ptr::NonNull;
/// use rfc2580::into_non_null_parts;
///
/// let mut array = [1; 4];
/// let (meta, data) = into_non_null_parts(NonNull::from(&mut array[..]));
///
/// assert_eq!(NonNull::from(&mut array[..]).cast(), data);
/// ```
pub fn into_non_null_parts<T: ?Sized + Pointee>(ptr: NonNull<T>) -> (<T as Pointee>::MetaData, NonNull<u8>) {
    let (meta, ptr) = into_raw_parts(ptr.as_ptr());
    //  Safety:
    //  -   `ptr` is non-null, hence the result is non-null.
    (meta, unsafe { NonNull::new_unchecked(ptr) })
}


/// Joins a meta-data and data-pointer parts into a pointer to the appropriate type.
///
/// For the raw pointer equivalent, see `from_raw_parts`.
///
/// #   Examples
///
/// ```
/// use core::ptr::NonNull;
/// use rfc2580::{from_non_null_parts, into_non_null_parts};
///
/// let mut array = [1; 4];
/// let (meta, data) = into_non_null_parts(NonNull::from(&mut array[..]));
///
/// let reconstituted = from_non_null_parts(meta, data);
///
/// assert_eq!(NonNull::from(&mut array[..]), reconstituted);
/// ```
pub fn from_non_null_parts<T: ?Sized, M: MetaData<Pointee = T>>(meta: M, ptr: NonNull<u8>) -> NonNull<T> {
    //  Safety:
    //  -   `ptr` is non-null, hence the result is non-null.
    unsafe { NonNull::new_unchecked(from_raw_parts(meta, ptr.as_ptr())) }
}


#[cfg(test)]
mod tests {

use core::fmt::Debug;

use super::*;

const SIZE_OF_USIZE: usize = mem::size_of::<usize>();

#[test]
fn split_join_sized() {
    let item = 42i32;
    let ptr = &item as *const _ as *mut _;

    let (meta, data) = into_raw_parts(ptr);

    assert_eq!(0, mem::size_of_val(&meta));
    assert_eq!(ptr, data as *mut i32);

    let phoenix = from_raw_parts(meta, data);

    assert_eq!(ptr, phoenix);
}

#[test]
fn split_join_slice() {
    let array = [1, 2, 3, 4];
    let slice = &array[..];
    let ptr = slice as *const [i32] as *mut [i32];

    let (meta, data) = into_raw_parts(ptr);

    assert_eq!(SIZE_OF_USIZE, mem::size_of_val(&meta));
    assert_eq!(slice.as_ptr(), data as *mut i32);

    let phoenix = from_raw_parts(meta, data);

    assert_eq!(ptr, phoenix);
}

#[test]
fn split_join_trait() {
    let item = 42i32;
    let debug = &item as &dyn Debug;
    let ptr = debug as *const dyn Debug as *mut dyn Debug;

    let (meta, data) = into_raw_parts(ptr);

    assert_eq!(SIZE_OF_USIZE, mem::size_of_val(&meta));

    let phoenix = from_raw_parts(meta, data);

    assert_eq!(ptr, phoenix);
}

}