From a31fc62ac421dd5f2cc05ad5ef410d4dd60e6ec3 Mon Sep 17 00:00:00 2001 From: Edsko de Vries Date: Wed, 22 Mar 2023 09:29:05 +0100 Subject: [PATCH] Introduce `HaskellMaxSize` --- haskell-ffi/src/haskell_max_size.rs | 158 ++++++++++++++++++++++++++++ haskell-ffi/src/lib.rs | 1 + haskell-ffi/src/macros.rs | 12 +++ haskell-ffi/src/to_haskell.rs | 34 +++++- 4 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 haskell-ffi/src/haskell_max_size.rs diff --git a/haskell-ffi/src/haskell_max_size.rs b/haskell-ffi/src/haskell_max_size.rs new file mode 100644 index 0000000..6e28fba --- /dev/null +++ b/haskell-ffi/src/haskell_max_size.rs @@ -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 { + /// Statically known size (in bytes) + fn haskell_max_size(tag: PhantomData) -> usize; +} + +/******************************************************************************* + Simple instances + + These all just piggy-back on the `HaskellSize` instances. +*******************************************************************************/ + +impl HaskellMaxSize for u8 { + fn haskell_max_size(tag: PhantomData) -> usize { + u8::haskell_size(tag) + } +} + +impl HaskellMaxSize for u16 { + fn haskell_max_size(tag: PhantomData) -> usize { + u16::haskell_size(tag) + } +} + +impl HaskellMaxSize for u32 { + fn haskell_max_size(tag: PhantomData) -> usize { + u32::haskell_size(tag) + } +} + +impl HaskellMaxSize for u64 { + fn haskell_max_size(tag: PhantomData) -> usize { + u64::haskell_size(tag) + } +} + +impl HaskellMaxSize for u128 { + fn haskell_max_size(tag: PhantomData) -> usize { + u128::haskell_size(tag) + } +} + +impl HaskellMaxSize for i8 { + fn haskell_max_size(tag: PhantomData) -> usize { + i8::haskell_size(tag) + } +} + +impl HaskellMaxSize for i16 { + fn haskell_max_size(tag: PhantomData) -> usize { + i16::haskell_size(tag) + } +} + +impl HaskellMaxSize for i32 { + fn haskell_max_size(tag: PhantomData) -> usize { + i32::haskell_size(tag) + } +} + +impl HaskellMaxSize for i64 { + fn haskell_max_size(tag: PhantomData) -> usize { + i64::haskell_size(tag) + } +} + +impl HaskellMaxSize for i128 { + fn haskell_max_size(tag: PhantomData) -> usize { + i128::haskell_size(tag) + } +} + +impl HaskellMaxSize for f32 { + fn haskell_max_size(tag: PhantomData) -> usize { + f32::haskell_size(tag) + } +} + +impl HaskellMaxSize for f64 { + fn haskell_max_size(tag: PhantomData) -> usize { + f64::haskell_size(tag) + } +} + +impl HaskellMaxSize for () { + fn haskell_max_size(tag: PhantomData) -> usize { + <()>::haskell_size(tag) + } +} + +/******************************************************************************* + Composite instances + + See comments in `instances.rs` regarding `Result` +*******************************************************************************/ + +impl, const N: usize> HaskellMaxSize for [T; N] { + fn haskell_max_size(tag: PhantomData) -> usize { + T::haskell_max_size(tag) * N + } +} + +impl> HaskellMaxSize for Option { + fn haskell_max_size(tag: PhantomData) -> usize { + 1 + T::haskell_max_size(tag) + } +} + +impl, E: HaskellMaxSize> HaskellMaxSize for Result { + fn haskell_max_size(tag: PhantomData) -> 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 +); diff --git a/haskell-ffi/src/lib.rs b/haskell-ffi/src/lib.rs index 231cff5..d90899c 100644 --- a/haskell-ffi/src/lib.rs +++ b/haskell-ffi/src/lib.rs @@ -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; diff --git a/haskell-ffi/src/macros.rs b/haskell-ffi/src/macros.rs index 429b736..749a2c6 100644 --- a/haskell-ffi/src/macros.rs +++ b/haskell-ffi/src/macros.rs @@ -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 ),* > HaskellMaxSize for ( $($ts),* ) { + fn haskell_max_size(tag: PhantomData) -> usize { + fold_types!( [ $($ts),* ], haskell_max_size, tag, +, 0) + } + } + }; +} diff --git a/haskell-ffi/src/to_haskell.rs b/haskell-ffi/src/to_haskell.rs index 4375ada..1f23a3d 100644 --- a/haskell-ffi/src/to_haskell.rs +++ b/haskell-ffi/src/to_haskell.rs @@ -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(t: &T, out: *mut u8, out_len: usize, tag: PhantomData) +where + T: HaskellMaxSize + ToHaskell, +{ + 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 + ); + } } }