Merge pull request #3 from BeFunctional/edsko/haskell-max-size

Introduce `HaskellMaxSize`
This commit is contained in:
Edsko de Vries 2023-03-22 11:07:49 +00:00 committed by GitHub
commit 04f00f547b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 204 additions and 1 deletions

View file

@ -0,0 +1,158 @@
use std::{cmp::max, marker::PhantomData};
use crate::{derive_max_size_tuple_instance, fold_types, haskell_size::HaskellSize};
/*******************************************************************************
Main class definition
TODO: We do not currently support deriving 'HaskellMaxSize'.
*******************************************************************************/
pub trait HaskellMaxSize<Tag> {
/// Statically known size (in bytes)
fn haskell_max_size(tag: PhantomData<Tag>) -> usize;
}
/*******************************************************************************
Simple instances
These all just piggy-back on the `HaskellSize` instances.
*******************************************************************************/
impl<Tag> HaskellMaxSize<Tag> for u8 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
u8::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for u16 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
u16::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for u32 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
u32::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for u64 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
u64::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for u128 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
u128::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for i8 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
i8::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for i16 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
i16::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for i32 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
i32::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for i64 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
i64::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for i128 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
i128::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for f32 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
f32::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for f64 {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
f64::haskell_size(tag)
}
}
impl<Tag> HaskellMaxSize<Tag> for () {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
<()>::haskell_size(tag)
}
}
/*******************************************************************************
Composite instances
See comments in `instances.rs` regarding `Result`
*******************************************************************************/
impl<Tag, T: HaskellMaxSize<Tag>, const N: usize> HaskellMaxSize<Tag> for [T; N] {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
T::haskell_max_size(tag) * N
}
}
impl<Tag, T: HaskellMaxSize<Tag>> HaskellMaxSize<Tag> for Option<T> {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
1 + T::haskell_max_size(tag)
}
}
impl<Tag, T: HaskellMaxSize<Tag>, E: HaskellMaxSize<Tag>> HaskellMaxSize<Tag> for Result<T, E> {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
1 + max(T::haskell_max_size(tag), E::haskell_max_size(tag))
}
}
/*******************************************************************************
Tuples
We support the same sizes of tuples as `borsh` does.
*******************************************************************************/
derive_max_size_tuple_instance!(T0, T1);
derive_max_size_tuple_instance!(T0, T1, T2);
derive_max_size_tuple_instance!(T0, T1, T2, T3);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
derive_max_size_tuple_instance!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
derive_max_size_tuple_instance!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15
);
derive_max_size_tuple_instance!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
);
derive_max_size_tuple_instance!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17
);
derive_max_size_tuple_instance!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18
);
derive_max_size_tuple_instance!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19
);

View file

@ -8,6 +8,7 @@ pub mod bincode;
pub mod deriving_via;
pub mod error;
pub mod from_haskell;
pub mod haskell_max_size;
pub mod haskell_size;
pub mod to_haskell;
pub mod use_borsh;

View file

@ -188,3 +188,15 @@ macro_rules! derive_size_tuple_instance {
}
};
}
/// Derive `HaskellMaxSize` instance for tuple with the specified type arguments.
#[macro_export]
macro_rules! derive_max_size_tuple_instance {
($($ts:ident),*) => {
impl<Tag, $($ts: HaskellMaxSize<Tag> ),* > HaskellMaxSize<Tag> for ( $($ts),* ) {
fn haskell_max_size(tag: PhantomData<Tag>) -> usize {
fold_types!( [ $($ts),* ], haskell_max_size, tag, +, 0)
}
}
};
}

View file

@ -1,6 +1,6 @@
use std::{fmt::Display, io::Write, marker::PhantomData};
use crate::{error::Result, HaskellSize};
use crate::{error::Result, haskell_max_size::HaskellMaxSize, HaskellSize};
/*******************************************************************************
Main class definition
@ -61,6 +61,38 @@ where
} else {
let mut out_len_copy = out_len;
marshall_to_haskell_var(t, out, &mut out_len_copy, tag);
if out_len_copy != expected_len {
panic!(
"marshall_to_haskell_fixed: got buffer of expected size {}, but needed {}; bug in HaskellSize instance?",
expected_len, out_len_copy
);
}
}
}
/// Marshall value with encoding of known maximum size
///
/// The `out_len` parameter is only used to verify that the Haskell-side and
/// the Rust side agree on the length of the encoding.
pub fn marshall_to_haskell_max<Tag, T>(t: &T, out: *mut u8, out_len: usize, tag: PhantomData<Tag>)
where
T: HaskellMaxSize<Tag> + ToHaskell<Tag>,
{
let max_len: usize = T::haskell_max_size(tag);
if out_len != max_len {
panic!(
"marshall_to_haskell_max: expected buffer of size {}, but got {}",
max_len, out_len
)
} else {
let mut out_len_copy = out_len;
marshall_to_haskell_var(t, out, &mut out_len_copy, tag);
if out_len_copy > max_len {
panic!(
"marshall_to_haskell_max: required size {} exceeds maximum {}; bug in HaskellMaxSize instance?",
out_len_copy, max_len
);
}
}
}