Skip to main content

fromsoftware_shared/
owned_pointer.rs

1use std::alloc::{Layout, handle_alloc_error};
2use std::ops::{Deref, DerefMut};
3use std::{fmt, marker::PhantomData, ptr::NonNull};
4
5use crate::{GameAllocator, NoOpAllocator};
6
7/// Pointer to a structure that the containing structure owns. You will generally use this to model
8/// structures in foreign memory when extending the game libraries. Do not use this in your own
9/// code as you're risking all rusts safety reasoning.
10///
11/// ## Safety
12///
13/// ### `OwnedPtr` in FFI
14///
15/// When declaring definitions of C++ structs and functions that use this type,
16/// the author must ensure several invariants hold true:
17///
18/// * This must be the only way to access the data it refers to without an
19///   `unsafe` block (not including the `unsafe` block necessary to call
20///   [`FromStatic::instance`]). This means there may not be any other structs
21///   or methods that provide an `OwnedPtr` or a reference to the underlying
22///   data.
23///
24///   Although *generally* this means that the struct that h olds an `OwnedPtr`
25///   is the same one that "owns" the data it refers to in the sense of being
26///   responsible for constructing and destroying it, that's not a hard
27///   requirement. In some cases, it may be more ergonomic to expose an
28///   `OwnedPtr` (or a reference) through a struct that's easy to obtain and use
29///   `NonNull` for the struct that actually has ownership.
30///
31/// * This must ensure that the backing memory is allocated by an allocator
32///   that's *compatible with* `A`. Note that all memory allocated in any way is
33///   *compatible with* [`NoOpAllocator`], so this requirement only matters if
34///   `A` is set explicitly.
35///
36/// [`FromStatic::instance`]: crate::FromStatic::instance
37///
38/// ### `OwnedPtr` and `Drop`
39///
40/// For any type `T` where Rust code might take ownership over an `OwnedPtr<T>`,
41/// the author should be sure that `T`'s [Drop] implementation is correct. (This
42/// is true in general for FFI types that Rust code can own.) There are two
43/// concerns here:
44///
45/// * Generally, C++ code will declare a specific destroy function for each
46///   type. In addition to calling the destructor method (`~T()`), this destroys
47///   any fields as well.
48///
49/// * Rust drops fields in declaration order but C++ destroys them in reverse
50///   declaration order.
51///
52/// To mitigate these issues, all fields with non-trivial drop/destructor
53/// implementations should by wrapped in [std::mem::ManuallyDrop]. If the C++
54/// code has a destroy method, it should be called from [Drop::drop]; otherwise,
55/// the implementation of [Drop::drop] should drop these fields in reverse
56/// declaration order.
57#[repr(transparent)]
58pub struct OwnedPtr<T, A: GameAllocator = NoOpAllocator> {
59    ptr: NonNull<T>,
60    _marker: PhantomData<A>,
61}
62
63impl<T, A: GameAllocator> OwnedPtr<T, A> {
64    /// Allocates memory with `A` and places `value` into it.
65    ///
66    /// This doesn’t actually allocate if `T` is zero-sized.
67    ///
68    /// **Important:** any type constructed this way should have an appropriate
69    /// [Drop::drop] implementation defined. [See above](#ownedptr-and-drop) for
70    /// details.
71    pub fn new(value: T) -> Self {
72        let layout = Layout::new::<T>();
73        if layout.size() == 0 {
74            OwnedPtr {
75                ptr: NonNull::dangling(),
76                _marker: Default::default(),
77            }
78        } else if let Ok(ptr) = A::allocate(layout) {
79            let ptr = ptr.cast::<T>();
80            unsafe { ptr.write(value) };
81            OwnedPtr {
82                ptr,
83                _marker: Default::default(),
84            }
85        } else {
86            handle_alloc_error(layout)
87        }
88    }
89
90    pub fn as_ptr(&self) -> *mut T {
91        self.ptr.as_ptr()
92    }
93}
94
95impl<T: Default, A: GameAllocator> Default for OwnedPtr<T, A> {
96    fn default() -> Self {
97        OwnedPtr::new(Default::default())
98    }
99}
100
101impl<T, A: GameAllocator> Deref for OwnedPtr<T, A> {
102    type Target = T;
103
104    fn deref(&self) -> &Self::Target {
105        unsafe { self.ptr.as_ref() }
106    }
107}
108
109impl<T, A: GameAllocator> AsRef<T> for OwnedPtr<T, A> {
110    fn as_ref(&self) -> &T {
111        self.deref()
112    }
113}
114
115impl<T, A: GameAllocator> DerefMut for OwnedPtr<T, A> {
116    fn deref_mut(&mut self) -> &mut Self::Target {
117        unsafe { self.ptr.as_mut() }
118    }
119}
120
121impl<T, A: GameAllocator> AsMut<T> for OwnedPtr<T, A> {
122    fn as_mut(&mut self) -> &mut T {
123        self.deref_mut()
124    }
125}
126
127impl<T, A: GameAllocator> fmt::Debug for OwnedPtr<T, A> {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
129        self.ptr.fmt(f)
130    }
131}
132
133impl<T, A: GameAllocator> fmt::Pointer for OwnedPtr<T, A> {
134    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
135        fmt::Pointer::fmt(&self.ptr, f)
136    }
137}
138
139impl<T, A: GameAllocator> Drop for OwnedPtr<T, A> {
140    fn drop(&mut self) {
141        unsafe {
142            self.ptr.drop_in_place();
143            if Layout::new::<T>().size() > 0 {
144                A::deallocate(self.ptr.cast::<u8>(), Layout::new::<T>());
145            }
146        }
147    }
148}
149
150unsafe impl<T: Send, A: GameAllocator + Send> Send for OwnedPtr<T, A> {}
151unsafe impl<T: Sync, A: GameAllocator + Sync> Sync for OwnedPtr<T, A> where T: Sync {}