vortex_array/arrays/varbinview/
view.rs1use std::fmt;
7use std::hash::Hash;
8use std::hash::Hasher;
9use std::ops::Range;
10
11use static_assertions::assert_eq_align;
12use static_assertions::assert_eq_size;
13use vortex_error::VortexExpect;
14
15#[derive(Clone, Copy)]
20#[repr(C, align(16))]
21pub union BinaryView {
22 pub(crate) le_bytes: [u8; 16],
25
26 pub(crate) inlined: Inlined,
28
29 pub(crate) _ref: Ref,
31}
32
33assert_eq_align!(BinaryView, u128);
34assert_eq_size!(BinaryView, [u8; 16]);
35assert_eq_size!(Inlined, [u8; 16]);
36assert_eq_size!(Ref, [u8; 16]);
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
40#[repr(C, align(8))]
41pub struct Inlined {
42 pub size: u32,
44 pub data: [u8; BinaryView::MAX_INLINED_SIZE],
46}
47
48impl Inlined {
49 fn new<const N: usize>(value: &[u8]) -> Self {
51 debug_assert_eq!(value.len(), N);
52 let mut inlined = Self {
53 size: N.try_into().vortex_expect("inlined size must fit in u32"),
54 data: [0u8; BinaryView::MAX_INLINED_SIZE],
55 };
56 inlined.data[..N].copy_from_slice(&value[..N]);
57 inlined
58 }
59
60 #[inline]
62 pub fn value(&self) -> &[u8] {
63 &self.data[0..(self.size as usize)]
64 }
65}
66
67#[derive(Clone, Copy, Debug)]
69#[repr(C, align(8))]
70pub struct Ref {
71 pub size: u32,
73 pub prefix: [u8; 4],
75 pub buffer_index: u32,
77 pub offset: u32,
79}
80
81impl Ref {
82 #[inline]
84 pub fn as_range(&self) -> Range<usize> {
85 self.offset as usize..(self.offset + self.size) as usize
86 }
87
88 #[inline]
90 pub fn with_buffer_and_offset(&self, buffer_index: u32, offset: u32) -> Ref {
91 Self {
92 size: self.size,
93 prefix: self.prefix,
94 buffer_index,
95 offset,
96 }
97 }
98}
99
100impl BinaryView {
101 pub const MAX_INLINED_SIZE: usize = 12;
103
104 #[inline(never)]
112 pub fn make_view(value: &[u8], block: u32, offset: u32) -> Self {
113 match value.len() {
114 0 => Self {
115 inlined: Inlined::new::<0>(value),
116 },
117 1 => Self {
118 inlined: Inlined::new::<1>(value),
119 },
120 2 => Self {
121 inlined: Inlined::new::<2>(value),
122 },
123 3 => Self {
124 inlined: Inlined::new::<3>(value),
125 },
126 4 => Self {
127 inlined: Inlined::new::<4>(value),
128 },
129 5 => Self {
130 inlined: Inlined::new::<5>(value),
131 },
132 6 => Self {
133 inlined: Inlined::new::<6>(value),
134 },
135 7 => Self {
136 inlined: Inlined::new::<7>(value),
137 },
138 8 => Self {
139 inlined: Inlined::new::<8>(value),
140 },
141 9 => Self {
142 inlined: Inlined::new::<9>(value),
143 },
144 10 => Self {
145 inlined: Inlined::new::<10>(value),
146 },
147 11 => Self {
148 inlined: Inlined::new::<11>(value),
149 },
150 12 => Self {
151 inlined: Inlined::new::<12>(value),
152 },
153 _ => Self {
154 _ref: Ref {
155 size: u32::try_from(value.len()).vortex_expect("value length must fit in u32"),
156 prefix: value[0..4]
157 .try_into()
158 .ok()
159 .vortex_expect("prefix must be exactly 4 bytes"),
160 buffer_index: block,
161 offset,
162 },
163 },
164 }
165 }
166
167 #[inline]
169 pub fn empty_view() -> Self {
170 Self { le_bytes: [0; 16] }
171 }
172
173 #[inline]
179 pub fn new_inlined(value: &[u8]) -> Self {
180 assert!(
181 value.len() <= Self::MAX_INLINED_SIZE,
182 "expected inlined value to be <= 12 bytes, was {}",
183 value.len()
184 );
185
186 Self::make_view(value, 0, 0)
187 }
188
189 #[inline]
191 pub fn len(&self) -> u32 {
192 unsafe { self.inlined.size }
193 }
194
195 #[inline]
197 pub fn is_empty(&self) -> bool {
198 self.len() == 0
199 }
200
201 #[inline]
203 #[expect(
204 clippy::cast_possible_truncation,
205 reason = "MAX_INLINED_SIZE is a small constant"
206 )]
207 pub fn is_inlined(&self) -> bool {
208 self.len() <= (Self::MAX_INLINED_SIZE as u32)
209 }
210
211 pub fn as_inlined(&self) -> &Inlined {
213 debug_assert!(self.is_inlined());
214 unsafe { &self.inlined }
215 }
216
217 pub fn as_view(&self) -> &Ref {
219 debug_assert!(!self.is_inlined());
220 unsafe { &self._ref }
221 }
222
223 pub fn as_view_mut(&mut self) -> &mut Ref {
225 unsafe { &mut self._ref }
226 }
227
228 pub fn as_u128(&self) -> u128 {
230 unsafe { u128::from_le_bytes(self.le_bytes) }
232 }
233}
234
235impl From<u128> for BinaryView {
236 fn from(value: u128) -> Self {
237 BinaryView {
238 le_bytes: value.to_le_bytes(),
239 }
240 }
241}
242
243impl From<Ref> for BinaryView {
244 fn from(value: Ref) -> Self {
245 BinaryView { _ref: value }
246 }
247}
248
249impl PartialEq for BinaryView {
250 fn eq(&self, other: &Self) -> bool {
251 let a = unsafe { std::mem::transmute::<&BinaryView, &u128>(self) };
252 let b = unsafe { std::mem::transmute::<&BinaryView, &u128>(other) };
253 a == b
254 }
255}
256impl Eq for BinaryView {}
257
258impl Hash for BinaryView {
259 fn hash<H: Hasher>(&self, state: &mut H) {
260 unsafe { std::mem::transmute::<&BinaryView, &u128>(self) }.hash(state);
261 }
262}
263
264impl Default for BinaryView {
265 fn default() -> Self {
266 Self::make_view(&[], 0, 0)
267 }
268}
269
270impl fmt::Debug for BinaryView {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 let mut s = f.debug_struct("BinaryView");
273 if self.is_inlined() {
274 s.field("inline", &self.as_inlined());
275 } else {
276 s.field("ref", &self.as_view());
277 }
278 s.finish()
279 }
280}