1use std::cmp::Ordering;
15use std::ffi::c_void;
16use std::fmt;
17use std::hash::{Hash, Hasher};
18use std::marker::PhantomData;
19use std::ptr::NonNull;
20use triomphe::ThinArc;
21
22#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
29pub struct IsizeInPtr {
30 ptr: NonNull<c_void>,
32}
33
34impl IsizeInPtr {
35 pub const MAX: isize = isize::MAX >> 1;
37 pub const MIN: isize = isize::MIN >> 1;
38 const TAG_MASK: isize = 1;
39
40 fn from_ptr(ptr: *const c_void) -> Option<Self> {
41 if (ptr as isize & Self::TAG_MASK) == 0 {
42 None
43 } else {
44 Some(unsafe { Self::new_unchecked(ptr) })
45 }
46 }
47
48 fn from_isize_unchecked(value: isize) -> Self {
49 let tagged = (value << 1) | Self::TAG_MASK;
50 unsafe { Self::new_unchecked(tagged as *const c_void) }
51 }
52
53 unsafe fn new_unchecked(ptr: *const c_void) -> Self {
54 IsizeInPtr {
55 ptr: unsafe { NonNull::from_ref(&*ptr) },
56 }
57 }
58
59 pub fn get(&self) -> isize {
61 (self.ptr.as_ptr() as isize) >> 1
62 }
63}
64
65impl Default for IsizeInPtr {
66 fn default() -> Self {
67 Self::from_isize_unchecked(0)
68 }
69}
70
71impl From<IsizeInPtr> for isize {
72 fn from(value: IsizeInPtr) -> isize {
74 value.get()
75 }
76}
77
78impl TryFrom<isize> for IsizeInPtr {
79 type Error = TryFromIsizeError<isize>;
80
81 fn try_from(value: isize) -> Result<IsizeInPtr, Self::Error> {
83 if (value <= Self::MAX) && (value >= Self::MIN) {
84 Ok(Self::from_isize_unchecked(value))
85 } else {
86 Err(TryFromIsizeError { original: value })
87 }
88 }
89}
90
91macro_rules! impl_try_from_for_integral {
92 ($($t:ty),*) => {
93 $(
94 impl TryFrom<$t> for IsizeInPtr {
95 type Error = TryFromIsizeError<$t>;
96
97 fn try_from(value: $t) -> Result<Self, Self::Error> {
98 isize::try_from(value).ok()
99 .and_then(|n: isize| IsizeInPtr::try_from(n).ok())
100 .ok_or(TryFromIsizeError{ original: value })
101 }
102 }
103 )*
104 };
105}
106impl_try_from_for_integral!(i8, i16, i32, i64, i128);
107
108#[derive(Debug, Clone, Copy)]
110pub struct TryFromIsizeError<N> {
111 pub original: N,
112}
113
114impl<N: std::fmt::Display> std::fmt::Display for TryFromIsizeError<N> {
115 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
116 write!(
117 f,
118 "Value {} doesn't fit inside taggeed isize bounds [{}, {}]",
119 self.original,
120 IsizeInPtr::MIN,
121 IsizeInPtr::MAX
122 )
123 }
124}
125
126impl<N: std::fmt::Display + std::fmt::Debug> std::error::Error for TryFromIsizeError<N> {}
127
128pub struct ThinArcOrInt<H, T> {
134 raw: NonNull<c_void>,
136 _marker: PhantomData<ThinArc<H, T>>,
137}
138
139unsafe impl<H, T> Send for ThinArcOrInt<H, T> where ThinArc<H, T>: Send {}
140unsafe impl<H, T> Sync for ThinArcOrInt<H, T> where ThinArc<H, T>: Sync {}
141
142impl<H, T> ThinArcOrInt<H, T> {
143 pub fn from_isize(val: IsizeInPtr) -> Self {
146 Self {
147 raw: val.ptr,
148 _marker: PhantomData,
149 }
150 }
151
152 pub fn from_arc(arc: ThinArc<H, T>) -> Self {
155 let ptr = ThinArc::into_raw(arc);
156 debug_assert!(
157 IsizeInPtr::from_ptr(ptr).is_none(),
158 "Pointer must be 2-aligned!"
159 );
160 Self {
162 raw: unsafe { NonNull::from_ref(&*ptr) },
163 _marker: PhantomData,
164 }
165 }
166
167 pub fn from_convertible<U, E>(value: U, slice: &[T]) -> Self
171 where
172 U: TryInto<IsizeInPtr, Error = E> + Into<H>,
173 E: Into<H>,
174 T: Copy,
175 {
176 if slice.is_empty() {
177 match value.try_into() {
178 Ok(i) => Self::from_isize(i),
179 Err(e) => {
180 Self::from_arc(ThinArc::from_header_and_iter(e.into(), std::iter::empty()))
181 }
182 }
183 } else {
184 Self::from_arc(ThinArc::from_header_and_slice(value.into(), slice))
185 }
186 }
187
188 pub fn has_number(&self) -> bool {
191 self.as_isize().is_some()
192 }
193
194 pub fn has_ref(&self) -> bool {
196 !self.has_number()
197 }
198
199 pub fn as_isize(&self) -> Option<isize> {
201 IsizeInPtr::from_ptr(self.raw.as_ptr()).map(|i| i.into())
202 }
203
204 pub fn as_arc(&self) -> Option<&ThinArc<H, T>> {
206 if self.has_ref() {
207 unsafe { Some(self.as_arc_internal()) }
208 } else {
209 None
210 }
211 }
212
213 unsafe fn as_arc_internal(&self) -> &ThinArc<H, T> {
214 &*(&self.raw as *const NonNull<c_void> as *const ThinArc<H, T>)
215 }
216}
217
218impl<H, T> Default for ThinArcOrInt<H, T> {
219 fn default() -> Self {
220 Self::from_isize(Default::default())
221 }
222}
223
224impl<H, T> Drop for ThinArcOrInt<H, T> {
225 fn drop(&mut self) {
226 if self.has_ref() {
227 let _arc = unsafe { ThinArc::<H, T>::from_raw(self.raw.as_ptr()) };
228 }
229 }
230}
231
232impl<H, T> Clone for ThinArcOrInt<H, T> {
233 fn clone(&self) -> Self {
234 if self.has_number() {
235 Self {
236 raw: self.raw,
237 _marker: PhantomData,
238 }
239 } else {
240 let arc = unsafe { self.as_arc_internal() };
241 let cloned_arc = arc.clone();
242 Self::from_arc(cloned_arc)
243 }
244 }
245}
246
247impl<H, T> PartialEq for ThinArcOrInt<H, T>
248where
249 H: PartialEq,
250 T: PartialEq,
251{
252 fn eq(&self, other: &Self) -> bool {
253 match (self.as_isize(), other.as_isize()) {
254 (Some(s), Some(o)) => s == o,
255 (None, None) => unsafe { self.as_arc_internal() == other.as_arc_internal() },
256 _ => false,
257 }
258 }
259}
260
261impl<H, T> Eq for ThinArcOrInt<H, T>
262where
263 H: Eq,
264 T: Eq,
265{
266}
267
268impl<H, T> PartialOrd for ThinArcOrInt<H, T>
269where
270 H: PartialOrd,
271 T: PartialOrd,
272{
273 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
274 match (self.as_isize(), other.as_isize()) {
275 (Some(s), Some(o)) => s.partial_cmp(&o),
276 (None, None) => unsafe { self.as_arc_internal().partial_cmp(other.as_arc_internal()) },
277 (Some(_), None) => Some(Ordering::Less),
278 (None, Some(_)) => Some(Ordering::Greater),
279 }
280 }
281}
282
283impl<H, T> Ord for ThinArcOrInt<H, T>
284where
285 H: Ord,
286 T: Ord,
287{
288 fn cmp(&self, other: &Self) -> Ordering {
289 self.partial_cmp(other)
290 .expect("ThinArc::partial_cmp returned `None`")
291 }
292}
293
294impl<H, T> Hash for ThinArcOrInt<H, T>
295where
296 H: Hash,
297 T: Hash,
298{
299 fn hash<S: Hasher>(&self, state: &mut S) {
300 if let Some(num) = self.as_isize() {
301 0.hash(state);
302 num.hash(state);
303 } else {
304 1.hash(state);
305 unsafe { self.as_arc_internal().hash(state) };
306 }
307 }
308}
309
310impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArcOrInt<H, T> {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 if let Some(num) = self.as_isize() {
313 f.debug_tuple("ThinArcOrInt::Number").field(&num).finish()
314 } else {
315 f.debug_tuple("ThinArcOrInt::ThinArc")
316 .field(self.as_arc().unwrap())
317 .finish()
318 }
319 }
320}
321
322#[cfg(test)]
323mod tests {
324 use super::*;
325
326 fn from_isize(value: isize) -> ThinArcOrInt<(), ()> {
327 ThinArcOrInt::<(), ()>::from_isize(
328 IsizeInPtr::try_from(value).expect("Out of allowed bounds"),
329 )
330 }
331
332 #[test]
333 fn test_size() {
334 assert_eq!(
335 std::mem::size_of::<ThinArcOrInt<(), String>>(),
336 std::mem::size_of::<usize>()
337 );
338 }
339
340 #[test]
341 fn test_clone_and_drop() {
342 let arc = ThinArc::from_header_and_slice((), "Shared data".as_bytes());
343 let val1 = ThinArcOrInt::from_arc(arc);
344
345 let val2 = val1.clone();
346
347 assert_eq!(&val1.as_arc().unwrap().slice, "Shared data".as_bytes());
348 assert!(std::ptr::eq(
349 &val1.as_arc().unwrap().slice,
350 &val2.as_arc().unwrap().slice
351 ));
352 }
353
354 #[test]
355 fn test_option_size_optimization() {
356 assert_eq!(
357 std::mem::size_of::<ThinArcOrInt<(), String>>(),
358 std::mem::size_of::<usize>()
359 );
360 assert_eq!(
361 std::mem::size_of::<Option<ThinArcOrInt<(), String>>>(),
362 std::mem::size_of::<usize>()
363 );
364 assert_ne!(None, Some(from_isize(0)));
365 }
366
367 #[test]
368 fn test_negative_numbers() {
369 let negative = from_isize(-42);
370 assert!(negative.has_number());
371 assert_eq!(negative.as_isize(), Some(-42));
372 }
373
374 #[test]
375 fn test_max_number() {
376 let negative = from_isize(IsizeInPtr::MAX);
377 assert!(negative.has_number());
378 assert_eq!(negative.as_isize(), Some(IsizeInPtr::MAX));
379 }
380
381 #[test]
382 fn test_min_number() {
383 let negative = from_isize(IsizeInPtr::MIN);
384 assert!(negative.has_number());
385 assert_eq!(negative.as_isize(), Some(IsizeInPtr::MIN));
386 }
387
388 #[test]
389 fn test_thin_arc() {
390 let arc = ThinArc::from_header_and_slice((), b"Hello Rust");
391 let val = ThinArcOrInt::from_arc(arc);
392
393 assert!(val.has_ref());
394 assert!(!val.has_number());
395 assert_eq!(
396 &val.as_arc().expect("Must be Arc, not isize").slice,
397 b"Hello Rust"
398 );
399 }
400}