ext_php_rs/
alloc.rs

1//! Functions relating to the Zend Memory Manager, used to allocate
2//! request-bound memory.
3
4use cfg_if::cfg_if;
5
6use crate::ffi::{_efree, _emalloc, _estrdup};
7use std::{
8    alloc::Layout,
9    ffi::{c_char, c_void, CString},
10};
11
12/// Uses the PHP memory allocator to allocate request-bound memory.
13///
14/// # Parameters
15///
16/// * `layout` - The layout of the requested memory.
17///
18/// # Returns
19///
20/// A pointer to the memory allocated.
21#[must_use]
22pub fn emalloc(layout: Layout) -> *mut u8 {
23    // TODO account for alignment
24    let size = layout.size();
25
26    (unsafe {
27        cfg_if! {
28            if #[cfg(php_debug)] {
29                #[allow(clippy::used_underscore_items)]
30                _emalloc(size as _, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0)
31            } else {
32                #[allow(clippy::used_underscore_items)]
33                _emalloc(size as _)
34            }
35        }
36    })
37    .cast::<u8>()
38}
39
40/// Frees a given memory pointer which was allocated through the PHP memory
41/// manager.
42///
43/// # Parameters
44///
45/// * `ptr` - The pointer to the memory to free.
46///
47/// # Safety
48///
49/// Caller must guarantee that the given pointer is valid (aligned and non-null)
50/// and was originally allocated through the Zend memory manager.
51pub unsafe fn efree(ptr: *mut u8) {
52    cfg_if! {
53        if #[cfg(php_debug)] {
54            #[allow(clippy::used_underscore_items)]
55            _efree(
56                ptr.cast::<c_void>(),
57                std::ptr::null_mut(),
58                0,
59                std::ptr::null_mut(),
60                0,
61            );
62        } else {
63            #[allow(clippy::used_underscore_items)]
64            _efree(ptr.cast::<c_void>());
65        }
66    }
67}
68
69/// Duplicates a string using the PHP memory manager.
70///
71/// # Parameters
72///
73/// * `string` - The string to duplicate, which can be any type that can be
74///   converted into a `Vec<u8>`.
75///
76/// # Returns
77///
78/// A pointer to the duplicated string in the PHP memory manager.
79pub fn estrdup(string: impl Into<Vec<u8>>) -> *mut c_char {
80    let string = unsafe { CString::from_vec_unchecked(string.into()) }.into_raw();
81
82    let result = unsafe {
83        cfg_if! {
84            if #[cfg(php_debug)] {
85                #[allow(clippy::used_underscore_items)]
86                _estrdup(string, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0)
87            } else {
88                #[allow(clippy::used_underscore_items)]
89                _estrdup(string)
90            }
91        }
92    };
93
94    drop(unsafe { CString::from_raw(string) });
95    result
96}
97
98#[cfg(test)]
99#[cfg(feature = "embed")]
100mod test {
101    use super::*;
102    use crate::embed::Embed;
103    use std::ffi::CStr;
104
105    #[test]
106    fn test_emalloc() {
107        Embed::run(|| {
108            let layout = Layout::from_size_align(16, 8).expect("should create layout");
109            let ptr = emalloc(layout);
110            assert!(!ptr.is_null());
111            unsafe { efree(ptr) };
112        });
113    }
114
115    #[test]
116    fn test_estrdup() {
117        Embed::run(|| {
118            let original = "Hello, world!";
119            let duplicated = estrdup(original);
120            assert!(!duplicated.is_null());
121
122            let duplicated_str = unsafe { CStr::from_ptr(duplicated) };
123            assert_eq!(
124                duplicated_str.to_str().expect("should convert to str"),
125                original
126            );
127
128            unsafe { efree(duplicated.cast::<u8>()) }
129        });
130    }
131}