use core::{
convert::Infallible,
marker::PhantomData,
};
use qubit_codec::Codec;
use crate::{
Leb128Codec,
Leb128DecodeError,
Leb128DecodePolicy,
NonStrict,
};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ZigZagCodec<T, P = NonStrict> {
marker: PhantomData<fn() -> (T, P)>,
}
macro_rules! impl_zig_zag_codec {
($signed:ty, $unsigned:ty, $shift:expr) => {
impl<P> ZigZagCodec<$signed, P>
where
P: Leb128DecodePolicy,
{
pub const MIN_UNITS_PER_VALUE: usize = 1;
pub const MAX_UNITS_PER_VALUE: usize =
Leb128Codec::<$unsigned, NonStrict>::MAX_UNITS_PER_VALUE;
#[inline(always)]
pub unsafe fn decode_unchecked(
input: &[u8],
index: usize,
) -> Result<($signed, core::num::NonZeroUsize), Leb128DecodeError>
{
debug_assert!(
input.len().saturating_sub(index)
>= Self::MIN_UNITS_PER_VALUE
);
let (encoded, consumed) = unsafe {
Leb128Codec::<$unsigned, P>::decode_unchecked(input, index)?
};
let value =
((encoded >> 1) as $signed) ^ (-((encoded & 1) as $signed));
Ok((value, consumed))
}
#[inline(always)]
pub unsafe fn encode_unchecked(
value: $signed,
output: &mut [u8],
index: usize,
) -> usize {
let encoded = ((value as $unsigned) << 1)
^ ((value >> $shift) as $unsigned);
unsafe {
Leb128Codec::<$unsigned, NonStrict>::encode_unchecked(
encoded, output, index,
)
}
}
}
unsafe impl<P> Codec for ZigZagCodec<$signed, P>
where
P: Leb128DecodePolicy,
{
type Value = $signed;
type Unit = u8;
type DecodeError = Leb128DecodeError;
type EncodeError = Infallible;
#[inline(always)]
fn min_units_per_value(&self) -> core::num::NonZeroUsize {
core::num::NonZeroUsize::MIN
}
#[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<($signed, core::num::NonZeroUsize), Self::DecodeError>
{
debug_assert!(
input.len().saturating_sub(index)
>= Self::MIN_UNITS_PER_VALUE
);
unsafe { Self::decode_unchecked(input, index) }
}
#[inline(always)]
unsafe fn encode_unchecked(
&self,
value: &$signed,
output: &mut [u8],
index: usize,
) -> Result<usize, Self::EncodeError> {
debug_assert!(
output.len().saturating_sub(index)
>= Self::MAX_UNITS_PER_VALUE
);
Ok(unsafe { Self::encode_unchecked(*value, output, index) })
}
}
};
}
impl_zig_zag_codec!(i8, u8, 7);
impl_zig_zag_codec!(i16, u16, 15);
impl_zig_zag_codec!(i32, u32, 31);
impl_zig_zag_codec!(i64, u64, 63);
impl_zig_zag_codec!(i128, u128, 127);
impl_zig_zag_codec!(isize, usize, isize::BITS - 1);