1use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
13use std::{fmt, marker, mem};
14use to_shmem::{SharedMemoryBuilder, ToShmem};
15
16#[doc(hidden)]
18#[repr(C)]
19#[cfg(target_pointer_width = "32")]
20pub struct BoxedVariant<B> {
21 tag: u8,
22 ptr: *mut B,
23 _phantom: marker::PhantomData<B>,
24}
25
26#[doc(hidden)]
27#[repr(C)]
28#[cfg(target_pointer_width = "64")]
29pub struct BoxedVariant<B> {
30 ptr: usize, _phantom: marker::PhantomData<B>,
32}
33
34impl<B> Copy for BoxedVariant<B> {}
35impl<B> Clone for BoxedVariant<B> {
36 fn clone(&self) -> Self {
37 *self
38 }
39}
40
41unsafe impl<B: Send> Send for BoxedVariant<B> {}
42unsafe impl<B: Sync> Sync for BoxedVariant<B> {}
43
44#[doc(hidden)]
45#[derive(Clone, Copy)]
46#[repr(C)]
47pub struct TagVariant {
48 tag: u8,
49}
50
51#[doc(hidden)]
52#[derive(Clone, Copy)]
53#[repr(C)]
54pub struct InlineVariant<T, N> {
55 tag: u8,
56 numeric_tag: T,
57 value: N,
58}
59
60#[doc(hidden)]
61#[repr(C)]
62pub union NumericUnionImpl<T: Copy, N: Copy, B> {
63 inl: InlineVariant<T, N>,
64 boxed: BoxedVariant<B>,
65 tag: TagVariant,
66}
67
68#[repr(C)]
71pub struct NumericUnion<T: Copy, N: Copy, B>(NumericUnionImpl<T, N, B>);
72
73#[doc(hidden)] pub const NUMERIC_UNION_TAG_INLINE: u8 = 0b1;
75
76impl<T: Copy, N: Copy, B> NumericUnion<T, N, B> {
77 pub fn is_inline(&self) -> bool {
79 unsafe { (self.0.tag.tag & NUMERIC_UNION_TAG_INLINE) != 0 }
80 }
81
82 pub fn is_boxed(&self) -> bool {
84 !self.is_inline()
85 }
86
87 #[inline]
88 unsafe fn boxed_ptr(&self) -> *mut B {
89 debug_assert!(self.is_boxed());
90 unsafe {
91 #[cfg(not(all(target_endian = "big", target_pointer_width = "64")))]
92 {
93 self.0.boxed.ptr as *mut _
94 }
95 #[cfg(all(target_endian = "big", target_pointer_width = "64"))]
96 {
97 self.0.boxed.ptr.swap_bytes() as *mut _
98 }
99 }
100 }
101
102 pub fn unpack_mut(&mut self) -> UnpackedMut<'_, T, N, B> {
104 unsafe {
105 if self.is_boxed() {
106 UnpackedMut::Boxed(&mut *self.boxed_ptr())
107 } else {
108 UnpackedMut::Inline(&mut self.0.inl.numeric_tag, &mut self.0.inl.value)
109 }
110 }
111 }
112
113 pub fn unpack(&self) -> Unpacked<'_, T, N, B> {
115 unsafe {
116 if self.is_boxed() {
117 Unpacked::Boxed(&*self.boxed_ptr())
118 } else {
119 Unpacked::Inline(self.0.inl.numeric_tag, self.0.inl.value)
120 }
121 }
122 }
123
124 pub fn extract(self) -> Extracted<T, N, B> {
126 let extracted = unsafe {
127 if self.is_boxed() {
128 Extracted::Boxed(Box::from_raw(self.boxed_ptr()))
129 } else {
130 Extracted::Inline(self.0.inl.numeric_tag, self.0.inl.value)
131 }
132 };
133 mem::forget(self);
134 extracted
135 }
136
137 pub fn inline(numeric_tag: T, value: N) -> Self {
139 Self(NumericUnionImpl {
140 inl: InlineVariant {
141 tag: NUMERIC_UNION_TAG_INLINE,
142 numeric_tag,
143 value,
144 },
145 })
146 }
147
148 pub fn boxed(v: Box<B>) -> Self {
150 let ptr = Box::into_raw(v);
151
152 #[cfg(target_pointer_width = "32")]
153 let boxed = BoxedVariant {
154 tag: 0,
155 ptr,
156 _phantom: marker::PhantomData,
157 };
158
159 #[cfg(target_pointer_width = "64")]
160 let boxed = BoxedVariant {
161 #[cfg(target_endian = "little")]
162 ptr: ptr as usize,
163 #[cfg(target_endian = "big")]
164 ptr: (ptr as usize).swap_bytes(),
165 _phantom: marker::PhantomData,
166 };
167
168 let union = Self(NumericUnionImpl { boxed });
169 debug_assert!(union.is_boxed());
170 union
171 }
172}
173
174impl<T: Copy, N: Copy, B> Drop for NumericUnion<T, N, B> {
175 fn drop(&mut self) {
176 if self.is_boxed() {
177 let _ = unsafe { Box::from_raw(self.boxed_ptr()) };
178 }
179 }
180}
181
182impl<T: Copy, N: Copy, B: Clone> Clone for NumericUnion<T, N, B> {
183 fn clone(&self) -> Self {
184 match self.unpack() {
185 Unpacked::Inline(t, n) => Self::inline(t, n),
186 Unpacked::Boxed(b) => Self::boxed(Box::new(b.clone())),
187 }
188 }
189}
190
191impl<T: Copy, N: Copy, B> MallocSizeOf for NumericUnion<T, N, B> {
192 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
193 match self.unpack() {
194 Unpacked::Boxed(c) => unsafe { ops.malloc_size_of(c) },
195 Unpacked::Inline(..) => 0,
196 }
197 }
198}
199
200impl<T: Copy + ToShmem, N: Copy + ToShmem, B: ToShmem> ToShmem for NumericUnion<T, N, B> {
201 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
202 unsafe {
203 Ok(mem::ManuallyDrop::new(if self.is_inline() {
204 let inl = self.0.inl;
205 Self(NumericUnionImpl { inl })
206 } else {
207 let b = mem::ManuallyDrop::new(Box::from_raw(self.boxed_ptr()));
208 let b = (*b).to_shmem(builder)?;
209 Self::boxed(mem::ManuallyDrop::into_inner(b))
210 }))
211 }
212 }
213}
214
215impl<T: Copy + fmt::Debug, N: Copy + fmt::Debug, B: fmt::Debug> fmt::Debug
216 for NumericUnion<T, N, B>
217{
218 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
219 self.unpack().fmt(formatter)
220 }
221}
222
223impl<T: Copy + PartialEq, N: Copy + PartialEq, B: PartialEq> PartialEq for NumericUnion<T, N, B> {
224 fn eq(&self, other: &Self) -> bool {
225 self.unpack() == other.unpack()
226 }
227}
228
229#[derive(Clone, Debug, PartialEq)]
231pub enum Unpacked<'a, T, N, B> {
232 Boxed(&'a B),
234 Inline(T, N),
236}
237
238#[derive(Clone, Debug, PartialEq)]
240pub enum Extracted<T, N, B> {
241 Boxed(Box<B>),
243 Inline(T, N),
245}
246
247pub enum UnpackedMut<'a, T, N, B> {
249 Boxed(&'a mut B),
251 Inline(&'a mut T, &'a mut N),
253}