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