ext_php_rs/boxed.rs
1//! A pointer type for heap allocation using the Zend memory manager.
2//!
3//! Heap memory in PHP is usually request-bound and allocated inside [memory
4//! arenas], which are cleared at the start and end of a PHP request. Allocating
5//! and freeing memory that is allocated on the Zend heap is done through two
6//! separate functions [`efree`] and [`emalloc`].
7//!
8//! As such, most heap-allocated PHP types **cannot** be allocated on the stack,
9//! such as [`ZendStr`], which is a dynamically-sized type, and therefore must
10//! be allocated on the heap. A regular [`Box`] would not work in this case, as
11//! the memory needs to be freed from a separate function `zend_string_release`.
12//! The [`ZBox`] type provides a wrapper which calls the relevant release
13//! functions based on the type and what is inside the implementation of
14//! [`ZBoxable`].
15//!
16//! This type is not created directly, but rather through a function implemented
17//! on the downstream type. For example, [`ZendStr`] has a function `new` which
18//! returns a [`ZBox<ZendStr>`].
19//!
20//! [memory arenas]: https://en.wikipedia.org/wiki/Region-based_memory_management
21//! [`ZendStr`]: crate::types::ZendStr
22//! [`emalloc`]: super::alloc::efree
23
24use std::{
25 borrow::Borrow,
26 fmt::Debug,
27 mem::ManuallyDrop,
28 ops::{Deref, DerefMut},
29 ptr::{self, NonNull},
30};
31
32use super::alloc::efree;
33
34/// A pointer type for heap allocation using the Zend memory manager.
35///
36/// See the [module level documentation](../index.html) for more.
37pub struct ZBox<T: ZBoxable>(NonNull<T>);
38
39impl<T: ZBoxable> ZBox<T> {
40 /// Creates a new box from a given pointer.
41 ///
42 /// # Parameters
43 ///
44 /// * `ptr` - A non-null, well-aligned pointer to a `T`.
45 ///
46 /// # Safety
47 ///
48 /// Caller must ensure that `ptr` is non-null, well-aligned and pointing to
49 /// a `T`.
50 pub unsafe fn from_raw(ptr: *mut T) -> Self {
51 Self(NonNull::new_unchecked(ptr))
52 }
53
54 /// Returns the pointer contained by the box, dropping the box in the
55 /// process. The data pointed to by the returned pointer is not
56 /// released.
57 ///
58 /// # Safety
59 ///
60 /// The caller is responsible for managing the memory pointed to by the
61 /// returned pointer, including freeing the memory.
62 #[must_use]
63 pub fn into_raw(self) -> &'static mut T {
64 let mut this = ManuallyDrop::new(self);
65 // SAFETY: All constructors ensure the contained pointer is well-aligned and
66 // dereferenceable.
67 unsafe { this.0.as_mut() }
68 }
69}
70
71impl<T: ZBoxable> Drop for ZBox<T> {
72 #[inline]
73 fn drop(&mut self) {
74 self.deref_mut().free();
75 }
76}
77
78impl<T: ZBoxable> Deref for ZBox<T> {
79 type Target = T;
80
81 #[inline]
82 fn deref(&self) -> &Self::Target {
83 // SAFETY: All constructors ensure the contained pointer is well-aligned and
84 // dereferenceable.
85 unsafe { self.0.as_ref() }
86 }
87}
88
89impl<T: ZBoxable> DerefMut for ZBox<T> {
90 #[inline]
91 fn deref_mut(&mut self) -> &mut Self::Target {
92 // SAFETY: All constructors ensure the contained pointer is well-aligned and
93 // dereferenceable.
94 unsafe { self.0.as_mut() }
95 }
96}
97
98impl<T: ZBoxable + Debug> Debug for ZBox<T> {
99 #[inline]
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 (**self).fmt(f)
102 }
103}
104
105impl<T: ZBoxable> Borrow<T> for ZBox<T> {
106 #[inline]
107 fn borrow(&self) -> &T {
108 self
109 }
110}
111
112impl<T: ZBoxable> AsRef<T> for ZBox<T> {
113 #[inline]
114 fn as_ref(&self) -> &T {
115 self
116 }
117}
118
119/// Implemented on types that can be heap allocated using the Zend memory
120/// manager. These types are stored inside a [`ZBox`] when heap-allocated, and
121/// the [`free`] method is called when the box is dropped.
122///
123/// # Safety
124///
125/// The default implementation of the [`free`] function uses the [`efree`]
126/// function to free the memory without calling any destructors.
127///
128/// The implementor must ensure that any time a pointer to the implementor is
129/// passed into a [`ZBox`] that the memory pointed to was allocated by the Zend
130/// memory manager.
131///
132/// [`free`]: #method.free
133pub unsafe trait ZBoxable {
134 /// Frees the memory pointed to by `self`, calling any destructors required
135 /// in the process.
136 fn free(&mut self) {
137 unsafe { efree(ptr::from_mut(self).cast::<u8>()) };
138 }
139}