use super::const_writer::{
max_size_add, max_size_array, max_size_bool, max_size_f32, max_size_f64, max_size_i16,
max_size_i32, max_size_i64, max_size_max, max_size_opaque_fixed, max_size_opaque_variable,
max_size_u16, max_size_u32, max_size_u64, size_i32,
};
#[cfg(feature = "int128")]
use super::const_writer::{max_size_i128, max_size_u128};
use core::convert::Infallible;
use core::marker::PhantomData;
use xdr_common::{BytesM, StaticIfNoAlloc, StringM, VecM};
pub trait ConstMaxSize {
const MAX_SIZE: Option<usize>;
}
macro_rules! impl_primitive {
($($ty:ty => $max_fn:ident),* $(,)?) => {$(
impl ConstMaxSize for $ty {
const MAX_SIZE: Option<usize> = Some($max_fn());
}
)*};
}
impl_primitive! {
i16 => max_size_i16,
u16 => max_size_u16,
i32 => max_size_i32,
u32 => max_size_u32,
i64 => max_size_i64,
u64 => max_size_u64,
bool => max_size_bool,
f32 => max_size_f32,
f64 => max_size_f64,
}
#[cfg(feature = "int128")]
impl_primitive! {
i128 => max_size_i128,
u128 => max_size_u128,
}
impl<const N: usize> ConstMaxSize for [u8; N] {
const MAX_SIZE: Option<usize> = max_size_opaque_fixed(N);
}
impl<T: ConstMaxSize, const N: usize> ConstMaxSize for [T; N] {
const MAX_SIZE: Option<usize> = max_size_array(N, T::MAX_SIZE);
}
impl<T: ?Sized> ConstMaxSize for &T {
const MAX_SIZE: Option<usize> = None;
}
#[cfg(feature = "alloc")]
impl<T: ?Sized> ConstMaxSize for alloc::boxed::Box<T> {
const MAX_SIZE: Option<usize> = None;
}
#[cfg(not(feature = "alloc"))]
impl<T: ConstMaxSize> ConstMaxSize for xdr_common::Box<T> {
const MAX_SIZE: Option<usize> = T::MAX_SIZE;
}
impl<T: ConstMaxSize> ConstMaxSize for Option<T> {
const MAX_SIZE: Option<usize> = max_size_add(Some(size_i32(1)), T::MAX_SIZE);
}
impl<T: ConstMaxSize, E: ConstMaxSize> ConstMaxSize for Result<T, E> {
const MAX_SIZE: Option<usize> =
max_size_add(Some(size_i32(1)), max_size_max(T::MAX_SIZE, E::MAX_SIZE));
}
impl<T> ConstMaxSize for PhantomData<T> {
const MAX_SIZE: Option<usize> = Some(0);
}
impl ConstMaxSize for Infallible {
const MAX_SIZE: Option<usize> = Some(0);
}
impl ConstMaxSize for () {
const MAX_SIZE: Option<usize> = Some(0);
}
macro_rules! impl_tuple {
($($t:ident),+) => {
impl<$($t: ConstMaxSize),+> ConstMaxSize for ($($t,)+) {
const MAX_SIZE: Option<usize> = {
let mut total: Option<usize> = Some(0);
$( total = max_size_add(total, $t::MAX_SIZE); )+
total
};
}
};
}
impl_tuple!(A);
impl_tuple!(A, B);
impl_tuple!(A, B, C);
impl_tuple!(A, B, C, D);
impl_tuple!(A, B, C, D, E);
impl_tuple!(A, B, C, D, E, F);
impl_tuple!(A, B, C, D, E, F, G);
impl_tuple!(A, B, C, D, E, F, G, H);
impl_tuple!(A, B, C, D, E, F, G, H, I);
impl_tuple!(A, B, C, D, E, F, G, H, I, J);
impl_tuple!(A, B, C, D, E, F, G, H, I, J, K);
impl_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
impl<const MAX: u32> ConstMaxSize for BytesM<MAX> {
const MAX_SIZE: Option<usize> = max_size_opaque_variable(MAX);
}
impl<const MAX: u32> ConstMaxSize for StringM<MAX> {
const MAX_SIZE: Option<usize> = max_size_opaque_variable(MAX);
}
impl<const MAX: u32> ConstMaxSize for VecM<u8, MAX> {
const MAX_SIZE: Option<usize> = max_size_opaque_variable(MAX);
}
impl<T: ConstMaxSize + StaticIfNoAlloc, const MAX: u32> ConstMaxSize for VecM<T, MAX> {
const MAX_SIZE: Option<usize> = None;
}
#[cfg(test)]
mod tests {
use super::ConstMaxSize;
use xdr_common::{BytesM, StringM, VecM};
const fn bound<T: ConstMaxSize>() -> Option<usize> {
T::MAX_SIZE
}
const _: () = assert!(matches!(bound::<u32>(), Some(5)));
const _: () = assert!(matches!(bound::<i64>(), Some(9)));
const _: () = assert!(matches!(bound::<bool>(), Some(1)));
const _: () = assert!(matches!(bound::<f32>(), Some(4)));
const _: () = assert!(matches!(bound::<u16>(), Some(3)));
const _: () = assert!(matches!(bound::<[u8; 5]>(), Some(5)));
const _: () = assert!(matches!(bound::<[u32; 3]>(), Some(15)));
const _: () = assert!(bound::<&'static u32>().is_none());
const _: () = assert!(bound::<&'static [u8]>().is_none());
const _: () = assert!(bound::<&'static str>().is_none());
const _: () = assert!(bound::<&'static [u32]>().is_none());
#[cfg(feature = "alloc")]
const _: () = assert!(bound::<alloc::boxed::Box<u32>>().is_none());
#[cfg(feature = "alloc")]
const _: () = assert!(bound::<alloc::boxed::Box<[u8; 4]>>().is_none());
#[cfg(feature = "alloc")]
const _: () = assert!(bound::<Option<alloc::boxed::Box<u32>>>().is_none());
#[cfg(feature = "alloc")]
struct Rec;
#[cfg(feature = "alloc")]
impl ConstMaxSize for Rec {
const MAX_SIZE: Option<usize> = <Option<alloc::boxed::Box<Rec>> as ConstMaxSize>::MAX_SIZE;
}
#[cfg(feature = "alloc")]
const _: () = assert!(bound::<Rec>().is_none());
const _: () = assert!(matches!(bound::<Option<u32>>(), Some(6)));
const _: () = assert!(bound::<Option<&'static u32>>().is_none());
const _: () = assert!(matches!(bound::<Result<u32, i64>>(), Some(10)));
const _: () = assert!(matches!(bound::<(u16, i16)>(), Some(6)));
const _: () = assert!(matches!(bound::<()>(), Some(0)));
const _: () = assert!(matches!(bound::<core::marker::PhantomData<u64>>(), Some(0)));
const _: () = assert!(matches!(bound::<core::convert::Infallible>(), Some(0)));
const _: () = assert!(matches!(bound::<[&'static [u8]; 0]>(), Some(0)));
const _: () = assert!(matches!(bound::<BytesM<16>>(), Some(17)));
const _: () = assert!(matches!(bound::<StringM<8>>(), Some(9)));
const _: () = assert!(matches!(bound::<VecM<u8, 4>>(), Some(5)));
const _: () = assert!(bound::<VecM<u32, 5>>().is_none());
#[cfg(target_pointer_width = "64")]
const _: () = assert!(matches!(
bound::<BytesM>(),
Some(n) if n == 5 + u32::MAX as usize
));
#[cfg(feature = "int128")]
const _: () = assert!(matches!(bound::<i128>(), Some(18)));
#[cfg(feature = "int128")]
const _: () = assert!(matches!(bound::<u128>(), Some(18)));
#[test]
fn bounds_are_stable() {
assert_eq!(bound::<u32>(), Some(5));
assert_eq!(bound::<Option<u32>>(), Some(6));
assert_eq!(bound::<VecM<u32, 5>>(), None);
}
}