From 32f49c7371eea5766f8eb7df3b08fbae974e689d Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Thu, 12 Jun 2025 19:15:52 +0800 Subject: [PATCH] feat(alloc): add estrdup --- allowed_bindings.rs | 1 + docsrs_bindings.rs | 9 ++++++ src/alloc.rs | 71 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/allowed_bindings.rs b/allowed_bindings.rs index 1dda0ee9c..79e8d56f7 100644 --- a/allowed_bindings.rs +++ b/allowed_bindings.rs @@ -27,6 +27,7 @@ bind! { _call_user_function_impl, _efree, _emalloc, + _estrdup, _zend_executor_globals, _sapi_globals_struct, _sapi_module_struct, diff --git a/docsrs_bindings.rs b/docsrs_bindings.rs index 16a3d1169..9ec2e72a0 100644 --- a/docsrs_bindings.rs +++ b/docsrs_bindings.rs @@ -490,6 +490,15 @@ extern "C" { __zend_orig_lineno: u32, ); } +extern "C" { + pub fn _estrdup( + s: *const ::std::os::raw::c_char, + __zend_filename: *const ::std::os::raw::c_char, + __zend_lineno: u32, + __zend_orig_filename: *const ::std::os::raw::c_char, + __zend_orig_lineno: u32, + ) -> *mut ::std::os::raw::c_char; +} extern "C" { pub fn __zend_malloc(len: usize) -> *mut ::std::os::raw::c_void; } diff --git a/src/alloc.rs b/src/alloc.rs index 47517e1f1..9ed94f9ea 100644 --- a/src/alloc.rs +++ b/src/alloc.rs @@ -3,8 +3,11 @@ use cfg_if::cfg_if; -use crate::ffi::{_efree, _emalloc}; -use std::{alloc::Layout, ffi::c_void}; +use crate::ffi::{_efree, _emalloc, _estrdup}; +use std::{ + alloc::Layout, + ffi::{c_char, c_void, CString}, +}; /// Uses the PHP memory allocator to allocate request-bound memory. /// @@ -62,3 +65,67 @@ pub unsafe fn efree(ptr: *mut u8) { } } } + +/// Duplicates a string using the PHP memory manager. +/// +/// # Parameters +/// +/// * `string` - The string to duplicate, which can be any type that can be +/// converted into a `Vec`. +/// +/// # Returns +/// +/// A pointer to the duplicated string in the PHP memory manager. +pub fn estrdup(string: impl Into>) -> *mut c_char { + let string = unsafe { CString::from_vec_unchecked(string.into()) }.into_raw(); + + let result = unsafe { + cfg_if! { + if #[cfg(php_debug)] { + #[allow(clippy::used_underscore_items)] + _estrdup(string, std::ptr::null_mut(), 0, std::ptr::null_mut(), 0) + } else { + #[allow(clippy::used_underscore_items)] + _estrdup(string) + } + } + }; + + drop(unsafe { CString::from_raw(string) }); + result +} + +#[cfg(test)] +#[cfg(feature = "embed")] +mod test { + use super::*; + use crate::embed::Embed; + use std::ffi::CStr; + + #[test] + fn test_emalloc() { + Embed::run(|| { + let layout = Layout::from_size_align(16, 8).expect("should create layout"); + let ptr = emalloc(layout); + assert!(!ptr.is_null()); + unsafe { efree(ptr) }; + }); + } + + #[test] + fn test_estrdup() { + Embed::run(|| { + let original = "Hello, world!"; + let duplicated = estrdup(original); + assert!(!duplicated.is_null()); + + let duplicated_str = unsafe { CStr::from_ptr(duplicated) }; + assert_eq!( + duplicated_str.to_str().expect("should convert to str"), + original + ); + + unsafe { efree(duplicated.cast::()) } + }); + } +}