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 #[cfg(not(all(target_endian = "big", target_pointer_width = "64")))]
91 {
92 self.0.boxed.ptr as *mut _
93 }
94 #[cfg(all(target_endian = "big", target_pointer_width = "64"))]
95 {
96 self.0.boxed.ptr.swap_bytes() as *mut _
97 }
98 }
99
100 pub fn unpack_mut(&mut self) -> UnpackedMut<'_, T, N, B> {
102 unsafe {
103 if self.is_boxed() {
104 UnpackedMut::Boxed(&mut *self.boxed_ptr())
105 } else {
106 UnpackedMut::Inline(&mut self.0.inl.numeric_tag, &mut self.0.inl.value)
107 }
108 }
109 }
110
111 pub fn unpack(&self) -> Unpacked<'_, T, N, B> {
113 unsafe {
114 if self.is_boxed() {
115 Unpacked::Boxed(&*self.boxed_ptr())
116 } else {
117 Unpacked::Inline(self.0.inl.numeric_tag, self.0.inl.value)
118 }
119 }
120 }
121
122 pub fn extract(self) -> Extracted<T, N, B> {
124 let extracted = unsafe {
125 if self.is_boxed() {
126 Extracted::Boxed(Box::from_raw(self.boxed_ptr()))
127 } else {
128 Extracted::Inline(self.0.inl.numeric_tag, self.0.inl.value)
129 }
130 };
131 mem::forget(self);
132 extracted
133 }
134
135 pub fn inline(numeric_tag: T, value: N) -> Self {
137 Self(NumericUnionImpl {
138 inl: InlineVariant {
139 tag: NUMERIC_UNION_TAG_INLINE,
140 numeric_tag,
141 value,
142 },
143 })
144 }
145
146 pub fn boxed(v: Box<B>) -> Self {
148 let ptr = Box::into_raw(v);
149
150 #[cfg(target_pointer_width = "32")]
151 let boxed = BoxedVariant {
152 tag: 0,
153 ptr,
154 _phantom: marker::PhantomData,
155 };
156
157 #[cfg(target_pointer_width = "64")]
158 let boxed = BoxedVariant {
159 #[cfg(target_endian = "little")]
160 ptr: ptr as usize,
161 #[cfg(target_endian = "big")]
162 ptr: (ptr as usize).swap_bytes(),
163 _phantom: marker::PhantomData,
164 };
165
166 let union = Self(NumericUnionImpl { boxed });
167 debug_assert!(union.is_boxed());
168 union
169 }
170}
171
172impl<T: Copy, N: Copy, B> Drop for NumericUnion<T, N, B> {
173 fn drop(&mut self) {
174 if self.is_boxed() {
175 let _ = unsafe { Box::from_raw(self.boxed_ptr()) };
176 }
177 }
178}
179
180impl<T: Copy, N: Copy, B: Clone> Clone for NumericUnion<T, N, B> {
181 fn clone(&self) -> Self {
182 match self.unpack() {
183 Unpacked::Inline(t, n) => Self::inline(t, n),
184 Unpacked::Boxed(b) => Self::boxed(Box::new(b.clone())),
185 }
186 }
187}
188
189impl<T: Copy, N: Copy, B> MallocSizeOf for NumericUnion<T, N, B> {
190 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
191 match self.unpack() {
192 Unpacked::Boxed(c) => unsafe { ops.malloc_size_of(c) },
193 Unpacked::Inline(..) => 0,
194 }
195 }
196}
197
198impl<T: Copy + ToShmem, N: Copy + ToShmem, B: ToShmem> ToShmem for NumericUnion<T, N, B> {
199 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> {
200 unsafe {
201 Ok(mem::ManuallyDrop::new(if self.is_inline() {
202 let inl = self.0.inl;
203 Self(NumericUnionImpl { inl })
204 } else {
205 let b = mem::ManuallyDrop::new(Box::from_raw(self.boxed_ptr()));
206 let b = (*b).to_shmem(builder)?;
207 Self::boxed(mem::ManuallyDrop::into_inner(b))
208 }))
209 }
210 }
211}
212
213impl<T: Copy + fmt::Debug, N: Copy + fmt::Debug, B: fmt::Debug> fmt::Debug
214 for NumericUnion<T, N, B>
215{
216 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
217 self.unpack().fmt(formatter)
218 }
219}
220
221impl<T: Copy + PartialEq, N: Copy + PartialEq, B: PartialEq> PartialEq for NumericUnion<T, N, B> {
222 fn eq(&self, other: &Self) -> bool {
223 self.unpack() == other.unpack()
224 }
225}
226
227#[derive(Clone, Debug, PartialEq)]
229pub enum Unpacked<'a, T, N, B> {
230 Boxed(&'a B),
232 Inline(T, N),
234}
235
236#[derive(Clone, Debug, PartialEq)]
238pub enum Extracted<T, N, B> {
239 Boxed(Box<B>),
241 Inline(T, N),
243}
244
245pub enum UnpackedMut<'a, T, N, B> {
247 Boxed(&'a mut B),
249 Inline(&'a mut T, &'a mut N),
251}