use core::{
convert::Infallible,
marker::PhantomData,
ptr,
};
use qubit_codec::Codec;
use crate::{
BigEndian,
LittleEndian,
};
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BinaryCodec<T, O> {
marker: PhantomData<fn() -> (T, O)>,
}
impl<O> BinaryCodec<u8, O> {
pub const MIN_UNITS_PER_VALUE: usize = 1;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> (u8, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
(
unsafe { *input.as_ptr().add(index) },
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: u8,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(index + Self::MAX_UNITS_PER_VALUE <= output.len());
unsafe {
*output.as_mut_ptr().add(index) = value;
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl<O> Codec for BinaryCodec<u8, O> {
type Value = u8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(Self::MIN_UNITS_PER_VALUE)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(Self::MAX_UNITS_PER_VALUE)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<(u8, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &u8,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
impl<O> BinaryCodec<i8, O> {
pub const MIN_UNITS_PER_VALUE: usize = 1;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> (i8, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
(
unsafe { *input.as_ptr().add(index) as i8 },
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: i8,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(index + Self::MAX_UNITS_PER_VALUE <= output.len());
unsafe {
*output.as_mut_ptr().add(index) = value as u8;
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl<O> Codec for BinaryCodec<i8, O> {
type Value = i8;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(Self::MIN_UNITS_PER_VALUE)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(Self::MAX_UNITS_PER_VALUE)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<(i8, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &i8,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
macro_rules! impl_integer_binary_codec {
($ty:ty, $len:expr) => {
impl BinaryCodec<$ty, BigEndian> {
pub const MIN_UNITS_PER_VALUE: usize = $len;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> ($ty, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
let pointer =
unsafe { input.as_ptr().add(index).cast::<$ty>() };
let raw = unsafe { ptr::read_unaligned(pointer) };
(
<$ty>::from_be(raw),
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: $ty,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(
index + Self::MAX_UNITS_PER_VALUE <= output.len()
);
let raw = value.to_be();
let pointer =
unsafe { output.as_mut_ptr().add(index).cast::<$ty>() };
unsafe {
ptr::write_unaligned(pointer, raw);
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl Codec for BinaryCodec<$ty, BigEndian> {
type Value = $ty;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MAX_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &$ty,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
impl BinaryCodec<$ty, LittleEndian> {
pub const MIN_UNITS_PER_VALUE: usize = $len;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> ($ty, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
let pointer =
unsafe { input.as_ptr().add(index).cast::<$ty>() };
let raw = unsafe { ptr::read_unaligned(pointer) };
(
<$ty>::from_le(raw),
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: $ty,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(
index + Self::MAX_UNITS_PER_VALUE <= output.len()
);
let raw = value.to_le();
let pointer =
unsafe { output.as_mut_ptr().add(index).cast::<$ty>() };
unsafe {
ptr::write_unaligned(pointer, raw);
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl Codec for BinaryCodec<$ty, LittleEndian> {
type Value = $ty;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MAX_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &$ty,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
};
}
macro_rules! impl_float_binary_codec {
($ty:ty, $bits:ty, $len:expr) => {
impl BinaryCodec<$ty, BigEndian> {
pub const MIN_UNITS_PER_VALUE: usize = $len;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> ($ty, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
let pointer =
unsafe { input.as_ptr().add(index).cast::<$bits>() };
let raw = unsafe { ptr::read_unaligned(pointer) };
(
<$ty>::from_bits(<$bits>::from_be(raw)),
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: $ty,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(
index + Self::MAX_UNITS_PER_VALUE <= output.len()
);
let raw = value.to_bits().to_be();
let pointer =
unsafe { output.as_mut_ptr().add(index).cast::<$bits>() };
unsafe {
ptr::write_unaligned(pointer, raw);
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl Codec for BinaryCodec<$ty, BigEndian> {
type Value = $ty;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MAX_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &$ty,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
impl BinaryCodec<$ty, LittleEndian> {
pub const MIN_UNITS_PER_VALUE: usize = $len;
pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
#[must_use]
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> ($ty, core::num::NonZeroUsize) {
debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
let pointer =
unsafe { input.as_ptr().add(index).cast::<$bits>() };
let raw = unsafe { ptr::read_unaligned(pointer) };
(
<$ty>::from_bits(<$bits>::from_le(raw)),
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
},
)
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: $ty,
output: &mut [u8],
index: usize,
) -> usize {
debug_assert!(
index + Self::MAX_UNITS_PER_VALUE <= output.len()
);
let raw = value.to_bits().to_le();
let pointer =
unsafe { output.as_mut_ptr().add(index).cast::<$bits>() };
unsafe {
ptr::write_unaligned(pointer, raw);
}
Self::MAX_UNITS_PER_VALUE
}
}
unsafe impl Codec for BinaryCodec<$ty, LittleEndian> {
type Value = $ty;
type Unit = u8;
type DecodeError = Infallible;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MIN_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
fn max_units_per_value(&self) -> core::num::NonZeroUsize {
unsafe {
core::num::NonZeroUsize::new_unchecked(
Self::MAX_UNITS_PER_VALUE,
)
}
}
#[inline(always)]
unsafe fn decode_unchecked(
&self,
input: &[u8],
index: usize,
) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
Ok(unsafe { Self::decode_unchecked(input, index) })
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &$ty,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
};
}
impl_integer_binary_codec!(u16, 2);
impl_integer_binary_codec!(u32, 4);
impl_integer_binary_codec!(u64, 8);
impl_integer_binary_codec!(u128, 16);
impl_integer_binary_codec!(i16, 2);
impl_integer_binary_codec!(i32, 4);
impl_integer_binary_codec!(i64, 8);
impl_integer_binary_codec!(i128, 16);
impl_float_binary_codec!(f32, u32, 4);
impl_float_binary_codec!(f64, u64, 8);