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
use core::{any::TypeId, mem::MaybeUninit};
use std::ptr::{addr_of_mut, addr_of};

use crate::{never::Never, ValidAlignment, Alignment, align::AlignedBuffer};

fn never_type_id() -> TypeId {
    core::any::TypeId::of::<Never>()
}

/// Core of a dungeon primitive.
///
/// Any static type with size less than or equal to `SIZE` bytes and alignment
/// requirements less than or equal to `2^ALIGN` bytes can be stored in the core.
///
/// By default, this type uses a `8` byte alignment as that
/// is the most common alignment for x64 machines.
pub struct DungeonCore<const SIZE: usize, const ALIGN: u8 = 3>
where
    Alignment<ALIGN>: ValidAlignment,
{
    /// The type of the value `data` contains.
    ///
    /// If `current` is the TypeId of a uninhabited type
    /// then `data` must not be used to make an instance of it.
    current: TypeId,

    /// A function to cause the value in `data` to be dropped.
    drop_impl: fn(&mut DungeonCore<SIZE, ALIGN>),

    /// Value of whatever type `current` says.
    ///
    /// If `current` is a uninhabited type then `data` cannot be used
    /// to make an instance of it.
    buffer: AlignedBuffer<SIZE, ALIGN>,
}

impl<const SIZE: usize, const ALIGN: u8> core::fmt::Debug for DungeonCore<SIZE, ALIGN> 
where
    Alignment<ALIGN>: ValidAlignment,
{
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("DungeonCore")
         .field("current_type", &if self.is_empty() { None } else { Some(self.current) })
         .finish()
    }
}


/// Drop the value inside a `DungeonCore` by using the `take` method.
/// Then immediately dropping the return. If the dungeon core was empty
/// then this does nothing.
fn drop_t<T, const SIZE: usize, const ALIGN: u8>(core: &mut DungeonCore<SIZE, ALIGN>)
where
    T: 'static,
    Alignment<ALIGN>: ValidAlignment,
{
    let _ = core.take::<T>();
}

impl<const SIZE: usize, const ALIGN: u8> DungeonCore<SIZE, ALIGN> 
where
    Alignment<ALIGN>: ValidAlignment,
{
    /// Create a empty dungeon core.
    pub fn new() -> Self {
        Self {
            current: never_type_id(),
            drop_impl: drop_t::<Never, SIZE, ALIGN>,
            buffer: AlignedBuffer::new(),
        }
    }

    /// Store a value of type `T`.
    ///
    /// If `T` can not be stored by this dungeon core do to
    /// size or alignment requirements a compiler error or
    /// runtime panic will be generated. This behavior is
    /// controlled by the `assert_runtime` feature.
    ///
    /// If the dungeon core contains a value already it will
    /// be dropped.
    #[track_caller]
    pub fn store<T: 'static>(&mut self, value: T) {
        // Assert that this dungeon core can store a value of `T`.
        crate::can_store::assert_can_store::<SIZE, ALIGN, T>();

        // Drop the current value.
        // Does nothing if already empty.
        let drop_old = self.drop_impl;
        drop_old(self);

        // SAFETY:
        // The call do `assert_can_store` makes the `store_unchecked` requirements
        // satisfied if this code runs.
        unsafe { self.store_unchecked::<T>(value) };
    }

    /// Gain ownership of the value inside the dungeon core.
    ///
    /// This will leave the dungeon core empty if the value is of 
    /// type `T`. If the dungeon core is empty or has another type's
    /// value stored in it then a `None` will be returned.
    pub fn take<T: 'static>(&mut self) -> Option<T> {
        if self.is_type::<T>() {
            // SAFETY:
            // We just checked that this dungeon core is storing a value
            // of `T`.
            Some(unsafe { self.take_unchecked::<T>() })
        } else {
            None
        }
    }

    /// Replace the current value with a `T`.
    ///
    /// The current value is returned if the dungeon core is not empty,
    /// and the current stored value is of type `U`.
    ///
    /// If `T` can not be stored by this dungeon core do to
    /// size or alignment requirements a compiler error or
    /// runtime panic will be generated. This behavior is
    /// controlled by the `assert_runtime` feature.
    ///
    /// This is slightly more efficient than a `take()` followed be
    /// a `store()
    #[track_caller]
    pub fn replace<T: 'static, U: 'static>(&mut self, value: T) -> Option<U> {
        // Assert `T` can be stored.
        crate::can_store::assert_can_store::<SIZE, ALIGN, T>();

        // Take the old value.
        let old_value = self.take();

        // SAFETY:
        // The call to `assert_can_store()` satisfies the safety requirements.
        // 
        // We also won't forget a value because we just took it.
        unsafe { self.store_unchecked::<T>(value) };

        old_value
    }

    /// Try to borrow the stored value.
    ///
    /// If the dungeon core is empty or the stored value is
    /// not of type `T` then `None` will be returned.
    pub fn try_borrow<T: 'static>(&self) -> Option<&T> {
        if !self.is_type::<T>() {
            return None;
        }

        // SAFETY:
        // We just checked that the current stored type is a `T`.
        let value = unsafe { self.try_borrow_unchecked::<T>() };

        Some(value)
    }

    /// Try to mutably borrow the stored value.
    ///
    /// If the dungeon core is empty or the stored value is
    /// not of type `T` then `None` will be returned.
    pub fn try_borrow_mut<T: 'static>(&mut self) -> Option<&mut T> {
        if !self.is_type::<T>() {
            return None;
        }

        // SAFETY:
        // We just checked that the current stored type is a `T`.
        let value = unsafe { self.try_borrow_mut_unchecked::<T>() };

        Some(value)
    }

    /// Check if a value is being stored.
    pub fn is_empty(&self) -> bool {
        self.current == never_type_id()
    }

    /// Check if a value being stored is of type `T`.
    ///
    /// Will always return `false` if empty.
    pub fn is_type<T: 'static>(&self) -> bool {
        if self.is_empty() {
            // A dungeon core never stores a value of `!`.
            // It is used as a sentinel value.
            false
        } else {
            self.current == core::any::TypeId::of::<T>()
        }
    }
}


/// Unsafe methods.
impl<const SIZE: usize, const ALIGN: u8> DungeonCore<SIZE, ALIGN> 
where
    Alignment<ALIGN>: ValidAlignment,
{
    /// Unchecked form of `store`.
    ///
    /// If the dungeon core is not empty, then the stored value will 
    /// be forgotten.
    ///
    /// # Safety
    /// `T` must be storable in `SIZE` bytes with alignment of `2^ALIGN` bytes.
    pub unsafe fn store_unchecked<T: 'static>(&mut self, value: T) {
        // Wrap the value so we can manually move it.
        // `value` will be considered uninit after the read below.
        let value = MaybeUninit::new(value);

        // Get a pointer to the aligned buffer.
        // This is done in long form to make it easier to read.
        let data: &mut [MaybeUninit<u8>; SIZE] = &mut self.buffer.data;
        let data: *mut [MaybeUninit<u8>; SIZE] = data as _;
        let data: *mut MaybeUninit<T> = data.cast();

        // SAFETY:
        // `value` is valid for a read because we just made it above 
        // from a valid value.
        //
        // `data` is valid for a write because of the safety requirements
        // for the outer function.
        //
        // They do not overlap.
        unsafe {
            core::ptr::copy_nonoverlapping(addr_of!(value), data, 1);
        }

        // Set the drop function to drop a value of `T`.
        self.drop_impl = drop_t::<T, SIZE, ALIGN>;

        // Set the current type to `T`.
        self.current = core::any::TypeId::of::<T>();
    }

    /// Unchecked form of [take].
    ///
    /// # Safety
    /// The dungeon core must be storing a valid value of type `T`.
    pub unsafe fn take_unchecked<T: 'static>(&mut self) -> T {
        // Buffer to move the stored value into.
        let mut value = MaybeUninit::uninit();

        // Get a pointer to the aligned buffer.
        // This is done in long form to make it easier to read.
        let data: &[MaybeUninit<u8>; SIZE] = &self.buffer.data;
        let data: *const [MaybeUninit<u8>; SIZE] = data as _;
        let data: *const MaybeUninit<T> = data.cast();

        // SAFETY:
        // `data` is valid for a read because we borrowed it.
        // And we must be storing a `T` because of the outer safety requirements.
        //
        // `value` is valid for a write because we just made it above.
        //
        // They do not overlap.
        unsafe {
            core::ptr::copy_nonoverlapping(data, addr_of_mut!(value), 1);
        }

        // Mark the dungeon core as empty.
        self.current = never_type_id();

        // SAFETY:
        // The requirements of the outer function require that the
        // value we just copied to be a valid `T`; And, by marking 
        // the dungeon core as empty the value will not be aliased.
        unsafe { value.assume_init() }
    }

    /// Unchecked form of `try_borrow`.
    ///
    /// # Safety
    /// The dungeon core must be storing a valid value of type `T`.
    pub unsafe fn try_borrow_unchecked<T: 'static>(&self) -> &T {
        // Get a pointer to the aligned buffer.
        // This is done in long form to make it easier to read.
        let data: &[MaybeUninit<u8>; SIZE] = &self.buffer.data;
        let data: *const [MaybeUninit<u8>; SIZE] = data as _;
        let data: *const MaybeUninit<T> = data.cast();

        // SAFETY:
        // We just borrowed `data` so it's valid to dereference.
        let data: &MaybeUninit<T> = unsafe { &*data };

        // SAFETY:
        // The outer safety requirements makes this safe.
        // `data` is a valid value of `T`.
        unsafe { data.assume_init_ref() }
    }

    /// Unchecked form of `try_borrow_mut`.
    ///
    /// # Safety
    /// The dungeon core must be storing a valid value of type `T`.
    pub unsafe fn try_borrow_mut_unchecked<T: 'static>(&mut self) -> &mut T {
        // Get a pointer to the aligned buffer.
        // This is done in long form to make it easier to read.
        let data: &mut [MaybeUninit<u8>; SIZE] = &mut self.buffer.data;
        let data: *mut [MaybeUninit<u8>; SIZE] = data as _;
        let data: *mut MaybeUninit<T> = data.cast();
        
        // SAFETY:
        // We just borrowed `data` so it's valid to dereference.
        let data: &mut MaybeUninit<T> = unsafe { &mut *data };

        // SAFETY:
        // The outer safety requirements makes this safe.
        // `data` is a valid value of `T`.
        unsafe { data.assume_init_mut() }
    }
}