1 // Take a look at the license at the top of the repository in the LICENSE file. 2 3 use std::marker::PhantomData; 4 5 use crate::ffi::cairo_user_data_key_t; 6 7 pub struct UserDataKey<T> { 8 pub(crate) ffi: cairo_user_data_key_t, 9 marker: PhantomData<*const T>, 10 } 11 12 unsafe impl<T> Sync for UserDataKey<T> {} 13 14 impl<T> UserDataKey<T> { new() -> Self15 pub const fn new() -> Self { 16 Self { 17 ffi: cairo_user_data_key_t { unused: 0 }, 18 marker: PhantomData, 19 } 20 } 21 } 22 23 // In a safe API for user data we can’t make `get_user_data` 24 // transfer full ownership of the value to the caller (e.g. by returning `Box<T>`) 25 // because `self` still has a pointer to that value 26 // and `get_user_data` could be called again with the same key. 27 // 28 // We also can’t return a `&T` reference that borrows from `self` 29 // because the value could be removed with `remove_user_data` or replaced with `set_user_data` 30 // while the borrow still needs to be valid. 31 // (Borrowing with `&mut self` would not help as `Self` can be itself reference-counted.) 32 // 33 // Therefore, the value must be reference-counted. 34 // 35 // We use `Rc` over `Arc` because the types implementing these methods are `!Send` and `!Sync`. 36 // See <https://github.com/gtk-rs/cairo/issues/256> 37 38 macro_rules! user_data_methods { 39 ($ffi_get_user_data: path, $ffi_set_user_data: path,) => { 40 /// Attach user data to `self` for the given `key`. 41 pub fn set_user_data<T: 'static>( 42 &self, 43 key: &'static crate::UserDataKey<T>, 44 value: std::rc::Rc<T>, 45 ) -> Result<(), crate::Error> { 46 unsafe extern "C" fn destructor<T>(ptr: *mut libc::c_void) { 47 let ptr: *const T = ptr as _; 48 drop(std::rc::Rc::from_raw(ptr)) 49 } 50 // Safety: 51 // 52 // The destructor’s cast and `from_raw` are symmetric 53 // with the `into_raw` and cast below. 54 // They both transfer ownership of one strong reference: 55 // neither of them touches the reference count. 56 let ptr: *const T = std::rc::Rc::into_raw(value); 57 let ptr = ptr as *mut T as *mut libc::c_void; 58 let status = unsafe { 59 $ffi_set_user_data(self.to_raw_none(), &key.ffi, ptr, Some(destructor::<T>)) 60 }; 61 crate::utils::status_to_result(status) 62 } 63 64 /// Return the user data previously attached to `self` with the given `key`, if any. 65 pub fn user_data<T: 'static>( 66 &self, 67 key: &'static crate::UserDataKey<T>, 68 ) -> Option<std::rc::Rc<T>> { 69 let ptr = self.user_data_ptr(key)?.as_ptr(); 70 71 // Safety: 72 // 73 // `Rc::from_raw` would normally take ownership of a strong reference for this pointer. 74 // But `self` still has a copy of that pointer and `get_user_data` can be called again 75 // with the same key. 76 // We use `ManuallyDrop` to avoid running the destructor of that first `Rc`, 77 // and return a cloned one (which increments the reference count). 78 unsafe { 79 let rc = std::mem::ManuallyDrop::new(std::rc::Rc::from_raw(ptr)); 80 Some(std::rc::Rc::clone(&rc)) 81 } 82 } 83 84 /// Return the user data previously attached to `self` with the given `key`, if any, 85 /// without incrementing the reference count. 86 /// 87 /// The pointer is valid when it is returned from this method, 88 /// until the cairo object that `self` represents is destroyed 89 /// or `remove_user_data` or `set_user_data` is called with the same key. 90 pub fn user_data_ptr<T: 'static>( 91 &self, 92 key: &'static crate::UserDataKey<T>, 93 ) -> Option<std::ptr::NonNull<T>> { 94 // Safety: 95 // 96 // If `ffi_get_user_data` returns a non-null pointer, 97 // there was a previous call to `ffi_set_user_data` with a key with the same address. 98 // Either: 99 // 100 // * This was a call to a Rust `Self::set_user_data` method. 101 // Because that method takes a `&'static` reference, 102 // the key used then must live at that address until the end of the process. 103 // Because `UserDataKey<T>` has a non-zero size regardless of `T`, 104 // no other `UserDataKey<U>` value can have the same address. 105 // Therefore, the `T` type was the same then at it is now and `cast` is type-safe. 106 // 107 // * Or, it is technically possible that the `set` call was to the C function directly, 108 // with a `cairo_user_data_key_t` in heap-allocated memory that was then freed, 109 // then `Box::new(UserDataKey::new()).leak()` was used to create a `&'static` 110 // that happens to have the same address because the allocator for `Box` 111 // reused that memory region. 112 // Since this involves a C (or FFI) call *and* is so far out of “typical” use 113 // of the user data functionality, we consider this a misuse of an unsafe API. 114 unsafe { 115 let ptr = $ffi_get_user_data(self.to_raw_none(), &key.ffi); 116 Some(std::ptr::NonNull::new(ptr)?.cast()) 117 } 118 } 119 120 /// Unattached from `self` the user data associated with `key`, if any. 121 /// If there is no other `Rc` strong reference, the data is destroyed. 122 pub fn remove_user_data<T: 'static>( 123 &self, 124 key: &'static crate::UserDataKey<T>, 125 ) -> Result<(), crate::Error> { 126 let status = unsafe { 127 $ffi_set_user_data(self.to_raw_none(), &key.ffi, std::ptr::null_mut(), None) 128 }; 129 crate::utils::status_to_result(status) 130 } 131 }; 132 } 133