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 124 125 126 127 128 129 130 131 132
// Take a look at the license at the top of the repository in the LICENSE file.
use std::marker::PhantomData;
use crate::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 {
Self {
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>,
) -> Result<(), crate::Error> {
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 symmetric
// 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 status = unsafe {
$ffi_set_user_data(self.to_raw_none(), &key.ffi, ptr, Some(destructor::<T>))
};
crate::utils::status_to_result(status)
}
/// Return the user data previously attached to `self` with the given `key`, if any.
pub fn user_data<T: 'static>(
&self,
key: &'static crate::UserDataKey<T>,
) -> Option<std::rc::Rc<T>> {
let ptr = self.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 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())
}
}
/// Unattached 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>,
) -> Result<(), crate::Error> {
let status = unsafe {
$ffi_set_user_data(self.to_raw_none(), &key.ffi, std::ptr::null_mut(), None)
};
crate::utils::status_to_result(status)
}
};
}