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> {
137 raw: NonNull<c_void>,
139 _marker: PhantomData<ThinArc<H, T>>,
140}
141
142unsafe impl<H, T> Send for ThinArcOrInt<H, T> where ThinArc<H, T>: Send {}
143unsafe impl<H, T> Sync for ThinArcOrInt<H, T> where ThinArc<H, T>: Sync {}
144
145impl<H, T> ThinArcOrInt<H, T> {
146 pub fn from_isize(val: IsizeInPtr) -> Self {
149 Self {
150 raw: val.ptr,
151 _marker: PhantomData,
152 }
153 }
154
155 pub fn from_arc(arc: ThinArc<H, T>) -> Self {
158 let ptr = ThinArc::into_raw(arc);
159 debug_assert!(
160 IsizeInPtr::from_ptr(ptr).is_none(),
161 "Pointer must be 2-aligned!"
162 );
163 Self {
165 raw: unsafe { NonNull::from_ref(&*ptr) },
166 _marker: PhantomData,
167 }
168 }
169
170 pub fn from_convertible<U, E>(value: U, slice: &[T]) -> Self
174 where
175 U: TryInto<IsizeInPtr, Error = E> + Into<H>,
176 E: Into<H>,
177 T: Copy,
178 {
179 if slice.is_empty() {
180 match value.try_into() {
181 Ok(i) => Self::from_isize(i),
182 Err(e) => {
183 Self::from_arc(ThinArc::from_header_and_iter(e.into(), std::iter::empty()))
184 }
185 }
186 } else {
187 Self::from_arc(ThinArc::from_header_and_slice(value.into(), slice))
188 }
189 }
190
191 pub fn has_number(&self) -> bool {
194 self.as_isize().is_some()
195 }
196
197 pub fn has_ref(&self) -> bool {
199 !self.has_number()
200 }
201
202 pub fn as_isize(&self) -> Option<isize> {
204 IsizeInPtr::from_ptr(self.raw.as_ptr()).map(|i| i.into())
205 }
206
207 pub fn as_arc(&self) -> Option<&ThinArc<H, T>> {
209 if self.has_ref() {
210 unsafe { Some(self.as_arc_internal()) }
211 } else {
212 None
213 }
214 }
215
216 unsafe fn as_arc_internal(&self) -> &ThinArc<H, T> {
217 &*(&self.raw as *const NonNull<c_void> as *const ThinArc<H, T>)
218 }
219}
220
221impl<H, T> Default for ThinArcOrInt<H, T> {
222 fn default() -> Self {
223 Self::from_isize(Default::default())
224 }
225}
226
227impl<H, T> Drop for ThinArcOrInt<H, T> {
228 fn drop(&mut self) {
229 if self.has_ref() {
230 let _arc = unsafe { ThinArc::<H, T>::from_raw(self.raw.as_ptr()) };
231 }
232 }
233}
234
235impl<H, T> Clone for ThinArcOrInt<H, T> {
236 fn clone(&self) -> Self {
237 if self.has_number() {
238 Self {
239 raw: self.raw,
240 _marker: PhantomData,
241 }
242 } else {
243 let arc = unsafe { self.as_arc_internal() };
244 let cloned_arc = arc.clone();
245 Self::from_arc(cloned_arc)
246 }
247 }
248}
249
250impl<H, T> PartialEq for ThinArcOrInt<H, T>
251where
252 H: PartialEq,
253 T: PartialEq,
254{
255 fn eq(&self, other: &Self) -> bool {
256 match (self.as_isize(), other.as_isize()) {
257 (Some(s), Some(o)) => s == o,
258 (None, None) => unsafe { self.as_arc_internal() == other.as_arc_internal() },
259 _ => false,
260 }
261 }
262}
263
264impl<H, T> Eq for ThinArcOrInt<H, T>
265where
266 H: Eq,
267 T: Eq,
268{
269}
270
271impl<H, T> PartialOrd for ThinArcOrInt<H, T>
272where
273 H: PartialOrd,
274 T: PartialOrd,
275{
276 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
277 match (self.as_isize(), other.as_isize()) {
278 (Some(s), Some(o)) => s.partial_cmp(&o),
279 (None, None) => unsafe { self.as_arc_internal().partial_cmp(other.as_arc_internal()) },
280 (Some(_), None) => Some(Ordering::Less),
281 (None, Some(_)) => Some(Ordering::Greater),
282 }
283 }
284}
285
286impl<H, T> Ord for ThinArcOrInt<H, T>
287where
288 H: Ord,
289 T: Ord,
290{
291 fn cmp(&self, other: &Self) -> Ordering {
292 self.partial_cmp(other)
293 .expect("ThinArc::partial_cmp returned `None`")
294 }
295}
296
297impl<H, T> Hash for ThinArcOrInt<H, T>
298where
299 H: Hash,
300 T: Hash,
301{
302 fn hash<S: Hasher>(&self, state: &mut S) {
303 if let Some(num) = self.as_isize() {
304 0.hash(state);
305 num.hash(state);
306 } else {
307 1.hash(state);
308 unsafe { self.as_arc_internal().hash(state) };
309 }
310 }
311}
312
313impl<H: fmt::Debug, T: fmt::Debug> fmt::Debug for ThinArcOrInt<H, T> {
314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
315 if let Some(num) = self.as_isize() {
316 f.debug_tuple("ThinArcOrInt::Number").field(&num).finish()
317 } else {
318 f.debug_tuple("ThinArcOrInt::ThinArc")
319 .field(self.as_arc().unwrap())
320 .finish()
321 }
322 }
323}
324
325#[cfg(test)]
326mod tests {
327 use super::*;
328
329 fn from_isize(value: isize) -> ThinArcOrInt<(), ()> {
330 ThinArcOrInt::<(), ()>::from_isize(
331 IsizeInPtr::try_from(value).expect("Out of allowed bounds"),
332 )
333 }
334
335 #[test]
336 fn test_size() {
337 assert_eq!(
338 std::mem::size_of::<ThinArcOrInt<(), String>>(),
339 std::mem::size_of::<usize>()
340 );
341 }
342
343 #[test]
344 fn test_clone_and_drop() {
345 let arc = ThinArc::from_header_and_slice((), "Shared data".as_bytes());
346 let val1 = ThinArcOrInt::from_arc(arc);
347
348 let val2 = val1.clone();
349
350 assert_eq!(&val1.as_arc().unwrap().slice, "Shared data".as_bytes());
351 assert!(std::ptr::eq(
352 &val1.as_arc().unwrap().slice,
353 &val2.as_arc().unwrap().slice
354 ));
355 }
356
357 #[test]
358 fn test_option_size_optimization() {
359 assert_eq!(
360 std::mem::size_of::<ThinArcOrInt<(), String>>(),
361 std::mem::size_of::<usize>()
362 );
363 assert_eq!(
364 std::mem::size_of::<Option<ThinArcOrInt<(), String>>>(),
365 std::mem::size_of::<usize>()
366 );
367 assert_ne!(None, Some(from_isize(0)));
368 }
369
370 #[test]
371 fn test_negative_numbers() {
372 let negative = from_isize(-42);
373 assert!(negative.has_number());
374 assert_eq!(negative.as_isize(), Some(-42));
375 }
376
377 #[test]
378 fn test_max_number() {
379 let negative = from_isize(IsizeInPtr::MAX);
380 assert!(negative.has_number());
381 assert_eq!(negative.as_isize(), Some(IsizeInPtr::MAX));
382 }
383
384 #[test]
385 fn test_min_number() {
386 let negative = from_isize(IsizeInPtr::MIN);
387 assert!(negative.has_number());
388 assert_eq!(negative.as_isize(), Some(IsizeInPtr::MIN));
389 }
390
391 #[test]
392 fn test_thin_arc() {
393 let arc = ThinArc::from_header_and_slice((), b"Hello Rust");
394 let val = ThinArcOrInt::from_arc(arc);
395
396 assert!(val.has_ref());
397 assert!(!val.has_number());
398 assert_eq!(
399 &val.as_arc().expect("Must be Arc, not isize").slice,
400 b"Hello Rust"
401 );
402 }
403}