use std::fmt;
use std::hash::Hash;
use std::hash::Hasher;
use std::ops::Range;
use static_assertions::assert_eq_align;
use static_assertions::assert_eq_size;
use vortex_error::VortexExpect;
#[derive(Clone, Copy)]
#[repr(C, align(16))]
pub union BinaryView {
pub(crate) le_bytes: [u8; 16],
pub(crate) inlined: Inlined,
pub(crate) _ref: Ref,
}
assert_eq_align!(BinaryView, u128);
assert_eq_size!(BinaryView, [u8; 16]);
assert_eq_size!(Inlined, [u8; 16]);
assert_eq_size!(Ref, [u8; 16]);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(C, align(8))]
pub struct Inlined {
pub size: u32,
pub data: [u8; BinaryView::MAX_INLINED_SIZE],
}
impl Inlined {
fn new<const N: usize>(value: &[u8]) -> Self {
debug_assert_eq!(value.len(), N);
let mut inlined = Self {
size: N.try_into().vortex_expect("inlined size must fit in u32"),
data: [0u8; BinaryView::MAX_INLINED_SIZE],
};
inlined.data[..N].copy_from_slice(&value[..N]);
inlined
}
#[inline]
pub fn value(&self) -> &[u8] {
&self.data[0..(self.size as usize)]
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C, align(8))]
pub struct Ref {
pub size: u32,
pub prefix: [u8; 4],
pub buffer_index: u32,
pub offset: u32,
}
impl Ref {
#[inline]
pub fn as_range(&self) -> Range<usize> {
self.offset as usize..(self.offset + self.size) as usize
}
#[inline]
pub fn with_buffer_and_offset(&self, buffer_index: u32, offset: u32) -> Ref {
Self {
size: self.size,
prefix: self.prefix,
buffer_index,
offset,
}
}
}
impl BinaryView {
pub const MAX_INLINED_SIZE: usize = 12;
#[inline(never)]
pub fn make_view(value: &[u8], block: u32, offset: u32) -> Self {
match value.len() {
0 => Self {
inlined: Inlined::new::<0>(value),
},
1 => Self {
inlined: Inlined::new::<1>(value),
},
2 => Self {
inlined: Inlined::new::<2>(value),
},
3 => Self {
inlined: Inlined::new::<3>(value),
},
4 => Self {
inlined: Inlined::new::<4>(value),
},
5 => Self {
inlined: Inlined::new::<5>(value),
},
6 => Self {
inlined: Inlined::new::<6>(value),
},
7 => Self {
inlined: Inlined::new::<7>(value),
},
8 => Self {
inlined: Inlined::new::<8>(value),
},
9 => Self {
inlined: Inlined::new::<9>(value),
},
10 => Self {
inlined: Inlined::new::<10>(value),
},
11 => Self {
inlined: Inlined::new::<11>(value),
},
12 => Self {
inlined: Inlined::new::<12>(value),
},
_ => Self {
_ref: Ref {
size: u32::try_from(value.len()).vortex_expect("value length must fit in u32"),
prefix: value[0..4]
.try_into()
.ok()
.vortex_expect("prefix must be exactly 4 bytes"),
buffer_index: block,
offset,
},
},
}
}
#[inline]
pub fn empty_view() -> Self {
Self { le_bytes: [0; 16] }
}
#[inline]
pub fn new_inlined(value: &[u8]) -> Self {
assert!(
value.len() <= Self::MAX_INLINED_SIZE,
"expected inlined value to be <= 12 bytes, was {}",
value.len()
);
Self::make_view(value, 0, 0)
}
#[inline]
pub fn len(&self) -> u32 {
unsafe { self.inlined.size }
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
#[expect(
clippy::cast_possible_truncation,
reason = "MAX_INLINED_SIZE is a small constant"
)]
pub fn is_inlined(&self) -> bool {
self.len() <= (Self::MAX_INLINED_SIZE as u32)
}
pub fn as_inlined(&self) -> &Inlined {
debug_assert!(self.is_inlined());
unsafe { &self.inlined }
}
pub fn as_view(&self) -> &Ref {
debug_assert!(!self.is_inlined());
unsafe { &self._ref }
}
pub fn as_view_mut(&mut self) -> &mut Ref {
unsafe { &mut self._ref }
}
pub fn as_u128(&self) -> u128 {
unsafe { u128::from_le_bytes(self.le_bytes) }
}
}
impl From<u128> for BinaryView {
fn from(value: u128) -> Self {
BinaryView {
le_bytes: value.to_le_bytes(),
}
}
}
impl From<Ref> for BinaryView {
fn from(value: Ref) -> Self {
BinaryView { _ref: value }
}
}
impl PartialEq for BinaryView {
fn eq(&self, other: &Self) -> bool {
let a = unsafe { std::mem::transmute::<&BinaryView, &u128>(self) };
let b = unsafe { std::mem::transmute::<&BinaryView, &u128>(other) };
a == b
}
}
impl Eq for BinaryView {}
impl Hash for BinaryView {
fn hash<H: Hasher>(&self, state: &mut H) {
unsafe { std::mem::transmute::<&BinaryView, &u128>(self) }.hash(state);
}
}
impl Default for BinaryView {
fn default() -> Self {
Self::make_view(&[], 0, 0)
}
}
impl fmt::Debug for BinaryView {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("BinaryView");
if self.is_inlined() {
s.field("inline", &self.as_inlined());
} else {
s.field("ref", &self.as_view());
}
s.finish()
}
}