use core::{
fmt::{self, Debug},
hash::Hash,
ops::Deref,
};
use std::sync::Arc;
use bitvec::{order::Msb0, vec::BitVec};
use digest::{Digest, Output};
use crate::{
de::{CellDeserialize, CellDeserializeAs, CellParser, CellParserError},
ser::CellBuilder,
};
#[derive(Clone, Default, PartialEq, Eq, Hash)]
pub struct Cell {
pub is_exotic: bool,
pub data: BitVec<u8, Msb0>,
pub references: Vec<Arc<Self>>,
}
impl Cell {
#[inline]
#[must_use]
pub const fn builder() -> CellBuilder {
CellBuilder::new()
}
#[inline]
#[must_use]
pub const fn new() -> Self {
Self {
is_exotic: false,
data: BitVec::EMPTY,
references: Vec::new(),
}
}
#[inline]
#[must_use]
pub fn parser(&self) -> CellParser<'_> {
CellParser::new(self.is_exotic, &self.data, &self.references)
}
#[inline]
pub fn parse_fully<'de, T>(&'de self, args: T::Args) -> Result<T, CellParserError<'de>>
where
T: CellDeserialize<'de>,
{
let mut parser = self.parser();
let v = parser.parse(args)?;
parser.ensure_empty()?;
Ok(v)
}
#[inline]
pub fn parse_fully_as<'de, T, As>(&'de self, args: As::Args) -> Result<T, CellParserError<'de>>
where
As: CellDeserializeAs<'de, T> + ?Sized,
{
let mut parser = self.parser();
let v = parser.parse_as::<T, As>(args)?;
parser.ensure_empty()?;
Ok(v)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty() && self.references.is_empty()
}
#[inline]
fn data_bytes(&self) -> (usize, &[u8]) {
(self.data.len(), self.data.as_raw_slice())
}
#[inline]
pub fn level(&self) -> u8 {
self.references
.iter()
.map(Deref::deref)
.map(Cell::level)
.max()
.unwrap_or(0)
}
#[inline]
fn refs_descriptor(&self) -> u8 {
let is_exotic: u8 = if self.is_exotic { 1 } else { 0 };
self.references.len() as u8 | (is_exotic << 3) | (self.level() << 5)
}
#[inline]
fn bits_descriptor(&self) -> u8 {
let b = self.data.len();
(b / 8) as u8 + b.div_ceil(8) as u8
}
#[inline]
pub fn max_depth(&self) -> u16 {
let data = self.data.as_raw_slice();
if self.is_exotic && data.len() >= 36 && data[0] == 0x01 {
let level = data[1].count_ones() as usize;
if data.len() == 1 + 1 + 32 * level + 2 * level {
let depth_offset = 1 + 1 + 32 * level;
return u16::from_be_bytes([data[depth_offset], data[depth_offset + 1]]);
}
}
self.references
.iter()
.map(Deref::deref)
.map(Cell::max_depth)
.max()
.map(|d| d + 1)
.unwrap_or(0)
}
pub fn hash_digest<D>(&self) -> [u8; 32]
where
D: Digest,
Output<D>: Into<[u8; 32]>,
{
let data = self.data.as_raw_slice();
if self.is_exotic && data.len() >= 36 && data[0] == 0x01 {
let mut h = [0u8; 32];
h.copy_from_slice(&data[2..34]);
return h;
}
let mut d = D::new();
d.update([self.refs_descriptor(), self.bits_descriptor()]);
let rest_bits = self.data.len() % 8;
if rest_bits == 0 {
d.update(self.data.as_raw_slice());
} else {
let (last, data) = self
.data
.as_raw_slice()
.split_last()
.unwrap_or_else(|| unreachable!());
d.update(data);
let mut last = last & (!0u8 << (8 - rest_bits)); last |= 1 << (8 - rest_bits - 1); d.update([last])
}
for r in &self.references {
d.update(r.max_depth().to_be_bytes());
}
for r in &self.references {
d.update(r.hash_digest::<D>());
}
d.finalize().into()
}
#[cfg(feature = "sha2")]
#[inline]
pub fn hash(&self) -> [u8; 32] {
self.hash_digest::<sha2::Sha256>()
}
}
impl Debug for Cell {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(f, "{}[0b", self.data.len())?;
for bit in &self.data {
write!(f, "{}", if *bit { '1' } else { '0' })?;
}
write!(f, "]")?;
} else {
let (bits_len, data) = self.data_bytes();
write!(f, "{}[0x{}]", bits_len, hex::encode_upper(data))?;
}
if self.references.is_empty() {
return Ok(());
}
write!(f, " -> ")?;
f.debug_set().entries(&self.references).finish()
}
}
#[cfg(feature = "arbitrary")]
const _: () = {
use arbitrary::{Arbitrary, MaxRecursionReached, Result, Unstructured, size_hint};
use bitvec::mem::bits_of;
use crate::ser::{MAX_BITS_LEN, MAX_REFS_COUNT};
impl<'a> Arbitrary<'a> for Cell {
fn arbitrary(u: &mut Unstructured<'a>) -> Result<Self> {
Ok(Self {
is_exotic: false,
data: {
let len_bytes = u
.arbitrary_len::<u8>()?
.min(MAX_BITS_LEN.div_ceil(bits_of::<u8>()));
let bytes = u.bytes(len_bytes)?;
let mut bits = BitVec::from_slice(bytes);
bits.truncate(MAX_BITS_LEN);
bits
},
references: u
.arbitrary_iter()?
.take(MAX_REFS_COUNT)
.collect::<Result<_>>()?,
})
}
#[inline]
fn size_hint(depth: usize) -> (usize, Option<usize>) {
Self::try_size_hint(depth).unwrap_or_default()
}
fn try_size_hint(depth: usize) -> Result<(usize, Option<usize>), MaxRecursionReached> {
size_hint::try_recursion_guard(depth, |depth| {
Ok(size_hint::and(
(0, Some(MAX_BITS_LEN.div_ceil(bits_of::<u8>()))),
<Vec<Arc<Self>> as Arbitrary>::size_hint(depth),
))
})
}
fn arbitrary_take_rest(mut u: Unstructured<'a>) -> Result<Self> {
Ok(Self {
is_exotic: false,
data: {
let len_bytes = u.len().min(MAX_BITS_LEN.div_ceil(bits_of::<u8>()));
let bytes = u.bytes(len_bytes)?;
let mut bits = BitVec::from_slice(bytes);
bits.truncate(MAX_BITS_LEN);
bits
},
references: u
.arbitrary_take_rest_iter()?
.take(MAX_REFS_COUNT)
.collect::<Result<_>>()?,
})
}
}
};
#[cfg(test)]
mod tests {
use hex_literal::hex;
use crate::{
r#as::{Data, Ref},
bits::{NBits, NoArgs, ser::BitWriterExt},
ser::{CellSerializeExt, CellSerializeWrapAsExt},
tests::assert_store_parse_as_eq,
};
use super::*;
#[test]
fn zero_depth() {
assert_eq!(().to_cell(()).unwrap().max_depth(), 0)
}
#[test]
fn max_depth() {
let cell = (
().wrap_as::<Ref>(),
(().wrap_as::<Ref>(), ().wrap_as::<Ref<Ref>>())
.wrap_as::<Ref>()
.wrap_as::<Ref>(),
((), ()),
)
.to_cell(NoArgs::EMPTY)
.unwrap();
assert_eq!(cell.max_depth(), 4)
}
#[test]
fn cell_serde() {
assert_store_parse_as_eq::<
_,
(
Data<NBits<1>>,
Ref<Data<NBits<24>>>,
Ref<(Data<NBits<7>>, Ref<Data<NBits<24>>>)>,
),
>((0b1, 0x0AAAAA, (0x7F, 0x0AAAAA)), NoArgs::EMPTY);
}
#[test]
fn hash_no_refs() {
let mut builder = Cell::builder();
builder.pack_as::<_, NBits<32>>(0x0000000F, ()).unwrap();
let cell = builder.into_cell();
assert_eq!(
cell.hash(),
hex!("57b520dbcb9d135863fc33963cde9f6db2ded1430d88056810a2c9434a3860f9")
);
}
#[test]
fn hash_with_refs() {
let mut builder = Cell::builder();
builder
.store_as::<_, Data<NBits<24>>>(0x00000B, ())
.unwrap()
.store_reference_as::<_, Data>(0x0000000F_u32, ())
.unwrap()
.store_reference_as::<_, Data>(0x0000000F_u32, ())
.unwrap();
let cell = builder.into_cell();
assert_eq!(
cell.hash(),
hex!("f345277cc6cfa747f001367e1e873dcfa8a936b8492431248b7a3eeafa8030e7")
);
}
#[test]
fn cell_exotic_serde() {
let expected = Cell {
is_exotic: true,
data: BitVec::from_slice(&[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
references: vec![Arc::new(Cell {
is_exotic: false,
data: BitVec::from_slice(&[0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]),
references: vec![],
})],
};
let actual = expected
.to_cell(NoArgs::EMPTY)
.unwrap()
.parse_fully::<Cell>(NoArgs::EMPTY)
.unwrap();
assert_eq!(actual, expected);
}
}