diff --git a/src/jet/type_name.rs b/src/jet/type_name.rs index 89b1448d..5ef9f32e 100644 --- a/src/jet/type_name.rs +++ b/src/jet/type_name.rs @@ -43,12 +43,12 @@ impl TypeName { for c in self.0.iter().rev() { match c { b'1' => stack.push(Final::unit()), - b'2' => stack.push(Final::two_two_n(0)), - b'c' => stack.push(Final::two_two_n(3)), - b's' => stack.push(Final::two_two_n(4)), - b'i' => stack.push(Final::two_two_n(5)), - b'l' => stack.push(Final::two_two_n(6)), - b'h' => stack.push(Final::two_two_n(8)), + b'2' => stack.push(Final::two_two_n_fixed::<0>()), + b'c' => stack.push(Final::two_two_n_fixed::<3>()), + b's' => stack.push(Final::two_two_n_fixed::<4>()), + b'i' => stack.push(Final::two_two_n_fixed::<5>()), + b'l' => stack.push(Final::two_two_n_fixed::<6>()), + b'h' => stack.push(Final::two_two_n_fixed::<8>()), b'+' | b'*' => { let left = stack.pop().expect("Illegal type name syntax!"); let right = stack.pop().expect("Illegal type name syntax!"); diff --git a/src/merkle/tmr.rs b/src/merkle/tmr.rs index 381d0e1b..0c292577 100644 --- a/src/merkle/tmr.rs +++ b/src/merkle/tmr.rs @@ -236,11 +236,129 @@ impl Tmr { ])), ]; + /// The TMRs of the types (TWO^8)^<(2^(N + 1)) for small values of n + #[rustfmt::skip] + pub const BUFFER8_TWO_N_PLUS_ONE: [Tmr; 16] = [ + Tmr(Midstate([ + 0x66, 0x8c, 0x79, 0xa6, 0xf0, 0xe0, 0x86, 0xa6, + 0x2d, 0x73, 0xf0, 0xc6, 0x52, 0xb1, 0x5c, 0x56, + 0xb1, 0xd3, 0x53, 0x68, 0xe1, 0xff, 0xad, 0x76, + 0xc9, 0xd4, 0xdb, 0xae, 0xb5, 0x2d, 0xe6, 0x2f, + ])), + Tmr(Midstate([ + 0x9f, 0x91, 0xf5, 0x19, 0x9b, 0xb8, 0x16, 0x7a, + 0x3b, 0x6c, 0xf8, 0xd0, 0x39, 0x75, 0x6e, 0x1b, + 0x33, 0x18, 0x42, 0x6e, 0xef, 0x3a, 0x9b, 0xa0, + 0x90, 0x2c, 0x91, 0xdc, 0x5f, 0x7e, 0x63, 0x39, + ])), + Tmr(Midstate([ + 0x16, 0x98, 0x45, 0xb0, 0xe2, 0xf0, 0x4a, 0x83, + 0x76, 0xfe, 0x91, 0x14, 0xbd, 0x35, 0x3a, 0x29, + 0xe9, 0xb7, 0x80, 0x42, 0xb7, 0x70, 0x1b, 0xa1, + 0x6d, 0x02, 0xc4, 0x62, 0x18, 0xef, 0x8b, 0xdf, + ])), + Tmr(Midstate([ + 0xbd, 0x59, 0x57, 0x8c, 0x45, 0x78, 0xfe, 0xc4, + 0xef, 0x2a, 0x29, 0xc1, 0xad, 0xea, 0x4c, 0xe1, + 0x30, 0x6c, 0x1a, 0x2b, 0x10, 0xd6, 0x18, 0xbc, + 0x58, 0x44, 0x67, 0x84, 0x17, 0x97, 0x3b, 0x8e, + ])), + Tmr(Midstate([ + 0x88, 0xd1, 0x8c, 0x11, 0x5f, 0x86, 0x54, 0x40, + 0x5f, 0xd7, 0x1d, 0xe8, 0x7e, 0x6f, 0xf0, 0x80, + 0x6c, 0x5d, 0xa7, 0x97, 0xca, 0xaf, 0x48, 0xe2, + 0x47, 0xea, 0xcc, 0x43, 0x99, 0x96, 0x89, 0x1a, + ])), + Tmr(Midstate([ + 0x90, 0x65, 0x22, 0xe1, 0x6d, 0xf3, 0x6f, 0x83, + 0xa4, 0x50, 0x32, 0x8a, 0xf8, 0x4e, 0x2f, 0x3e, + 0x52, 0xf6, 0xd6, 0x49, 0xa7, 0x88, 0xe3, 0xb2, + 0x1c, 0xca, 0xe3, 0xcd, 0x9a, 0x24, 0x66, 0x77, + ])), + Tmr(Midstate([ + 0x3a, 0xba, 0x4c, 0xce, 0x0e, 0x46, 0xc5, 0x54, + 0xb4, 0x4f, 0x70, 0x21, 0x3d, 0x4d, 0x8b, 0xd8, + 0xe8, 0xf1, 0x7e, 0x2b, 0xd1, 0x7b, 0xdf, 0xb7, + 0x15, 0x0a, 0xdf, 0xfd, 0x9a, 0xdf, 0x80, 0x9d, + ])), + Tmr(Midstate([ + 0x7d, 0x81, 0x2a, 0xf0, 0x44, 0xcb, 0x04, 0xd2, + 0xaf, 0x4f, 0xc8, 0xe9, 0xb5, 0x30, 0x66, 0x03, + 0x9b, 0xd5, 0x89, 0x2a, 0x56, 0xa5, 0xd5, 0x64, + 0x1c, 0x5c, 0x58, 0xc8, 0x6e, 0xed, 0x8a, 0x35, + ])), + Tmr(Midstate([ + 0x23, 0x0e, 0xa9, 0x70, 0x03, 0x3e, 0x47, 0xc5, + 0x61, 0x68, 0x1e, 0xe7, 0x20, 0x58, 0xb9, 0xc0, + 0x75, 0x5b, 0x8d, 0x87, 0x22, 0x80, 0x18, 0x4f, + 0x2e, 0x3f, 0xe3, 0x93, 0xc0, 0x47, 0x8f, 0x1a, + ])), + Tmr(Midstate([ + 0x6e, 0x90, 0x88, 0x04, 0x20, 0x46, 0x2b, 0x84, + 0xf2, 0x16, 0x30, 0x2a, 0x09, 0x56, 0x9c, 0xc8, + 0xd4, 0x47, 0x54, 0x4a, 0xcd, 0x8a, 0xbc, 0x53, + 0xd8, 0xa7, 0xcf, 0x21, 0x9b, 0x44, 0x9c, 0x95, + ])), + Tmr(Midstate([ + 0x7a, 0xd1, 0x5d, 0xed, 0x85, 0x02, 0xa6, 0xf1, + 0x1e, 0xb8, 0xa4, 0x9b, 0x25, 0xc8, 0xd7, 0x62, + 0xa0, 0xfe, 0x00, 0x4f, 0xb5, 0x7d, 0x73, 0x43, + 0xbd, 0xf4, 0x90, 0xed, 0x7e, 0xd2, 0xf4, 0x07, + ])), + Tmr(Midstate([ + 0xaa, 0x30, 0x30, 0x15, 0x71, 0xcf, 0x8d, 0x2d, + 0x64, 0xe9, 0x65, 0x4b, 0x0c, 0xbd, 0xe0, 0x54, + 0x01, 0x77, 0xab, 0xc2, 0xf2, 0xef, 0xfb, 0x7f, + 0x05, 0x2e, 0x27, 0xa8, 0xcc, 0xcc, 0xee, 0x62, + ])), + Tmr(Midstate([ + 0x28, 0x6d, 0x4d, 0x57, 0x6d, 0xb8, 0xe8, 0x45, + 0x00, 0x9c, 0x74, 0x84, 0xe0, 0x0a, 0x9f, 0xa3, + 0xf3, 0x18, 0xa8, 0x21, 0x9e, 0x38, 0x66, 0x2c, + 0xda, 0x11, 0x75, 0x78, 0x22, 0xe4, 0x53, 0xbb, + ])), + Tmr(Midstate([ + 0x10, 0x56, 0xa7, 0x17, 0xdc, 0x0c, 0x86, 0xb7, + 0x49, 0x97, 0x5e, 0x9c, 0x36, 0x4a, 0xb5, 0xa6, + 0x64, 0x77, 0xc4, 0x2d, 0xe1, 0x6e, 0x5b, 0x05, + 0x29, 0x81, 0x4f, 0x4c, 0xba, 0x95, 0x7c, 0xb3, + ])), + Tmr(Midstate([ + 0x94, 0x59, 0x0e, 0xf5, 0x66, 0x10, 0xaa, 0x7f, + 0xec, 0x46, 0x5b, 0x72, 0x32, 0xab, 0xa3, 0xf4, + 0x7c, 0x94, 0x2b, 0x8e, 0x8d, 0xc8, 0x76, 0x82, + 0x51, 0xd3, 0x70, 0x2c, 0x99, 0xb9, 0xef, 0xef, + ])), + Tmr(Midstate([ + 0xd0, 0x7f, 0x71, 0xb7, 0xcd, 0x48, 0xb8, 0x50, + 0xad, 0x09, 0x06, 0x8b, 0x9b, 0x48, 0xe0, 0xf8, + 0x5f, 0x84, 0xc0, 0x1b, 0x34, 0x58, 0x6a, 0x51, + 0x34, 0x61, 0xfa, 0xf7, 0x6d, 0x54, 0x34, 0x64, + ])), + ]; + + /// The TMR of the `Ctx8` type used by the SHA256 jets. + #[rustfmt::skip] + pub const CTX8: Tmr = Tmr(Midstate([ + 0x8d, 0x5c, 0xe5, 0xf6, 0xeb, 0xfc, 0x70, 0x61, + 0xe7, 0x91, 0x16, 0xbd, 0xf6, 0x6f, 0xb4, 0xe4, + 0xc6, 0xca, 0x32, 0xa0, 0x1c, 0xda, 0x48, 0xf6, + 0xed, 0xc1, 0x0a, 0xee, 0xfd, 0x31, 0x0e, 0x8c, + ])); + /// The TMR for the unit type pub const fn unit() -> Tmr { Self::UNIT_IV } + /// The TMR for the successor of a type. + /// + /// Given a type `X`, we define its successor `S X` as `1 + X`. + /// In Rust notation this would be `Option`. + pub fn successor(self) -> Tmr { + Self::SUM_IV.update(Tmr::unit(), self) + } + /// The TMR for the sum of two types, whose TMRs are given pub fn sum(tmr1: Tmr, tmr2: Tmr) -> Tmr { Self::SUM_IV.update(tmr1, tmr2) @@ -256,8 +374,9 @@ impl Tmr { mod tests { use super::*; + use crate::jet::Jet; use crate::merkle::bip340_iv; - use crate::types; + use crate::types::{self, Final}; #[test] fn ivs() { @@ -317,4 +436,47 @@ mod tests { } }); } + + #[test] + fn const_buffer8() { + use std::sync::Arc; + + let two_8_tmr = Tmr::TWO_TWO_N[3]; + let buf_0_tmr = two_8_tmr.successor(); + + let two_8 = Final::two_two_n_fixed::<3>(); + let buf_0 = Arc::clone(&two_8).successor(); + + assert_eq!(buf_0, Final::buffer8_two_n_plus_one(0).unwrap()); + assert_eq!(two_8.tmr(), two_8_tmr); + assert_eq!(buf_0.tmr(), buf_0_tmr); + assert_eq!(buf_0_tmr, Tmr::BUFFER8_TWO_N_PLUS_ONE[0]); + + let mut tmr = buf_0_tmr; + let mut fin = buf_0; + assert_eq!(Tmr::BUFFER8_TWO_N_PLUS_ONE.len(), 16); + for n in 1..Tmr::BUFFER8_TWO_N_PLUS_ONE.len() { + tmr = Tmr::product(Tmr::TWO_TWO_N[n + 3].successor(), tmr); + fin = Final::product(Final::two_two_n(n + 3).unwrap().successor(), fin); + + assert_eq!(fin.tmr(), tmr); + assert_eq!(fin, Final::buffer8_two_n_plus_one(n).unwrap()); + assert_eq!(tmr, Tmr::BUFFER8_TWO_N_PLUS_ONE[n]); + + // For the case of n == 5, i.e. a 63-byte buffer (63 = 2^(n + 1) - 1), + // we can check against the SHA256 Ctx8 type to make sure that our + // computations are actually correct. + if n == 5 { + let extra_ctx_stuff = Final::product( + Final::two_two_n_fixed::<6>(), // 2^64 + Final::two_two_n_fixed::<8>(), // 2^256 + ); + let synthetic_ctx_ty = Final::product(Arc::clone(&fin), extra_ctx_stuff); + let actual_ctx_ty = crate::jet::Core::Sha256Ctx8Init.target_ty().to_final(); + assert_eq!(synthetic_ctx_ty, actual_ctx_ty,); + assert_eq!(synthetic_ctx_ty, Final::ctx8()); + assert_eq!(synthetic_ctx_ty.tmr(), Tmr::CTX8); + } + } + } } diff --git a/src/types/final_data.rs b/src/types/final_data.rs index 24b82b9b..bec8240b 100644 --- a/src/types/final_data.rs +++ b/src/types/final_data.rs @@ -157,6 +157,7 @@ macro_rules! construct_final_two_two_n { #[doc = "Create the type of"] #[doc = $text] #[doc = "words.\n\nThe type is precomputed and fast to access."] + #[inline] pub fn $name() -> Arc { super::precomputed::nth_power_of_2($n) } @@ -174,11 +175,80 @@ impl Final { }) } + /// Computes the successor of the type. + /// + /// Given a type `X`, we define its successor `S X` as `1 + X`. + /// In Rust notation this would be `Option`. + pub fn successor(self: Arc) -> Arc { + Self::sum(Self::unit(), self) + } + /// Create the type `2^(2^n)` for the given `n`. /// /// The type is precomputed and fast to access. - pub fn two_two_n(n: usize) -> Arc { - super::precomputed::nth_power_of_2(n) + #[inline] + pub fn two_two_n(n: usize) -> Result, TypeTooLargeError> { + let maximum = Tmr::TWO_TWO_N.len(); + if n < maximum { + Ok(super::precomputed::nth_power_of_2(n)) + } else { + Err(TypeTooLargeError { + ty: "2^(2^n)", + n, + maximum, + }) + } + } + + /// Create the type `2^(2^N)` for the compile-time constant `N`. + /// + /// Will fail to compile if `N` exceeds 31. + #[inline] + pub fn two_two_n_fixed() -> Arc { + // This crazy construction amounts to a compile-time assertion that N is less than the max. + struct Hack; + impl Hack { + const IS_IN_RANGE: () = { + assert!(N < Tmr::TWO_TWO_N.len()); + }; + } + let () = Hack::::IS_IN_RANGE; + + super::precomputed::nth_power_of_2(N) + } + + /// Create the type `(TWO^8)^<2^(n+1)` for the given `n`. + /// + /// Here + /// * The notation X^<2 is notation for the type (S X) + /// * The notation X^<(2*n) is notation for the type S (X^n) * X^` + /// + /// The type is precomputed and fast to access. + #[inline] + pub fn buffer8_two_n_plus_one(n: usize) -> Result, TypeTooLargeError> { + let maximum = Tmr::BUFFER8_TWO_N_PLUS_ONE.len(); + if n < maximum { + Ok(super::precomputed::buffer8_two_n_plus_one(n)) + } else { + // This is arguably a programming error and a panic would be justified, but it's + // hard to say how SimplicityHL will use this. I also think the current maximum + // may be too small and we could bump into this with real code, so better to let + // the caller decide how to handle that. + Err(TypeTooLargeError { + ty: "(TWO^8)^<2^(n+1)", + n, + maximum, + }) + } + } + + /// Create the `Ctx8` type used by the SHA256 jets. + /// + /// The type is precomputed and fast to access. + pub fn ctx8() -> Arc { + super::precomputed::ctx8() } construct_final_two_two_n!(u1, 0, "1-bit"); @@ -289,25 +359,70 @@ impl Final { } } +/// Attempted to produce a `(TWO^8)^(2^(n+1))` type exceeding the maximum size. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TypeTooLargeError { + ty: &'static str, + n: usize, + maximum: usize, +} + +impl fmt::Display for TypeTooLargeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "maximum n for {} is {}; got {}", + self.ty, self.maximum, self.n + ) + } +} + +impl std::error::Error for TypeTooLargeError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + let Self { + ty: _, + n: _, + maximum: _, + } = self; + None + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn two_two_n_consistent() { + // This test probably does not need to be exhaustive. + assert_eq!(Final::two_two_n_fixed::<0>(), Final::two_two_n(0).unwrap()); + assert_eq!(Final::two_two_n_fixed::<1>(), Final::two_two_n(1).unwrap()); + assert_eq!(Final::two_two_n_fixed::<2>(), Final::two_two_n(2).unwrap()); + assert_eq!(Final::two_two_n_fixed::<3>(), Final::two_two_n(3).unwrap()); + assert_eq!(Final::two_two_n_fixed::<4>(), Final::two_two_n(4).unwrap()); + assert_eq!(Final::two_two_n_fixed::<5>(), Final::two_two_n(5).unwrap()); + assert_eq!(Final::two_two_n_fixed::<6>(), Final::two_two_n(6).unwrap()); + assert_eq!(Final::two_two_n_fixed::<7>(), Final::two_two_n(7).unwrap()); + } + #[test] fn final_stringify() { - let ty1 = Final::two_two_n(10); + let ty1 = Final::two_two_n_fixed::<10>(); assert_eq!(ty1.to_string(), "2^1024"); - let sum = Final::sum(Final::two_two_n(5), Final::two_two_n(10)); + let sum = Final::sum(Final::two_two_n_fixed::<5>(), Final::two_two_n(10).unwrap()); assert_eq!(sum.to_string(), "2^32 + 2^1024"); - let prod = Final::product(Final::two_two_n(5), Final::two_two_n(10)); + let prod = Final::product( + Final::two_two_n_fixed::<5>(), + Final::two_two_n_fixed::<10>(), + ); assert_eq!(prod.to_string(), "2^32 × 2^1024"); - let ty1 = Final::two_two_n(0); + let ty1 = Final::two_two_n_fixed::<0>(); assert_eq!(ty1.to_string(), "2"); - let ty1 = Final::sum(Final::unit(), Final::two_two_n(2)); + let ty1 = Final::sum(Final::unit(), Final::two_two_n_fixed::<2>()); assert_eq!(ty1.to_string(), "2^4?"); } } diff --git a/src/types/mod.rs b/src/types/mod.rs index 070faf77..5b1bd299 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -87,7 +87,7 @@ mod union_bound; mod variable; pub use context::{BoundRef, Context}; -pub use final_data::{CompleteBound, Final}; +pub use final_data::{CompleteBound, Final, TypeTooLargeError}; pub use incomplete::Incomplete; /// Error type for simplicity diff --git a/src/types/precomputed.rs b/src/types/precomputed.rs index 42540f6f..8b1b9549 100644 --- a/src/types/precomputed.rs +++ b/src/types/precomputed.rs @@ -17,39 +17,44 @@ use crate::Tmr; use super::Final; use std::cell::RefCell; -use std::convert::TryInto; use std::sync::Arc; // Directly use the size of the precomputed TMR table to make sure they're in sync. const N_POWERS: usize = Tmr::TWO_TWO_N.len(); +// Directly use the size of the precomputed TMR table to make sure they're in sync. +const N_BUFFERS: usize = Tmr::BUFFER8_TWO_N_PLUS_ONE.len(); + thread_local! { + /// Types of the form 2^(2^n) for several small `n`. static TWO_TWO_N: RefCell; N_POWERS]>> = const { RefCell::new(None) }; + + /// The "variable-length buffer" typed used by sha256 and other jets. + static BUFFER8_TWO_N_PLUS_ONE: RefCell; N_BUFFERS]>> = const { RefCell::new(None) }; + + /// The sha256 Ctx8 type. + static CTX8: RefCell>> = const { RefCell::new(None) }; } fn initialize(write: &mut Option<[Arc; N_POWERS]>) { let one = Final::unit(); - let mut powers = Vec::with_capacity(N_POWERS); // Two^(2^0) = Two = (One + One) let mut power = Final::sum(Arc::clone(&one), one); - powers.push(Arc::clone(&power)); - - // Two^(2^(i + 1)) = (Two^(2^i) * Two^(2^i)) - for _ in 1..N_POWERS { - power = Final::product(Arc::clone(&power), power); - powers.push(Arc::clone(&power)); - } - - let powers: [Arc; N_POWERS] = powers.try_into().unwrap(); - *write = Some(powers); + *write = Some(core::array::from_fn(|i| { + if i > 0 { + power = Final::product(Arc::clone(&power), Arc::clone(&power)); + } + Arc::clone(&power) + })); } /// Obtain a precomputed copy of the nth power of two /// /// # Panics /// -/// Panics if you request a number `n` greater than or equal to [`Tmr::TWO_TWO_N`]. +/// Panics if you request a number `n` greater than or equal to the length +/// of [`Tmr::TWO_TWO_N`]. pub fn nth_power_of_2(n: usize) -> Arc { TWO_TWO_N.with(|arr| { if arr.borrow().is_none() { @@ -59,3 +64,57 @@ pub fn nth_power_of_2(n: usize) -> Arc { Arc::clone(&arr.borrow().as_ref().unwrap()[n]) }) } + +fn initialize_buffers(write: &mut Option<[Arc; N_BUFFERS]>) { + // (TWO^8)^<1 = S(TWO^8) + let mut buf = nth_power_of_2(3).successor(); + *write = Some(core::array::from_fn(|i| { + if i > 0 { + buf = Final::product( + Final::two_two_n(i + 3).unwrap().successor(), + Arc::clone(&buf), + ); + } + Arc::clone(&buf) + })); +} + +/// Obtain a precomputed copy of a "variable-length buffer" type. +/// +/// Precisely, this type is `(TWO^8)^<2^(n+1)`, where +/// +/// * The notation X^<2 is notation for the type (S X) +/// * The notation X^<(2*n) is notation for the type S (X^n) * X^` +/// +/// # Panics +/// +/// Panics if you request a number `n` greater than or equal to the length +/// of [`Tmr::BUFFER8_TWO_N_PLUS_1`]. +pub fn buffer8_two_n_plus_one(n: usize) -> Arc { + BUFFER8_TWO_N_PLUS_ONE.with(|arr| { + if arr.borrow().is_none() { + initialize_buffers(&mut arr.borrow_mut()); + } + debug_assert!(arr.borrow().is_some()); + Arc::clone(&arr.borrow().as_ref().unwrap()[n]) + }) +} + +/// Obtain a precomputed copy of the `SHA256` `Ctx8` type. +pub fn ctx8() -> Arc { + CTX8.with(|opt| { + if opt.borrow().is_none() { + *opt.borrow_mut() = Some(Final::product( + buffer8_two_n_plus_one(5), + Final::product( + Final::two_two_n_fixed::<6>(), // 2^64 + Final::two_two_n_fixed::<8>(), // 2^256 + ), + )); + } + debug_assert!(opt.borrow().is_some()); + Arc::clone(opt.borrow().as_ref().unwrap()) + }) +} diff --git a/src/value.rs b/src/value.rs index d378f216..049e5b4d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -446,6 +446,57 @@ impl Value { Self::right(Final::unit(), inner) } + /// Create a value of the type `(TWO^8)^<2^(n+1)` for the given `n`, populated + /// with the given slice's data. + /// + /// This type represents "a variable-length buffer of maximum length `2^(n+1) - 1`". + /// So for example, setting `n = 5`, the buffer can hold up to 63 bytes. + pub fn buffer8_two_n_plus_one(mut n: usize, mut data: &[u8]) -> Result { + // This line serves as a sanity check on `n`. After this we can assume that + // `1 << n` will not overflow (otherwise would would ty.bit_width be set to?) + // Similarly we repeatedly add 1, multiply by 8, etc. But all the values we + // compute will be <= ty.bit_width() and therefore not overflow. + let ty = Final::buffer8_two_n_plus_one(n).map_err(Buffer8Error::NTooLarge)?; + + debug_assert!( + ty.bit_width() > (2 << n) - 1, + "sanity check to prove that the below line won't overflow", + ); + if data.len() > (2 << n) - 1 { + return Err(Buffer8Error::SliceTooLarge { + length: data.len(), + n, + }); + } + + let mut dest = vec![0; ty.bit_width().div_ceil(8)]; + let mut dest_offset = 0; + loop { + let n_bytes = 1 << n; + + if data.len() & n_bytes != 0 { + // split_at will not panic due to SliceTooLarge check above the loop. + let (first, rest) = data.split_at(n_bytes); + copy_bits(&[0x80], 0, &mut dest, dest_offset, 1); + copy_bits(first, 0, &mut dest, dest_offset + 1, 8 * n_bytes); + data = rest; + }; + dest_offset += 1 + 8 * n_bytes; + + n = match n.checked_sub(1) { + Some(n) => n, + None => break, + }; + } + debug_assert_eq!(dest_offset, ty.bit_width()); + + Ok(Self { + inner: Arc::from(dest), + bit_offset: 0, + ty, + }) + } + /// Return the bit length of the value in compact encoding. pub fn compact_len(&self) -> usize { self.iter_compact().count() @@ -501,7 +552,7 @@ impl Value { Self { inner: Arc::new([value]), bit_offset: 7, - ty: Final::two_two_n(0), + ty: Final::two_two_n_fixed::<0>(), } } @@ -515,7 +566,7 @@ impl Value { Self { inner: Arc::new([value]), bit_offset: 6, - ty: Final::two_two_n(1), + ty: Final::two_two_n_fixed::<1>(), } } @@ -529,7 +580,7 @@ impl Value { Self { inner: Arc::new([value]), bit_offset: 4, - ty: Final::two_two_n(2), + ty: Final::two_two_n_fixed::<2>(), } } @@ -538,7 +589,7 @@ impl Value { Self { inner: Arc::new([value]), bit_offset: 0, - ty: Final::two_two_n(3), + ty: Final::two_two_n_fixed::<3>(), } } @@ -547,7 +598,7 @@ impl Value { Self { inner: Arc::new(bytes.to_be_bytes()), bit_offset: 0, - ty: Final::two_two_n(4), + ty: Final::two_two_n_fixed::<4>(), } } @@ -556,7 +607,7 @@ impl Value { Self { inner: Arc::new(bytes.to_be_bytes()), bit_offset: 0, - ty: Final::two_two_n(5), + ty: Final::two_two_n_fixed::<5>(), } } @@ -565,7 +616,7 @@ impl Value { Self { inner: Arc::new(bytes.to_be_bytes()), bit_offset: 0, - ty: Final::two_two_n(6), + ty: Final::two_two_n_fixed::<6>(), } } @@ -574,7 +625,7 @@ impl Value { Self { inner: Arc::new(bytes.to_be_bytes()), bit_offset: 0, - ty: Final::two_two_n(7), + ty: Final::two_two_n_fixed::<7>(), } } @@ -583,7 +634,7 @@ impl Value { Self { inner: Arc::new(bytes), bit_offset: 0, - ty: Final::two_two_n(8), + ty: Final::two_two_n_fixed::<8>(), } } @@ -592,7 +643,7 @@ impl Value { Self { inner: Arc::new(bytes), bit_offset: 0, - ty: Final::two_two_n(9), + ty: Final::two_two_n_fixed::<9>(), } } @@ -1069,8 +1120,10 @@ impl Word { bits: &mut BitIter, n: u32, ) -> Result { - assert!(n < 32, "TWO^(2^{n}) is not supported as a word type"); - let ty = Final::two_two_n(n as usize); // cast safety: 32-bit machine or higher + let nsize = usize::try_from(n).unwrap_or(usize::MAX); // usize::MAX will error on next line + let Ok(ty) = Final::two_two_n(nsize) else { + panic!("TWO^(2^{n}) is not supported as a word type"); + }; let value = Value::from_compact_bits(bits, &ty)?; Ok(Self { value, n }) } @@ -1101,6 +1154,37 @@ impl fmt::Display for Word { } } +/// Slice exceeded the maximum capacity of the buffer type in [`Value::buffer8_two_n`]. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum Buffer8Error { + NTooLarge(crate::types::TypeTooLargeError), + SliceTooLarge { length: usize, n: usize }, +} + +impl fmt::Display for Buffer8Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match *self { + Self::NTooLarge(_) => f.write_str("failed to construct (sub)type of buffer"), + Self::SliceTooLarge { length, n } => write!( + f, + "slice of length {} too long for type (TWO^8)^<2^({}+1) (maximum {})", + length, + n, + (1 << (n + 1)) - 1, + ), + } + } +} + +impl std::error::Error for Buffer8Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match *self { + Self::NTooLarge(ref e) => Some(e), + Self::SliceTooLarge { .. } => None, + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -1111,7 +1195,7 @@ mod tests { fn value_len() { let v = Value::u4(6); let s_v = Value::some(v.shallow_clone()); - let n_v = Value::none(Final::two_two_n(2)); + let n_v = Value::none(Final::two_two_n_fixed::<2>()); assert_eq!(v.compact_len(), 4); assert_eq!(v.padded_len(), 4); @@ -1137,11 +1221,11 @@ mod tests { (Value::left(Value::unit(), Final::unit()), TypeName(b"+11")), (Value::right(Final::unit(), Value::unit()), TypeName(b"+11")), ( - Value::left(Value::unit(), Final::two_two_n(8)), + Value::left(Value::unit(), Final::two_two_n_fixed::<8>()), TypeName(b"+1h"), ), ( - Value::right(Final::two_two_n(8), Value::unit()), + Value::right(Final::two_two_n_fixed::<8>(), Value::unit()), TypeName(b"+h1"), ), ( @@ -1162,7 +1246,7 @@ mod tests { fn prune_regression_1() { // Found this when fuzzing Elements; unsure how to reduce it further. let nontrivial_sum = Value::product( - Value::right(Final::two_two_n(4), Value::u16(0)), + Value::right(Final::two_two_n_fixed::<4>(), Value::u16(0)), Value::u8(0), ); // Formatting should succeed and have no effect. @@ -1378,6 +1462,72 @@ mod tests { a dirty bit from the shared product buffer; value reads as Right" ); } + + #[test] + fn buffer8_two_n_plus_one() { + // n = 0 is just Option + assert_eq!( + Value::buffer8_two_n_plus_one(0, &[]).unwrap(), + Value::none(Final::two_two_n_fixed::<3>()), + ); + assert_eq!( + Value::buffer8_two_n_plus_one(0, &[3]).unwrap(), + Value::some(Value::u8(3)), + ); + // n = 1 is Option<[u8; 2]> x Option + assert_eq!( + Value::buffer8_two_n_plus_one(1, &[]).unwrap(), + Value::product( + Value::none(Final::two_two_n_fixed::<4>()), + Value::none(Final::two_two_n_fixed::<3>()), + ), + ); + assert_eq!( + Value::buffer8_two_n_plus_one(1, &[0x99]).unwrap(), + Value::product( + Value::none(Final::two_two_n_fixed::<4>()), + Value::some(Value::u8(0x99)), + ), + ); + assert_eq!( + Value::buffer8_two_n_plus_one(1, &[0x12, 0x34]).unwrap(), + Value::product( + Value::some(Value::u16(0x1234)), + Value::none(Final::two_two_n_fixed::<3>()), + ), + ); + assert_eq!( + Value::buffer8_two_n_plus_one(1, &[0x12, 0x34, 0xff]).unwrap(), + Value::product( + Value::some(Value::u16(0x1234)), + Value::some(Value::u8(0xff)), + ), + ); + + // Then let's do a "big" one. + let sample: [u8; 49] = *b"there are strange things done in the midnight sun"; + Value::buffer8_two_n_plus_one(0, &sample).unwrap_err(); + Value::buffer8_two_n_plus_one(4, &sample).unwrap_err(); + assert_eq!( + Value::buffer8_two_n_plus_one(5, &sample).unwrap(), + Value::product( + Value::some(Value::from_byte_array(*b"there are strange things done in")), + Value::product( + Value::some(Value::from_byte_array(*b" the midnight su")), + Value::product( + Value::none(Final::two_two_n_fixed::<6>()), + Value::product( + Value::none(Final::two_two_n_fixed::<5>()), + Value::product( + Value::none(Final::two_two_n_fixed::<4>()), + Value::some(Value::u8(b'n')), + ), + ), + ), + ), + ), + ); + } } #[cfg(bench)]