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
//! Resting place for [OgreUnique<>]

use crate::ogre_std::ogre_alloc::{
        ogre_arc::OgreArc,
        OgreAllocator,
    };
use std::{
    ops::Deref,
    fmt::{Debug, Display, Formatter},
};


/// Wrapper type for data that requires a custom Drop to be called (through an [OgreAllocator]).
/// Similar to C++'s `unique_ptr`
pub struct OgreUnique<DataType:          Debug + Send + Sync + 'static,
                      OgreAllocatorType: OgreAllocator<DataType> + Send + Sync + 'static> {
    allocator: &'static OgreAllocatorType,
    data_ref:  &'static DataType,
}


impl<DataType:          Debug + Send + Sync + 'static,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
OgreUnique<DataType, OgreAllocatorType> {

    #[inline(always)]
    pub fn new<F: FnOnce(&mut DataType)>(setter: F, allocator: &OgreAllocatorType) -> Option<Self> {
        allocator.alloc_ref()
            .map(|(slot_ref, _slot_id)| {
                setter(slot_ref);
                Self::from_allocated_ref(slot_ref, allocator)
            })
    }

    #[inline(always)]
    pub fn from_allocated_id(data_id: u32, allocator: &OgreAllocatorType) -> Self {
        let data_ref = allocator.ref_from_id(data_id);
        Self::from_allocated_ref(data_ref, allocator)
    }

    #[inline(always)]
    pub fn from_allocated_ref(data_ref: &DataType, allocator: &OgreAllocatorType) -> Self {
        Self {
            allocator: unsafe { &*(allocator as *const OgreAllocatorType) },
            data_ref:  unsafe { &*(data_ref as *const DataType) },
        }
    }

    #[inline(always)]
    pub fn into_ogre_arc(self) -> OgreArc<DataType, OgreAllocatorType> {
        let undroppable_self = std::mem::ManuallyDrop::new(self);
        OgreArc::from_allocated(undroppable_self.allocator.id_from_ref(undroppable_self.data_ref), undroppable_self.allocator)
    }

}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Deref for
OgreUnique<DataType, OgreAllocatorType> {

    type Target = DataType;

    #[inline(always)]
    fn deref(&self) -> &Self::Target {
        self.data_ref
    }
}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
AsRef<DataType> for
OgreUnique<DataType, OgreAllocatorType> {

    #[inline(always)]
    fn as_ref(&self) -> &DataType {
        self.data_ref
    }

}


impl<DataType:          Debug + Send + Sync + Display,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Display for
OgreUnique<DataType, OgreAllocatorType> {

    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        std::fmt::Display::fmt(&self.deref(), f)
    }
}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Debug for
OgreUnique<DataType, OgreAllocatorType> {

    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "OgreUnique {{allocator: {:?}, data: #{}:{:?}}}",
                  self.allocator, self.allocator.id_from_ref(self.data_ref), self.data_ref)
    }
}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
PartialEq<DataType> for
OgreUnique<DataType, OgreAllocatorType>
where DataType: PartialEq {

    #[inline(always)]
    fn eq(&self, other: &DataType) -> bool {
        self.deref().eq(other)
    }
}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
From<OgreUnique<DataType, OgreAllocatorType>> for
OgreArc<DataType, OgreAllocatorType> {

    #[inline(always)]
    fn from(ogre_unique: OgreUnique<DataType, OgreAllocatorType>) -> OgreArc<DataType, OgreAllocatorType> {
        ogre_unique.into_ogre_arc()
    }
}


impl<DataType:          Debug + Send + Sync,
     OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Drop for
OgreUnique<DataType, OgreAllocatorType> {

    #[inline(always)]
    fn drop(&mut self) {
        self.allocator.dealloc_ref(self.data_ref);
    }
}


unsafe impl<DataType:          Debug + Send + Sync,
            OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Send for
OgreUnique<DataType, OgreAllocatorType> {}

unsafe impl<DataType:          Debug + Send + Sync,
            OgreAllocatorType: OgreAllocator<DataType> + Send + Sync>
Sync for
OgreUnique<DataType, OgreAllocatorType> {}


#[cfg(any(test,doc))]
mod tests {
    //! Unit tests for [ogre_arc](super) module

    use super::*;
    use crate::prelude::advanced::AllocatorAtomicArray;


    #[cfg_attr(not(doc),test)]
    pub fn happy_path_usage() {
        let allocator = AllocatorAtomicArray::<u128, 128>::new();
        let unique = OgreUnique::new(|slot| *slot = 128, &allocator).expect("Allocation should have been done");
        println!("unique is {unique} -- {:?}", unique);
        assert_eq!(*unique.deref(), 128, "Value doesn't match");
        drop(unique);
        // TODO assert that allocator have all elements free (change the trait) and do a similar assertion in `OgreArc`
        println!("all is free:");
        println!("{:?}", allocator);
    }

    #[cfg_attr(not(doc),test)]
    pub fn into_ogrearc() {
        let allocator = AllocatorAtomicArray::<u128, 128>::new();
        let unique = OgreUnique::new(|slot| *slot = 128, &allocator).expect("Allocation should have been done");
        let ogre_arc = unique.into_ogre_arc();
        println!("arc is {ogre_arc} -- {:?}", ogre_arc);
        assert_eq!((*ogre_arc.deref(), ogre_arc.references_count()), (128, 1), "Starting Value and Reference Counts don't match for the converted `ogre_arc`");
        drop(ogre_arc);
        // TODO assert that allocator have all elements free (change the trait) and do a similar assertion in `OgreArc`
        println!("all is free:");
        println!("{:?}", allocator);
    }

    #[cfg_attr(not(doc),test)]
    pub fn comparisons() {
        // &str
        let expected: &str = "&str to compare";
        let allocator = AllocatorAtomicArray::<&str, 128>::new();
        let observed = OgreUnique::new(|slot| *slot = expected, &allocator).expect("Allocation should have been done");
        assert_eq!(observed,  expected,  "Comparison of pristine types failed for `&str`");
        assert_eq!(&observed, &expected, "Comparison of references failed for `&str`");
        assert_eq!(*observed, expected,  "Comparison of `Deref` failed for `&str`");

        // String -- notice that, although possible, there is no nice API for "movable values", like `Arc` or `String`: OgreAllocator were not designed for those, therefore, OgreArc, also isn't.
        let expected: String = String::from("String to compare");
        let allocator = AllocatorAtomicArray::<String, 128>::new();
        let observed = OgreUnique::new(|slot| unsafe { std::ptr::write(slot, expected.clone()); }, &allocator).expect("Allocation should have been done");
        assert_eq!(observed,  expected,  "Comparison of pristine types failed for `String`");
        assert_eq!(&observed, &expected, "Comparison of references failed for `String`");
        assert_eq!(*observed, expected,  "Comparison of `Deref` failed for `String`");

        // Wrapped in an `Option<>` (not yet supported as I cannot implement PartialEq for Option<MyType>...)
        // unwrap and compare instead
        // let payload = String::from("Wrapped");
        // let expected = Some(payload.clone());
        // let allocator = AllocatorAtomicArray::<String, 128>::new();
        // let observed = Some(OgreUnique::new(|slot| unsafe { std::ptr::write(slot, payload); }, &allocator).expect("Allocation should have been done"));
        // assert_eq!(observed,  expected,  "Comparison of wrapped types failed for `Option<String>`");
        // assert_eq!(&observed, &expected, "Comparison of references failed for `Option<String>`");
        // assert_eq!(*observed, expected,  "Comparison of `Deref` failed for `Option<String>`");

    }

}