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
//! A pointer type for heap allocation using the Zend memory manager.
//!
//! Heap memory in PHP is usually request-bound and allocated inside [memory
//! arenas], which are cleared at the start and end of a PHP request. Allocating
//! and freeing memory that is allocated on the Zend heap is done through two
//! separate functions [`efree`] and [`emalloc`].
//!
//! As such, most heap-allocated PHP types **cannot** be allocated on the stack,
//! such as [`ZendStr`], which is a dynamically-sized type, and therefore must
//! be allocated on the heap. A regular [`Box`] would not work in this case, as
//! the memory needs to be freed from a separate function `zend_string_release`.
//! The [`ZBox`] type provides a wrapper which calls the relevant release
//! functions based on the type and what is inside the implementation of
//! [`ZBoxable`].
//!
//! This type is not created directly, but rather through a function implemented
//! on the downstream type. For example, [`ZendStr`] has a function `new` which
//! returns a [`ZBox<ZendStr>`].
//!
//! [memory arenas]: https://en.wikipedia.org/wiki/Region-based_memory_management
//! [`ZendStr`]: crate::types::ZendStr
//! [`emalloc`]: super::alloc::efree
use std::{
borrow::Borrow,
fmt::Debug,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr::NonNull,
};
use super::alloc::efree;
/// A pointer type for heap allocation using the Zend memory manager.
///
/// See the [module level documentation](../index.html) for more.
pub struct ZBox<T: ZBoxable>(NonNull<T>);
impl<T: ZBoxable> ZBox<T> {
/// Creates a new box from a given pointer.
///
/// # Parameters
///
/// * `ptr` - A non-null, well-aligned pointer to a `T`.
///
/// # Safety
///
/// Caller must ensure that `ptr` is non-null, well-aligned and pointing to
/// a `T`.
pub unsafe fn from_raw(ptr: *mut T) -> Self {
Self(NonNull::new_unchecked(ptr))
}
/// Returns the pointer contained by the box, dropping the box in the
/// process. The data pointed to by the returned pointer is not
/// released.
///
/// # Safety
///
/// The caller is responsible for managing the memory pointed to by the
/// returned pointer, including freeing the memory.
pub fn into_raw(self) -> &'static mut T {
let mut this = ManuallyDrop::new(self);
// SAFETY: All constructors ensure the contained pointer is well-aligned and
// dereferenceable.
unsafe { this.0.as_mut() }
}
}
impl<T: ZBoxable> Drop for ZBox<T> {
#[inline]
fn drop(&mut self) {
self.deref_mut().free()
}
}
impl<T: ZBoxable> Deref for ZBox<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY: All constructors ensure the contained pointer is well-aligned and
// dereferenceable.
unsafe { self.0.as_ref() }
}
}
impl<T: ZBoxable> DerefMut for ZBox<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: All constructors ensure the contained pointer is well-aligned and
// dereferenceable.
unsafe { self.0.as_mut() }
}
}
impl<T: ZBoxable + Debug> Debug for ZBox<T> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
impl<T: ZBoxable> Borrow<T> for ZBox<T> {
#[inline]
fn borrow(&self) -> &T {
self
}
}
impl<T: ZBoxable> AsRef<T> for ZBox<T> {
#[inline]
fn as_ref(&self) -> &T {
self
}
}
/// Implemented on types that can be heap allocated using the Zend memory
/// manager. These types are stored inside a [`ZBox`] when heap-allocated, and
/// the [`free`] method is called when the box is dropped.
///
/// # Safety
///
/// The default implementation of the [`free`] function uses the [`efree`]
/// function to free the memory without calling any destructors.
///
/// The implementor must ensure that any time a pointer to the implementor is
/// passed into a [`ZBox`] that the memory pointed to was allocated by the Zend
/// memory manager.
///
/// [`free`]: #method.free
pub unsafe trait ZBoxable {
/// Frees the memory pointed to by `self`, calling any destructors required
/// in the process.
fn free(&mut self) {
unsafe { efree(self as *mut _ as *mut u8) };
}
}