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
use std::cell::UnsafeCell;

use crate::{ChangeTicks, ComponentId, SparseArray};

pub trait Resource: Send + Sync + 'static {}

impl<T> Resource for T where T: Send + Sync + 'static {}

impl dyn Resource {
    #[inline]
    pub unsafe fn downcast_ref<T: Resource>(&self) -> &T {
        unsafe { &*(self as *const dyn Resource as *const T) }
    }

    #[inline]
    pub unsafe fn downcast_mut<T: Resource>(&mut self) -> &mut T {
        unsafe { &mut *(self as *mut dyn Resource as *mut T) }
    }
}

pub struct ResourceData {
    data: *mut dyn Resource,
    change_ticks: UnsafeCell<ChangeTicks>,
}

impl ResourceData {
    #[inline]
    pub fn new<T: Resource>(data: T, change_tick: u32) -> Self {
        Self {
            data: Box::into_raw(Box::new(data)),
            change_ticks: UnsafeCell::new(ChangeTicks::new(change_tick)),
        }
    }

    #[inline]
    pub fn from_boxed(data: Box<dyn Resource>, change_tick: u32) -> Self {
        Self {
            data: Box::into_raw(data),
            change_ticks: UnsafeCell::new(ChangeTicks::new(change_tick)),
        }
    }

    #[inline]
    pub fn as_ptr(&self) -> *mut dyn Resource {
        self.data
    }

    #[inline]
    pub fn change_ticks(&self) -> &UnsafeCell<ChangeTicks> {
        &self.change_ticks
    }

    #[inline]
    pub fn change_ticks_mut(&mut self) -> &mut ChangeTicks {
        self.change_ticks.get_mut()
    }
}

impl Drop for ResourceData {
    #[inline]
    fn drop(&mut self) {
        // SAFETY: `self.data` was crated from a Box.
        unsafe { Box::from_raw(self.data) };
    }
}

#[derive(Default)]
pub struct Resources {
    resources: SparseArray<ResourceData>,
}

impl Resources {
    #[inline]
    pub fn contains(&self, id: ComponentId) -> bool {
        self.resources.contains(id.index())
    }

    #[inline]
    pub unsafe fn insert(
        &mut self,
        id: ComponentId,
        resource: Box<dyn Resource>,
        change_tick: u32,
    ) {
        let data = ResourceData::from_boxed(resource, change_tick);
        self.resources.insert(id.index(), data);
    }

    #[inline]
    pub fn remove(&mut self, id: ComponentId) -> Option<*mut dyn Resource> {
        Some(self.resources.remove(id.index())?.as_ptr())
    }

    #[inline]
    pub fn get(&self, id: ComponentId) -> Option<*mut dyn Resource> {
        Some(self.resources.get(id.index())?.as_ptr())
    }

    #[inline]
    pub fn get_with_ticks(&self, id: ComponentId) -> Option<(*mut dyn Resource, *mut ChangeTicks)> {
        let data = self.resources.get(id.index())?;
        Some((data.as_ptr(), data.change_ticks.get()))
    }
}