hstr/
tagged_value.rs

1#![allow(clippy::missing_transmute_annotations)]
2
3use std::{num::NonZeroU8, os::raw::c_void, ptr::NonNull, slice};
4
5#[cfg(feature = "atom_size_128")]
6type RawTaggedValue = u128;
7#[cfg(any(
8    target_pointer_width = "32",
9    target_pointer_width = "16",
10    feature = "atom_size_64"
11))]
12type RawTaggedValue = u64;
13#[cfg(not(any(
14    target_pointer_width = "32",
15    target_pointer_width = "16",
16    feature = "atom_size_64",
17    feature = "atom_size_128"
18)))]
19type RawTaggedValue = usize;
20
21#[cfg(feature = "atom_size_128")]
22type RawTaggedNonZeroValue = std::num::NonZeroU128;
23#[cfg(any(
24    target_pointer_width = "32",
25    target_pointer_width = "16",
26    feature = "atom_size_64"
27))]
28type RawTaggedNonZeroValue = std::num::NonZeroU64;
29#[cfg(not(any(
30    target_pointer_width = "32",
31    target_pointer_width = "16",
32    feature = "atom_size_64",
33    feature = "atom_size_128"
34)))]
35type RawTaggedNonZeroValue = std::ptr::NonNull<()>;
36
37pub(crate) const MAX_INLINE_LEN: usize = std::mem::size_of::<TaggedValue>() - 1;
38
39#[derive(Copy, Clone, Debug, PartialEq, Eq)]
40#[repr(transparent)]
41pub(crate) struct TaggedValue {
42    value: RawTaggedNonZeroValue,
43}
44
45impl TaggedValue {
46    #[inline(always)]
47    pub fn new_ptr<T>(value: NonNull<T>) -> Self {
48        #[cfg(any(
49            target_pointer_width = "32",
50            target_pointer_width = "16",
51            feature = "atom_size_64",
52            feature = "atom_size_128"
53        ))]
54        unsafe {
55            let value: std::num::NonZeroUsize = std::mem::transmute(value);
56            Self {
57                value: RawTaggedNonZeroValue::new_unchecked(value.get() as _),
58            }
59        }
60
61        #[cfg(not(any(
62            target_pointer_width = "32",
63            target_pointer_width = "16",
64            feature = "atom_size_64",
65            feature = "atom_size_128"
66        )))]
67        {
68            Self {
69                value: value.cast(),
70            }
71        }
72    }
73
74    #[inline(always)]
75    pub const fn new_tag(value: NonZeroU8) -> Self {
76        let value = value.get() as RawTaggedValue;
77        Self {
78            value: unsafe { std::mem::transmute(value) },
79        }
80    }
81
82    #[inline(always)]
83    pub fn hash(&self) -> u64 {
84        self.get_value() as _
85    }
86
87    #[inline(always)]
88    pub fn get_ptr(&self) -> *const c_void {
89        #[cfg(any(
90            target_pointer_width = "32",
91            target_pointer_width = "16",
92            feature = "atom_size_64",
93            feature = "atom_size_128"
94        ))]
95        {
96            self.value.get() as usize as _
97        }
98        #[cfg(not(any(
99            target_pointer_width = "32",
100            target_pointer_width = "16",
101            feature = "atom_size_64",
102            feature = "atom_size_128"
103        )))]
104        unsafe {
105            std::mem::transmute(Some(self.value))
106        }
107    }
108
109    #[inline(always)]
110    fn get_value(&self) -> RawTaggedValue {
111        unsafe { std::mem::transmute(Some(self.value)) }
112    }
113
114    #[inline(always)]
115    pub fn tag(&self) -> u8 {
116        (self.get_value() & 0xff) as u8
117    }
118
119    pub fn data(&self) -> &[u8] {
120        let x: *const _ = &self.value;
121        let mut data = x as *const u8;
122        // All except the lowest byte, which is first in little-endian, last in
123        // big-endian.
124        if cfg!(target_endian = "little") {
125            unsafe {
126                data = data.offset(1);
127            }
128        }
129        let len = std::mem::size_of::<TaggedValue>() - 1;
130        unsafe { slice::from_raw_parts(data, len) }
131    }
132
133    /// The `TaggedValue` is a non-zero number or pointer, so caution must be
134    /// used when setting the untagged slice part of this value. If tag is
135    /// zero and the slice is zeroed out, using this `TaggedValue` will be
136    /// UB!
137    pub const unsafe fn data_mut(&mut self) -> &mut [u8] {
138        let x: *mut _ = &mut self.value;
139        let mut data = x as *mut u8;
140        // All except the lowest byte, which is first in little-endian, last in
141        // big-endian.
142        if cfg!(target_endian = "little") {
143            data = data.offset(1);
144        }
145        let len = std::mem::size_of::<TaggedValue>() - 1;
146        slice::from_raw_parts_mut(data, len)
147    }
148}