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
//! Virtual method tables.
//!
//! [`dungeon_cell`][crate] uses custom vtables to track what types are stored and
//! provide methods for those stored types.

use ::core::any::TypeId;
use std::mem::MaybeUninit;

mod coerce;
mod core;

pub use self::core::*;
pub use coerce::*;

/// Basic vtable trait.
///
/// A vtable that implements this trait can act like the vtable generated for `dyn Any`.
/// All dungeon types use this trait as the basis for their type erasure.
///
/// # Safety
/// The [`TypeId`][std::any::TypeId] value returned by [`Self::type_id()`] must be
/// for the type the vtable instance represents. The following safety requirements
/// are derived from that type.
///
/// - [`Self::type_name()`] has no safety requirements. It can return any value.
///     However, it is recommended to return a string representing the type.
///
/// - [`Self::size()`] must return the size in bytes the type takes in memory.
///     This is the value returned by [`std::mem::size_of()`] for the type.
///
/// - [`Self::alignment()`] must return the alignment in bytes the type requires.
///     This is the value returned by [`std::mem::align_of()`] for the type.
///
/// - [`Self::drop_in_place()`] must drop a value of the type in place.
///     The [`std::ptr::drop_in_place()`] function is recommended to implement this.
///     Alternatively, the implementation may forget the value (aka do nothing)
///     instead of dropping.
///
/// All methods, except [`Self::drop_in_place()`], defined by this trait must not
/// panic in any situation. [`Self::drop_in_place()`] is allowed to panic as it may
/// call user defined code.
pub unsafe trait VTable: Clone {
    /// Returns the type's ID.
    fn type_id(&self) -> TypeId;

    /// Returns name of the type.
    ///
    /// This should only be used for diagnostic purposes. See the
    /// [`std` docs](https://doc.rust-lang.org/std/any/fn.type_name.html#note)
    /// for more information.
    fn type_name(&self) -> &'static str;

    /// Returns type's size in bytes.
    fn size(&self) -> usize;

    /// Returns type's required alignment in bytes.
    fn alignment(&self) -> usize;

    /// Drops the value in the given aligned buffer.
    ///
    /// # Safety
    /// The given `buffer` must follow the safety requirements given in the
    /// [`std` docs](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html#safety)
    /// for [`std::ptr::drop_in_place()`]. Additionally, the value stored in `buffer`
    /// must be a value of the type reported by [`Self::type_id()`].
    unsafe fn drop_in_place(&self, buffer: *mut MaybeUninit<u8>);
}

// Automatically implement for borrows of a vtable.
// This allows for borrows to static vtable instances to be valid.
unsafe impl<T: VTable> VTable for &T {
    fn type_id(&self) -> TypeId {
        T::type_id(self)
    }

    fn type_name(&self) -> &'static str {
        T::type_name(self)
    }

    fn size(&self) -> usize {
        T::size(self)
    }

    fn alignment(&self) -> usize {
        T::alignment(self)
    }

    unsafe fn drop_in_place(&self, buffer: *mut MaybeUninit<u8>) {
        T::drop_in_place(self, buffer)
    }
}

/// Generate vtable for type.
///
/// # Safety
/// The vtable instance [`Self::instance()`] returns must report as being for type `T`
/// when the [`VTable::type_id()`] method is called on it.
pub unsafe trait VTableOf<T>: VTable {
    /// Get an instance of the vtable for the type `T`.
    fn instance() -> Self;
}

/// Generate vtable for type in a const context.
///
/// # Safety
/// The vtable instance [`Self::INSTANCE`] must report as being for type `T`
/// when the [`VTable::type_id()`] method is called on it.
pub unsafe trait ConstVTableOf<T>: VTableOf<T> {
    /// Instance of the vtable for the type `T`.
    const INSTANCE: Self;
}