use {
crate::{
ReadResult, WriteResult,
config::{ConfigCore, ZeroCopy},
error::invalid_tag_encoding,
io::{Reader, Writer},
},
pastey::paste,
};
pub trait ByteOrder: 'static {
const ENDIAN: Endian;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Endian {
Big,
Little,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BigEndian;
pub unsafe trait PlatformEndian {}
#[cfg(target_endian = "big")]
unsafe impl PlatformEndian for BigEndian {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LittleEndian;
#[cfg(target_endian = "little")]
unsafe impl PlatformEndian for LittleEndian {}
impl ByteOrder for BigEndian {
const ENDIAN: Endian = Endian::Big;
}
impl ByteOrder for LittleEndian {
const ENDIAN: Endian = Endian::Little;
}
macro_rules! impl_fix_int {
($byte_order:ty => $($ty:ty),*) => {
paste! {
$(
#[inline(always)]
fn [<encode_ $ty>] (val: $ty, mut writer: impl Writer) -> WriteResult<()> {
let bytes = match <$byte_order>::ENDIAN {
Endian::Big => val.to_be_bytes(),
Endian::Little => val.to_le_bytes(),
};
Ok(writer.write(&bytes)?)
}
#[inline(always)]
fn [<decode_ $ty>] <'de>(mut reader: impl Reader<'de>) -> ReadResult<$ty> {
let bytes = reader.take_array::<{ size_of::<$ty>() }>()?;
let val = match <$byte_order>::ENDIAN {
Endian::Big => <$ty>::from_be_bytes(bytes),
Endian::Little => <$ty>::from_le_bytes(bytes),
};
Ok(val)
}
#[inline(always)]
fn [<size_of_ $ty>](_val: $ty) -> usize {
size_of::<$ty>()
}
)*
}
};
}
pub unsafe trait IntEncoding<B: ByteOrder>: 'static {
const STATIC: bool;
const ZERO_COPY: bool;
fn encode_u16(val: u16, writer: impl Writer) -> WriteResult<()>;
fn size_of_u16(val: u16) -> usize;
fn decode_u16<'de>(reader: impl Reader<'de>) -> ReadResult<u16>;
fn encode_u32(val: u32, writer: impl Writer) -> WriteResult<()>;
fn size_of_u32(val: u32) -> usize;
fn decode_u32<'de>(reader: impl Reader<'de>) -> ReadResult<u32>;
fn encode_u64(val: u64, writer: impl Writer) -> WriteResult<()>;
fn size_of_u64(val: u64) -> usize;
fn decode_u64<'de>(reader: impl Reader<'de>) -> ReadResult<u64>;
fn encode_u128(val: u128, writer: impl Writer) -> WriteResult<()>;
fn size_of_u128(val: u128) -> usize;
fn decode_u128<'de>(reader: impl Reader<'de>) -> ReadResult<u128>;
fn encode_i16(val: i16, writer: impl Writer) -> WriteResult<()>;
fn size_of_i16(val: i16) -> usize;
fn decode_i16<'de>(reader: impl Reader<'de>) -> ReadResult<i16>;
fn encode_i32(val: i32, writer: impl Writer) -> WriteResult<()>;
fn size_of_i32(val: i32) -> usize;
fn decode_i32<'de>(reader: impl Reader<'de>) -> ReadResult<i32>;
fn encode_i64(val: i64, writer: impl Writer) -> WriteResult<()>;
fn size_of_i64(val: i64) -> usize;
fn decode_i64<'de>(reader: impl Reader<'de>) -> ReadResult<i64>;
fn encode_i128(val: i128, writer: impl Writer) -> WriteResult<()>;
fn size_of_i128(val: i128) -> usize;
fn decode_i128<'de>(reader: impl Reader<'de>) -> ReadResult<i128>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FixInt;
unsafe impl IntEncoding<BigEndian> for FixInt {
const STATIC: bool = true;
#[cfg(target_endian = "big")]
const ZERO_COPY: bool = true;
#[cfg(target_endian = "little")]
const ZERO_COPY: bool = false;
impl_fix_int!(BigEndian => u16, u32, u64, u128, i16, i32, i64, i128);
}
unsafe impl IntEncoding<LittleEndian> for FixInt {
const STATIC: bool = true;
#[cfg(target_endian = "big")]
const ZERO_COPY: bool = false;
#[cfg(target_endian = "little")]
const ZERO_COPY: bool = true;
impl_fix_int!(LittleEndian => u16, u32, u64, u128, i16, i32, i64, i128);
}
unsafe impl<C: ConfigCore> ZeroCopy<C> for FixInt where C::ByteOrder: PlatformEndian {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VarInt;
macro_rules! try_from_endian_bytes {
($reader:ident => $ty:ty as $target:ty) => {{
let ar = $reader.take_array::<{ size_of::<$ty>() }>()?;
let val = match B::ENDIAN {
Endian::Big => <$ty>::from_be_bytes(ar),
Endian::Little => <$ty>::from_le_bytes(ar),
};
val as $target
}};
($reader:ident => $ty:ty) => {{
try_from_endian_bytes!($reader => $ty as $ty)
}};
}
macro_rules! varint_decode_signed {
($ty:ty => $target:ty) => {
paste! {
#[inline]
fn [<decode_ $ty>]<'de>(reader: impl Reader<'de>) -> ReadResult<$ty> {
let n = <VarInt as IntEncoding<B>>::[<decode_ $target>](reader)?;
Ok(if n % 2 == 0 {
(n / 2) as _
} else {
!(n / 2) as _
})
}
}
};
}
macro_rules! varint_size_of_signed {
($ty:ty => $target:ty) => {
paste! {
#[inline]
#[expect(clippy::arithmetic_side_effects)]
fn [<size_of_ $ty>](val: $ty) -> usize {
let n: $target = if val < 0 {
(!(val as $target)) * 2 + 1
} else {
(val as $target) * 2
};
<VarInt as IntEncoding<B>>::[<size_of_ $target>](n)
}
}
};
}
macro_rules! varint_encode_signed {
($ty:ty => $target:ty) => {
paste! {
#[inline]
#[expect(clippy::arithmetic_side_effects)]
fn [<encode_ $ty>](val: $ty, writer: impl Writer) -> WriteResult<()> {
let n: $target = if val < 0 {
(!(val as $target)) * 2 + 1
} else {
(val as $target) * 2
};
<VarInt as IntEncoding<B>>::[<encode_ $target>](n, writer)
}
}
};
}
macro_rules! varint_encode_unsigned_impl {
($val:ident, $writer:ident, $ty:ty, $tag:ident => $cast:ty) => {{
let needed = size_of::<$cast>() + 1;
let mut writer = unsafe { $writer.as_trusted_for(needed) }?;
writer.write(&[$tag])?;
let encoded = $val as $cast;
let bytes = match B::ENDIAN {
Endian::Big => encoded.to_be_bytes(),
Endian::Little => encoded.to_le_bytes(),
};
writer.write(&bytes)?;
writer.finish()?;
Ok(())
}};
($val:ident, $writer:ident, $ty:ty, $tag:ident => $cast:ty, $($rest_tag:ident => $rest_cast:ty),+ $(,)?) => {{
if $val <= <$cast>::MAX as $ty {
let needed = size_of::<$cast>() + 1;
let mut writer = unsafe { $writer.as_trusted_for(needed) }?;
writer.write(&[$tag])?;
let encoded = $val as $cast;
let bytes = match B::ENDIAN {
Endian::Big => encoded.to_be_bytes(),
Endian::Little => encoded.to_le_bytes(),
};
writer.write(&bytes)?;
writer.finish()?;
Ok(())
} else {
varint_encode_unsigned_impl!($val, $writer, $ty, $($rest_tag => $rest_cast),+)
}
}};
}
macro_rules! varint_encode_unsigned {
($ty:ty, $($tag:ident => $cast:ty),+ $(,)?) => {
paste! {
#[inline]
#[expect(clippy::arithmetic_side_effects)]
fn [<encode_ $ty>](val: $ty, mut writer: impl Writer) -> WriteResult<()> {
if val <= SINGLE_BYTE_MAX as $ty {
writer.write(&[val as u8])?;
return Ok(());
}
varint_encode_unsigned_impl!(val, writer, $ty, $($tag => $cast),+)
}
}
};
}
const SINGLE_BYTE_MAX: u8 = 250;
const U16_BYTE: u8 = 251;
const U32_BYTE: u8 = 252;
const U64_BYTE: u8 = 253;
const U128_BYTE: u8 = 254;
unsafe impl<B: ByteOrder> IntEncoding<B> for VarInt {
const STATIC: bool = false;
const ZERO_COPY: bool = false;
#[inline]
fn size_of_u16(val: u16) -> usize {
if val <= SINGLE_BYTE_MAX as u16 { 1 } else { 3 }
}
fn decode_u16<'de>(mut reader: impl Reader<'de>) -> ReadResult<u16> {
let byte = reader.take_byte()?;
let out = match byte {
byte @ 0..=SINGLE_BYTE_MAX => byte as u16,
U16_BYTE => try_from_endian_bytes!(reader => u16),
byte => return Err(invalid_tag_encoding(byte as usize)),
};
Ok(out)
}
varint_encode_unsigned!(u16, U16_BYTE => u16);
#[inline]
fn size_of_u32(val: u32) -> usize {
if val <= SINGLE_BYTE_MAX as u32 {
1
} else if val <= u16::MAX as u32 {
3
} else {
5
}
}
fn decode_u32<'de>(mut reader: impl Reader<'de>) -> ReadResult<u32> {
let byte = reader.take_byte()?;
let out = match byte {
byte @ 0..=SINGLE_BYTE_MAX => byte as u32,
U16_BYTE => try_from_endian_bytes!(reader => u16 as u32),
U32_BYTE => try_from_endian_bytes!(reader => u32),
byte => return Err(invalid_tag_encoding(byte as usize)),
};
Ok(out)
}
varint_encode_unsigned!(u32, U16_BYTE => u16, U32_BYTE => u32);
#[inline]
fn size_of_u64(val: u64) -> usize {
if val <= SINGLE_BYTE_MAX as u64 {
1
} else if val <= u16::MAX as u64 {
3
} else if val <= u32::MAX as u64 {
5
} else {
9
}
}
fn decode_u64<'de>(mut reader: impl Reader<'de>) -> ReadResult<u64> {
let byte = reader.take_byte()?;
let out = match byte {
byte @ 0..=SINGLE_BYTE_MAX => byte as u64,
U16_BYTE => try_from_endian_bytes!(reader => u16 as u64),
U32_BYTE => try_from_endian_bytes!(reader => u32 as u64),
U64_BYTE => try_from_endian_bytes!(reader => u64),
byte => return Err(invalid_tag_encoding(byte as usize)),
};
Ok(out)
}
varint_encode_unsigned!(u64, U16_BYTE => u16, U32_BYTE => u32, U64_BYTE => u64);
#[inline]
fn size_of_u128(val: u128) -> usize {
if val <= SINGLE_BYTE_MAX as u128 {
1
} else if val <= u16::MAX as u128 {
3
} else if val <= u32::MAX as u128 {
5
} else if val <= u64::MAX as u128 {
9
} else {
17
}
}
fn decode_u128<'de>(mut reader: impl Reader<'de>) -> ReadResult<u128> {
let byte = reader.take_byte()?;
let out = match byte {
byte @ 0..=SINGLE_BYTE_MAX => byte as u128,
U16_BYTE => try_from_endian_bytes!(reader => u16 as u128),
U32_BYTE => try_from_endian_bytes!(reader => u32 as u128),
U64_BYTE => try_from_endian_bytes!(reader => u64 as u128),
U128_BYTE => try_from_endian_bytes!(reader => u128),
byte => return Err(invalid_tag_encoding(byte as usize)),
};
Ok(out)
}
varint_encode_unsigned!(
u128,
U16_BYTE => u16,
U32_BYTE => u32,
U64_BYTE => u64,
U128_BYTE => u128,
);
varint_size_of_signed!(i16 => u16);
varint_size_of_signed!(i32 => u32);
varint_size_of_signed!(i64 => u64);
varint_size_of_signed!(i128 => u128);
varint_encode_signed!(i16 => u16);
varint_encode_signed!(i32 => u32);
varint_encode_signed!(i64 => u64);
varint_encode_signed!(i128 => u128);
varint_decode_signed!(i16 => u16);
varint_decode_signed!(i32 => u32);
varint_decode_signed!(i64 => u64);
varint_decode_signed!(i128 => u128);
}