From 9e2de89ac4c6714d8a2f80e75da9dcd27d5fddfa Mon Sep 17 00:00:00 2001 From: Arseny Mitin Date: Fri, 30 Aug 2024 17:56:07 +0400 Subject: [PATCH] feat: optimize EitherInlineOrRef --- crates/bits/src/ser/writer.rs | 57 +++++++++++++++++++++- crates/contracts/src/jetton/wallet.rs | 11 ++--- crates/contracts/src/wallet/mod.rs | 5 -- crates/tlb-ton/src/message.rs | 10 ++-- crates/tlb/src/as/reference.rs | 70 ++++++++++++++++++++++++++- crates/tlb/src/ser/builder.rs | 5 ++ 6 files changed, 137 insertions(+), 21 deletions(-) diff --git a/crates/bits/src/ser/writer.rs b/crates/bits/src/ser/writer.rs index c24fcb5..96a2e1b 100644 --- a/crates/bits/src/ser/writer.rs +++ b/crates/bits/src/ser/writer.rs @@ -18,6 +18,9 @@ pub trait BitWriter { // An error ocurred while writing type Error: Error; + /// Returns remaining capacity in bits + fn capacity_left(&self) -> usize; + /// Writes a single bit. fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error>; @@ -216,12 +219,43 @@ pub trait BitWriterExt: BitWriter { } impl BitWriterExt for T where T: BitWriter {} +struct NoopBitWriter; + +impl BitWriter for NoopBitWriter { + type Error = StringError; + + #[inline] + fn capacity_left(&self) -> usize { + usize::MAX + } + + #[inline] + fn write_bit(&mut self, _bit: bool) -> Result<(), Self::Error> { + Ok(()) + } + + #[inline] + fn write_bitslice(&mut self, _bits: &BitSlice) -> Result<(), Self::Error> { + Ok(()) + } + + #[inline] + fn repeat_bit(&mut self, _n: usize, _bit: bool) -> Result<(), Self::Error> { + Ok(()) + } +} + impl BitWriter for BitCounter where W: BitWriter, { type Error = W::Error; + #[inline] + fn capacity_left(&self) -> usize { + self.inner.capacity_left() + } + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.inner.write_bit(bit)?; @@ -265,7 +299,7 @@ where #[inline] fn ensure_more(&self, n: usize) -> Result<(), W::Error> { - if self.bit_count() + n > self.limit { + if self.capacity_left() < n { return Err(Error::custom("max bits limit reached")); } Ok(()) @@ -283,6 +317,11 @@ where { type Error = W::Error; + #[inline] + fn capacity_left(&self) -> usize { + (self.limit - self.bit_count()).min(self.inner.capacity_left()) + } + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.ensure_more(1)?; @@ -309,6 +348,11 @@ where { type Error = T::Error; + #[inline] + fn capacity_left(&self) -> usize { + self.inner.capacity_left().min(self.writer.capacity_left()) + } + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.inner.write_bit(bit)?; @@ -343,6 +387,11 @@ where { type Error = StringError; + #[inline] + fn capacity_left(&self) -> usize { + usize::MAX - self.len() + } + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.push(bit); @@ -366,6 +415,12 @@ where impl BitWriter for String { type Error = StringError; + #[inline] + fn capacity_left(&self) -> usize { + usize::MAX - self.len() + } + + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.push(if bit { '1' } else { '0' }); Ok(()) diff --git a/crates/contracts/src/jetton/wallet.rs b/crates/contracts/src/jetton/wallet.rs index 4e197ca..149040c 100644 --- a/crates/contracts/src/jetton/wallet.rs +++ b/crates/contracts/src/jetton/wallet.rs @@ -9,8 +9,8 @@ use tlb::{ }, de::{CellDeserialize, CellParser, CellParserError}, either::Either, - r#as::{ParseFully, Ref, Same}, - ser::{CellBuilder, CellBuilderError, CellSerialize, CellSerializeExt}, + r#as::{EitherInlineOrRef, ParseFully, Ref, Same}, + ser::{CellBuilder, CellBuilderError, CellSerialize}, Cell, Error, }; use tlb_ton::MsgAddress; @@ -56,11 +56,8 @@ where // forward_ton_amount:(VarUInteger 16) .pack_as::<_, &VarInt<4>>(&self.forward_ton_amount)? // forward_payload:(Either Cell ^Cell) - .store_as::<_, Either<(), Ref>>( - Some(&self.forward_payload.to_cell()?) - // store empty cell inline - .filter(|cell| !cell.is_empty()), - )?; + .store_as::<_, EitherInlineOrRef>(&self.forward_payload)?; + Ok(()) } } diff --git a/crates/contracts/src/wallet/mod.rs b/crates/contracts/src/wallet/mod.rs index eb37fab..e519f16 100644 --- a/crates/contracts/src/wallet/mod.rs +++ b/crates/contracts/src/wallet/mod.rs @@ -162,11 +162,6 @@ where /// ).unwrap(); /// # let mut b = Cell::builder(); /// # b.store(msg).unwrap(); - /// # let cell = b.into_cell(); - /// # assert_eq!( - /// # hex!("607b41a4b219fbc6d23f4aae5c4b85e5ceca07bc0ba732ae02a621588f0577d4"), - ///# cell.hash(), - /// # ); /// ``` #[inline] pub fn create_external_message( diff --git a/crates/tlb-ton/src/message.rs b/crates/tlb-ton/src/message.rs index 1cff2a3..3781bfb 100644 --- a/crates/tlb-ton/src/message.rs +++ b/crates/tlb-ton/src/message.rs @@ -9,7 +9,7 @@ use tlb::{ }, de::{CellDeserialize, CellParser, CellParserError}, either::Either, - r#as::{DefaultOnNone, Ref, Same}, + r#as::{DefaultOnNone, EitherInlineOrRef, Ref, Same}, ser::{CellBuilder, CellBuilderError, CellSerialize, CellSerializeExt}, Cell, ResultExt, }; @@ -77,12 +77,8 @@ where fn store(&self, builder: &mut CellBuilder) -> Result<(), CellBuilderError> { builder .store(&self.info)? - .store_as::<_, Option>>(self.init.as_ref().map(Some))? - .store_as::<_, Either<(), Ref>>( - Some(self.body.to_cell()?) - // store empty cell inline - .filter(|cell| !cell.is_empty()), - )?; + .store_as::<_, &Option>(&self.init)? + .store_as::<_, EitherInlineOrRef>(&self.body)?; Ok(()) } } diff --git a/crates/tlb/src/as/reference.rs b/crates/tlb/src/as/reference.rs index 575929b..500053b 100644 --- a/crates/tlb/src/as/reference.rs +++ b/crates/tlb/src/as/reference.rs @@ -1,5 +1,7 @@ use core::marker::PhantomData; +use tlbits::{either::Either, r#as::args::NoArgs, ser::BitWriter}; + use crate::{ de::{ args::r#as::CellDeserializeAsWithArgs, r#as::CellDeserializeAs, CellParser, CellParserError, @@ -7,7 +9,7 @@ use crate::{ ser::{ args::r#as::CellSerializeAsWithArgs, r#as::CellSerializeAs, CellBuilder, CellBuilderError, }, - ResultExt, + Cell, ResultExt, }; use super::Same; @@ -68,3 +70,69 @@ where parser.parse_reference_as_with::(args).context("^") } } + +/// ```tlb +/// {X:Type} Either X ^X = EitherInlineOrRef X +/// ``` +pub struct EitherInlineOrRef(PhantomData); + +impl CellSerializeAs for EitherInlineOrRef +where + As: CellSerializeAs, +{ + #[inline] + fn store_as(source: &T, builder: &mut CellBuilder) -> Result<(), CellBuilderError> { + EitherInlineOrRef::>::store_as_with(source, builder, ()) + } +} + +impl CellSerializeAsWithArgs for EitherInlineOrRef +where + As: CellSerializeAsWithArgs, +{ + type Args = As::Args; + + #[inline] + fn store_as_with( + source: &T, + builder: &mut CellBuilder, + args: Self::Args, + ) -> Result<(), CellBuilderError> { + let mut b = Cell::builder(); + As::store_as_with(source, &mut b, args)?; + let cell = b.into_cell(); + builder.store_as::<_, Either>( + if cell.data.len() <= builder.capacity_left() { + Either::Left + } else { + Either::Right + }(cell), + )?; + Ok(()) + } +} + +impl<'de, T, As> CellDeserializeAs<'de, T> for EitherInlineOrRef +where + As: CellDeserializeAs<'de, T>, +{ + #[inline] + fn parse_as(parser: &mut CellParser<'de>) -> Result> { + EitherInlineOrRef::>::parse_as_with(parser, ()) + } +} + +impl<'de, T, As> CellDeserializeAsWithArgs<'de, T> for EitherInlineOrRef +where + As: CellDeserializeAsWithArgs<'de, T>, +{ + type Args = As::Args; + + #[inline] + fn parse_as_with( + parser: &mut CellParser<'de>, + args: Self::Args, + ) -> Result> { + Either::>::parse_as_with(parser, args).map(Either::into_inner) + } +} diff --git a/crates/tlb/src/ser/builder.rs b/crates/tlb/src/ser/builder.rs index 9ceda9d..8d58350 100644 --- a/crates/tlb/src/ser/builder.rs +++ b/crates/tlb/src/ser/builder.rs @@ -213,6 +213,11 @@ impl CellBuilder { impl BitWriter for CellBuilder { type Error = ::Error; + #[inline] + fn capacity_left(&self) -> usize { + self.data.capacity_left() + } + #[inline] fn write_bit(&mut self, bit: bool) -> Result<(), Self::Error> { self.data.write_bit(bit)?;