2
2
//! allowing users to store Rust data inside a PHP object.
3
3
4
4
use std:: {
5
+ alloc:: Layout ,
5
6
convert:: TryInto ,
6
7
fmt:: Debug ,
7
- mem,
8
+ marker:: PhantomData ,
9
+ mem:: MaybeUninit ,
8
10
ops:: { Deref , DerefMut } ,
11
+ sync:: atomic:: { AtomicBool , Ordering } ,
9
12
} ;
10
13
11
14
use crate :: {
@@ -182,7 +185,11 @@ pub trait ZendObjectOverride {
182
185
/// # Parameters
183
186
///
184
187
/// * `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 ;
186
193
}
187
194
188
195
/// A Zend class object which is allocated when a PHP
@@ -215,7 +222,7 @@ impl<T: Default> ZendClassObject<T> {
215
222
/// them with safety.
216
223
pub unsafe fn new_ptr (
217
224
ce : * mut ClassEntry ,
218
- handlers : * mut ZendObjectHandlers ,
225
+ handlers : & ZendObjectHandlers ,
219
226
) -> Result < * mut zend_object > {
220
227
let obj = {
221
228
let obj = ( ext_php_rs_zend_object_alloc ( std:: mem:: size_of :: < Self > ( ) as _ , ce)
@@ -244,8 +251,8 @@ impl<T: Default> ZendClassObject<T> {
244
251
let ptr = ( ex. This . object ( ) ? as * const ZendObject ) as * mut u8 ;
245
252
let offset = std:: mem:: size_of :: < T > ( ) ;
246
253
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 ( )
249
256
}
250
257
}
251
258
}
@@ -270,24 +277,73 @@ impl<T: Default> DerefMut for ZendClassObject<T> {
270
277
}
271
278
}
272
279
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
+
273
318
impl 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
+ }
291
342
}
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 > ) ;
292
348
}
293
349
}
0 commit comments