use std::sync::Arc;
use tlbits::adapters::LimitWriter;
use crate::{
Cell, Context, Error,
r#as::Ref,
bits::{
bitvec::{order::Msb0, slice::BitSlice, vec::BitVec},
ser::BitWriter,
},
};
use super::{CellSerialize, CellSerializeAs};
type CellBitWriter = LimitWriter<BitVec<u8, Msb0>>;
pub type CellBuilderError = <CellBuilder as BitWriter>::Error;
pub struct CellBuilder {
is_exotic: bool,
data: CellBitWriter,
references: Vec<Arc<Cell>>,
}
pub(crate) const MAX_BITS_LEN: usize = 1023;
pub(crate) const MAX_REFS_COUNT: usize = 4;
impl CellBuilder {
#[inline]
#[must_use]
pub(crate) const fn new() -> Self {
Self {
is_exotic: false,
data: LimitWriter::new(BitVec::EMPTY, MAX_BITS_LEN),
references: Vec::new(),
}
}
#[inline]
pub fn exotic(&mut self) -> Result<&mut Self, CellBuilderError> {
if !self.data.is_empty() || !self.references.is_empty() {
return Err(Error::custom(format!(
"cannot mark exotic cell with non-empty data: {} bits, {} references",
self.data.len(),
self.references.len(),
)));
}
self.is_exotic = true;
Ok(self)
}
#[inline]
pub fn store<T>(&mut self, value: T, args: T::Args) -> Result<&mut Self, CellBuilderError>
where
T: CellSerialize,
{
value.store(self, args)?;
Ok(self)
}
#[inline]
pub fn store_many<T>(
&mut self,
values: impl IntoIterator<Item = T>,
args: T::Args,
) -> Result<&mut Self, CellBuilderError>
where
T: CellSerialize,
T::Args: Clone,
{
for (i, v) in values.into_iter().enumerate() {
self.store(v, args.clone())
.with_context(|| format!("[{i}]"))?;
}
Ok(self)
}
#[inline]
pub fn store_as<T, As>(
&mut self,
value: T,
args: As::Args,
) -> Result<&mut Self, CellBuilderError>
where
As: CellSerializeAs<T> + ?Sized,
{
As::store_as(&value, self, args)?;
Ok(self)
}
#[inline]
pub fn store_many_as<T, As>(
&mut self,
values: impl IntoIterator<Item = T>,
args: As::Args,
) -> Result<&mut Self, CellBuilderError>
where
As: CellSerializeAs<T> + ?Sized,
As::Args: Clone,
{
for (i, v) in values.into_iter().enumerate() {
self.store_as::<T, As>(v, args.clone())
.with_context(|| format!("[{i}]"))?;
}
Ok(self)
}
#[inline]
fn ensure_reference(&self) -> Result<(), CellBuilderError> {
if self.references.len() == MAX_REFS_COUNT {
return Err(Error::custom("too many references"));
}
Ok(())
}
#[inline]
pub(crate) fn store_reference_as<T, As>(
&mut self,
value: T,
args: As::Args,
) -> Result<&mut Self, CellBuilderError>
where
As: CellSerializeAs<T> + ?Sized,
{
self.ensure_reference()?;
let mut builder = Self::new();
builder.store_as::<T, As>(value, args)?;
self.references.push(builder.into_cell().into());
Ok(self)
}
#[inline]
#[must_use]
pub fn into_cell(self) -> Cell {
Cell {
is_exotic: self.is_exotic,
data: self.data.into_inner(),
references: self.references,
}
}
}
impl BitWriter for CellBuilder {
type Error = <CellBitWriter as BitWriter>::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)?;
Ok(())
}
#[inline]
fn write_bitslice(&mut self, bits: &BitSlice<u8, Msb0>) -> Result<(), Self::Error> {
self.data.write_bitslice(bits)
}
#[inline]
fn repeat_bit(&mut self, n: usize, bit: bool) -> Result<(), Self::Error> {
self.data.repeat_bit(n, bit)
}
}
impl CellSerialize for CellBuilder {
type Args = ();
fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
if self.is_exotic {
builder.exotic()?;
}
builder.write_bitslice(&self.data)?;
builder.store_many_as::<_, Ref>(&self.references, ())?;
Ok(())
}
}