22//! allowing users to store Rust data inside a PHP object.
33
44use std:: {
5+ alloc:: Layout ,
56 convert:: TryInto ,
67 fmt:: Debug ,
7- mem,
8+ marker:: PhantomData ,
9+ mem:: MaybeUninit ,
810 ops:: { Deref , DerefMut } ,
11+ sync:: atomic:: { AtomicBool , Ordering } ,
912} ;
1013
1114use crate :: {
@@ -182,7 +185,11 @@ pub trait ZendObjectOverride {
182185 /// # Parameters
183186 ///
184187 /// * `ce` - The class entry that we are creating an object for.
185- extern "C" fn create_object ( ce : * mut ClassEntry ) -> * mut ZendObject ;
188+ ///
189+ /// # Safety
190+ ///
191+ /// Caller needs to ensure that the given `ce` is a valid pointer to a [`ClassEntry`].
192+ unsafe extern "C" fn create_object ( ce : * mut ClassEntry ) -> * mut ZendObject ;
186193}
187194
188195/// A Zend class object which is allocated when a PHP
@@ -215,7 +222,7 @@ impl<T: Default> ZendClassObject<T> {
215222 /// them with safety.
216223 pub unsafe fn new_ptr (
217224 ce : * mut ClassEntry ,
218- handlers : * mut ZendObjectHandlers ,
225+ handlers : & ZendObjectHandlers ,
219226 ) -> Result < * mut zend_object > {
220227 let obj = {
221228 let obj = ( ext_php_rs_zend_object_alloc ( std:: mem:: size_of :: < Self > ( ) as _ , ce)
@@ -244,8 +251,8 @@ impl<T: Default> ZendClassObject<T> {
244251 let ptr = ( ex. This . object ( ) ? as * const ZendObject ) as * mut u8 ;
245252 let offset = std:: mem:: size_of :: < T > ( ) ;
246253 unsafe {
247- let ptr = ptr. offset ( 0 - offset as isize ) ;
248- ( ptr as * mut Self ) . as_mut ( )
254+ let ptr = ptr. offset ( 0 - offset as isize ) as * mut Self ;
255+ ptr. as_mut ( )
249256 }
250257 }
251258}
@@ -270,24 +277,73 @@ impl<T: Default> DerefMut for ZendClassObject<T> {
270277 }
271278}
272279
280+ /// Lazy-loading wrapper around the [`ZendObjectHandlers`] type.
281+ pub struct Handlers < T > {
282+ init : AtomicBool ,
283+ handlers : MaybeUninit < ZendObjectHandlers > ,
284+ phantom : PhantomData < T > ,
285+ }
286+
287+ impl < T > Handlers < T > {
288+ /// Creates a new instance of object handlers.
289+ pub const fn new ( ) -> Self {
290+ Self {
291+ init : AtomicBool :: new ( false ) ,
292+ handlers : MaybeUninit :: uninit ( ) ,
293+ phantom : PhantomData ,
294+ }
295+ }
296+
297+ /// Returns an immutable reference to the object handlers, initializing them in the process
298+ /// if they have not already been initialized.
299+ pub fn get ( & self ) -> & ZendObjectHandlers {
300+ self . check_uninit ( ) ;
301+
302+ // SAFETY: `check_uninit` guarantees that the handlers have been initialized.
303+ unsafe { self . handlers . assume_init_ref ( ) }
304+ }
305+
306+ /// Checks if the handlers have been initialized, and initializes them if they have not been.
307+ fn check_uninit ( & self ) {
308+ if !self . init . load ( Ordering :: Acquire ) {
309+ // SAFETY: Memory location has been initialized therefore given pointer is valid.
310+ unsafe {
311+ ZendObjectHandlers :: init :: < T > ( std:: mem:: transmute ( self . handlers . assume_init_ref ( ) ) ) ;
312+ } ;
313+ self . init . store ( true , Ordering :: Release ) ;
314+ }
315+ }
316+ }
317+
273318impl ZendObjectHandlers {
274- /// Creates a new set of object handlers from the standard object handlers, returning a pointer
275- /// to the handlers.
276- pub fn init < T > ( ) -> * mut ZendObjectHandlers {
277- // SAFETY: We are allocating memory for the handlers ourselves, which ensures that
278- // we can copy to the allocated memory. We can also copy from the standard handlers
279- // as the `std_object_handlers` are not modified.
280- unsafe {
281- let s = mem:: size_of :: < Self > ( ) ;
282- let ptr = libc:: malloc ( s) as * mut Self ;
283- libc:: memcpy (
284- ptr as * mut _ ,
285- ( & std_object_handlers as * const Self ) as * mut _ ,
286- s,
287- ) ;
288- let offset = mem:: size_of :: < T > ( ) ;
289- ( * ptr) . offset = offset as i32 ;
290- ptr
319+ /// Initializes a given set of object handlers by copying the standard object handlers into
320+ /// the memory location, as well as setting up the `T` type destructor.
321+ ///
322+ /// # Parameters
323+ ///
324+ //// * `ptr` - Pointer to memory location to copy the standard handlers to.
325+ ///
326+ /// # Safety
327+ ///
328+ /// Caller must guarantee that the `ptr` given is a valid memory location.
329+ pub unsafe fn init < T > ( ptr : * mut ZendObjectHandlers ) {
330+ pub unsafe extern "C" fn free_obj < T > ( object : * mut zend_object ) {
331+ let layout = Layout :: new :: < T > ( ) ;
332+ let offset = layout. size ( ) ;
333+
334+ // Cast to *mut u8 to work in byte offsets
335+ let ptr = ( object as * mut u8 ) . offset ( 0 - offset as isize ) as * mut T ;
336+ let _ = Box :: from_raw ( ptr) ;
337+
338+ match std_object_handlers. free_obj {
339+ Some ( free) => free ( object) ,
340+ None => core:: hint:: unreachable_unchecked ( ) ,
341+ }
291342 }
343+
344+ std:: ptr:: copy_nonoverlapping ( & std_object_handlers, ptr, 1 ) ;
345+ let offset = std:: mem:: size_of :: < T > ( ) ;
346+ ( * ptr) . offset = offset as _ ;
347+ ( * ptr) . free_obj = Some ( free_obj :: < T > ) ;
292348 }
293349}
0 commit comments