1use std::fmt;
2use std::hash::{Hash, Hasher};
3use std::marker::PhantomData;
4use std::ptr::NonNull;
5
6#[derive(Copy, Clone)]
41pub struct GcHandle {
42 raw: NonNull<()>,
43 epoch: usize,
44 _not_send_sync: PhantomData<*const ()>,
45}
46
47impl GcHandle {
48 #[doc(hidden)]
54 pub unsafe fn from_ptr_unchecked(raw: NonNull<()>) -> Self {
55 Self {
56 raw,
57 epoch: 0,
58 _not_send_sync: PhantomData,
59 }
60 }
61
62 #[doc(hidden)]
68 pub unsafe fn from_parts_unchecked(raw: NonNull<()>, epoch: usize) -> Self {
69 Self {
70 raw,
71 epoch,
72 _not_send_sync: PhantomData,
73 }
74 }
75
76 pub fn addr(&self) -> usize {
77 self.raw.as_ptr() as usize
78 }
79
80 #[doc(hidden)]
81 pub fn epoch(&self) -> usize {
82 self.epoch
83 }
84
85 #[doc(hidden)]
91 pub unsafe fn as_ptr_unchecked(&self) -> NonNull<()> {
92 self.raw
93 }
94}
95
96impl PartialEq for GcHandle {
97 fn eq(&self, other: &Self) -> bool {
98 self.raw == other.raw && self.epoch == other.epoch
99 }
100}
101
102impl Eq for GcHandle {}
103
104impl Hash for GcHandle {
105 fn hash<H: Hasher>(&self, state: &mut H) {
106 self.raw.hash(state);
107 self.epoch.hash(state)
108 }
109}
110
111impl fmt::Debug for GcHandle {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "GcHandle({:p}@{})", self.raw.as_ptr(), self.epoch)
114 }
115}
116
117impl fmt::Display for GcHandle {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 write!(f, "{:p}", self.raw.as_ptr())
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::GcHandle;
126 use std::collections::hash_map::DefaultHasher;
127 use std::hash::{Hash, Hasher};
128 use std::ptr::NonNull;
129
130 #[test]
131 fn equality_is_pointer_identity_not_pointee_value() {
132 let raw_a = Box::into_raw(Box::new(7_u64));
133 let raw_b = Box::into_raw(Box::new(7_u64));
134 let ptr_a = unsafe {
135 GcHandle::from_ptr_unchecked(NonNull::new(raw_a.cast()).expect("non-null test pointer"))
136 };
137 let ptr_b = unsafe {
138 GcHandle::from_ptr_unchecked(NonNull::new(raw_b.cast()).expect("non-null test pointer"))
139 };
140
141 assert_eq!(ptr_a, ptr_a);
142 assert_ne!(ptr_a, ptr_b);
143
144 unsafe {
145 drop(Box::from_raw(raw_a));
146 drop(Box::from_raw(raw_b));
147 }
148 }
149
150 #[test]
151 fn hash_includes_pointer_and_epoch_identity() {
152 let raw_a = Box::into_raw(Box::new(7_u64));
153 let raw_b = Box::into_raw(Box::new(7_u64));
154 let ptr_a = unsafe {
155 GcHandle::from_parts_unchecked(
156 NonNull::new(raw_a.cast()).expect("non-null test pointer"),
157 1,
158 )
159 };
160 let ptr_a_next_epoch = unsafe {
161 GcHandle::from_parts_unchecked(
162 NonNull::new(raw_a.cast()).expect("non-null test pointer"),
163 2,
164 )
165 };
166 let ptr_b = unsafe {
167 GcHandle::from_parts_unchecked(
168 NonNull::new(raw_b.cast()).expect("non-null test pointer"),
169 1,
170 )
171 };
172
173 let mut ptr_hash = DefaultHasher::new();
174 ptr_a.hash(&mut ptr_hash);
175 let mut same_hash = DefaultHasher::new();
176 ptr_a.hash(&mut same_hash);
177 let mut next_epoch_hash = DefaultHasher::new();
178 ptr_a_next_epoch.hash(&mut next_epoch_hash);
179
180 assert_eq!(ptr_hash.finish(), same_hash.finish());
181 assert_ne!(ptr_hash.finish(), next_epoch_hash.finish());
182 assert_ne!(ptr_a, ptr_a_next_epoch);
183 assert_ne!(ptr_a, ptr_b);
184
185 unsafe {
186 drop(Box::from_raw(raw_a));
187 drop(Box::from_raw(raw_b));
188 }
189 }
190
191 #[test]
192 fn debug_and_display_do_not_dereference_pointee() {
193 let raw = Box::into_raw(Box::new(7_u64));
194 let ptr = unsafe {
195 GcHandle::from_ptr_unchecked(NonNull::new(raw.cast()).expect("non-null test pointer"))
196 };
197
198 assert!(format!("{ptr:?}").starts_with("GcHandle(0x"));
199 assert!(ptr.to_string().starts_with("0x"));
200
201 unsafe {
202 drop(Box::from_raw(raw));
203 }
204 }
205}