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