use crate::bail_ton_core_data;
use crate::cell::boc::raw_cell::{RawCell, RefPosStorage};
use crate::cell::boc::read_var_size::read_var_size;
use crate::cell::ton_cell::{CellBytesReader, RefStorage};
use crate::cell::{TonCell, TonHash};
use crate::errors::TonCoreError;
use bitstream_io::BigEndian;
use bitstream_io::{BitWrite, BitWriter, ByteRead};
use crc::Crc;
use smallvec::SmallVec;
use std::cell::RefCell;
use std::collections::HashMap;
use std::io::Cursor;
use std::ops::Deref;
use std::sync::Arc;
const GENERIC_BOC_MAGIC: u32 = 0xb5ee9c72;
const CRC_32_ISCSI: Crc<u32> = Crc::<u32>::new(&crc::CRC_32_ISCSI);
#[derive(PartialEq, Debug, Clone)]
pub(crate) struct RawBoC {
pub(crate) raw_cells: Vec<RawCell>,
pub(crate) roots_pos: RefPosStorage, }
impl RawBoC {
pub(crate) fn from_bytes(data_storage: Arc<Vec<u8>>) -> Result<RawBoC, TonCoreError> {
let cursor = Cursor::new(data_storage.as_slice());
let mut reader = CellBytesReader::new(cursor);
let magic = reader.read::<u32>()?;
if magic != GENERIC_BOC_MAGIC {
bail_ton_core_data!("Unexpected magic: {magic}");
};
let (has_idx, has_crc32c, _has_cache_bits, ref_pos_size_bytes) = {
let header = reader.read::<u8>()?;
let has_idx = (header & 0b1000_0000) != 0;
let has_crc32c = (header & 0b0100_0000) != 0;
let has_cache_bits = (header & 0b0010_0000) != 0;
let ref_pos_size = header & 0b0000_0111;
if ref_pos_size > 4 {
bail_ton_core_data!("Invalid BoC header: ref_pos_size={ref_pos_size} (must be <= 4)");
}
(has_idx, has_crc32c, has_cache_bits, ref_pos_size)
};
let off_bytes = reader.read::<u8>()?;
if off_bytes > 8 {
bail_ton_core_data!("Invalid BoC header: off_bytes({off_bytes}) <= 8");
}
let cells_cnt = read_var_size(&mut reader, ref_pos_size_bytes)?;
let roots_cnt = read_var_size(&mut reader, ref_pos_size_bytes)?;
if roots_cnt < 1 {
bail_ton_core_data!("Invalid BoC header: roots({roots_cnt}) >= 1");
}
let absent = read_var_size(&mut reader, ref_pos_size_bytes)?;
if roots_cnt + absent > cells_cnt {
bail_ton_core_data!("Invalid header: roots({roots_cnt}) + absent({absent}) <= cells({cells_cnt})");
}
let _tot_cells_size = read_var_size(&mut reader, off_bytes)?;
let mut roots_pos = RefPosStorage::with_capacity(roots_cnt);
for _ in 0..roots_cnt {
roots_pos.push(read_var_size(&mut reader, ref_pos_size_bytes)?)
}
if has_idx {
reader.skip(cells_cnt as u32 * off_bytes as u32)?;
}
let mut cells = Vec::with_capacity(cells_cnt);
for _ in 0..cells_cnt {
let cell = RawCell::read(&mut reader, ref_pos_size_bytes, data_storage.clone())?;
cells.push(cell);
}
let _crc32c = if has_crc32c { reader.read::<u32>()? } else { 0 };
Ok(RawBoC {
raw_cells: cells,
roots_pos,
})
}
pub(crate) fn to_bytes(&self, add_crc32: bool) -> Result<Vec<u8>, TonCoreError> {
let root_count = self.roots_pos.len();
let ref_size_bits = 32 - (self.raw_cells.len() as u32).leading_zeros();
let ref_pos_size_bytes = ref_size_bits.div_ceil(8);
let has_idx = false;
let mut full_size = 0u32;
for cell in &self.raw_cells {
full_size += cell.size_in_boc_bytes(ref_pos_size_bytes);
}
let num_offset_bits = 32 - full_size.leading_zeros();
let num_offset_bytes = num_offset_bits.div_ceil(8);
let total_size = 4 + 1 + 1 + 3 * ref_pos_size_bytes + num_offset_bytes + ref_pos_size_bytes + (if has_idx { self.raw_cells.len() as u32 * num_offset_bytes } else { 0 }) +
full_size +
(if add_crc32 { 4 } else { 0 });
let mut writer = BitWriter::endian(Vec::with_capacity(total_size as usize), BigEndian);
writer.write_var(32, GENERIC_BOC_MAGIC)?;
writer.write_bit(has_idx)?;
writer.write_bit(add_crc32)?;
writer.write_bit(false)?; writer.write_var(2, 0)?; writer.write_var(3, ref_pos_size_bytes)?;
writer.write_var(8, num_offset_bytes)?;
writer.write_var(8 * ref_pos_size_bytes, self.raw_cells.len() as u32)?;
writer.write_var(8 * ref_pos_size_bytes, root_count as u32)?;
writer.write_var(8 * ref_pos_size_bytes, 0)?; writer.write_var(8 * num_offset_bytes, full_size)?;
for &root in &self.roots_pos {
writer.write_var(8 * ref_pos_size_bytes, root as u32)?;
}
for cell in &self.raw_cells {
cell.write_to(&mut writer, ref_pos_size_bytes)?;
}
writer.byte_align()?;
let mut bytes = writer.into_writer();
if add_crc32 {
bytes.extend(CRC_32_ISCSI.checksum(&bytes).to_le_bytes());
}
Ok(bytes)
}
pub(crate) fn into_ton_cells(self) -> Result<Vec<TonCell>, TonCoreError> {
let cells_len = self.raw_cells.len();
let mut cells: Vec<TonCell> = Vec::with_capacity(cells_len);
for (cell_index, cell_raw) in self.raw_cells.into_iter().enumerate().rev() {
let mut refs = RefStorage::with_capacity(cell_raw.refs_pos.len());
for ref_index in &cell_raw.refs_pos {
if *ref_index <= cell_index {
bail_ton_core_data!("Invalid BoC: ref to parent cell detected");
}
refs.push(cells[cells_len - 1 - ref_index].clone());
}
cells.push(cell_raw.into_ton_cell(refs));
}
let mut roots = Vec::with_capacity(self.roots_pos.len());
for root_index in self.roots_pos {
roots.push(cells[cells_len - 1 - root_index].clone());
}
Ok(roots)
}
pub(crate) fn from_ton_cells(roots: &[TonCell], preserve_hash: bool) -> Result<Self, TonCoreError> {
let cell_by_hash = build_and_verify_index(roots)?;
let mut cell_sorted: Vec<_> = cell_by_hash.values().collect();
cell_sorted.sort_unstable_by(|a, b| a.index.cmp(&b.index));
cell_sorted
.iter()
.enumerate()
.for_each(|(real_index, indexed_cell)| *indexed_cell.index.borrow_mut() = real_index);
let raw_cells = cell_sorted
.into_iter()
.map(|indexed| raw_from_indexed(indexed.cell, &cell_by_hash, preserve_hash))
.collect::<Result<_, TonCoreError>>()?;
let roots_pos = roots.iter().map(|x| get_position(x, &cell_by_hash)).collect::<Result<_, TonCoreError>>()?;
Ok(RawBoC { raw_cells, roots_pos })
}
}
#[derive(Debug, Clone)]
struct IndexedCell<'a> {
cell: &'a TonCell,
index: RefCell<usize>, }
fn build_and_verify_index(roots: &[TonCell]) -> Result<HashMap<TonHash, IndexedCell<'_>>, TonCoreError> {
let mut cur_cells = Vec::from_iter(roots.iter());
let mut new_hash_index = 0;
let mut cells_by_hash = HashMap::new();
while !cur_cells.is_empty() {
let mut next_cells = Vec::with_capacity(cur_cells.len() * 4);
for cell in cur_cells {
let hash = cell.hash()?;
if cells_by_hash.contains_key(hash) {
continue; }
let indexed_cell = IndexedCell {
cell,
index: RefCell::new(new_hash_index),
};
cells_by_hash.insert(*hash, indexed_cell);
new_hash_index += 1;
next_cells.extend(cell.refs());
}
cur_cells = next_cells;
}
let mut verify_order = true;
while verify_order {
verify_order = false;
for index_cell in cells_by_hash.values() {
for ref_cell in index_cell.cell.refs() {
let ref_hash = ref_cell.hash()?;
if let Some(indexed) = cells_by_hash.get(ref_hash)
&& indexed.index < index_cell.index
{
*indexed.index.borrow_mut() = new_hash_index;
new_hash_index += 1;
verify_order = true; }
}
}
}
Ok(cells_by_hash)
}
fn raw_from_indexed(
cell: &TonCell,
cells_by_hash: &HashMap<TonHash, IndexedCell>,
preserve_hash: bool,
) -> Result<RawCell, TonCoreError> {
let refs_positions = raw_cell_refs_indexes(cell, cells_by_hash)?;
Ok(RawCell {
cell_type: cell.cell_type(),
data_storage: cell.cell_data.data_storage.clone(),
start_bit: cell.borders.start_bit,
end_bit: cell.borders.end_bit,
refs_pos: refs_positions,
level_mask: cell.level_mask(),
hashes_depths: if preserve_hash { cell.meta.hashes_depths.get().cloned() } else { None },
})
}
fn raw_cell_refs_indexes(
cell: &TonCell,
cells_by_hash: &HashMap<TonHash, IndexedCell>,
) -> Result<SmallVec<[usize; 4]>, TonCoreError> {
let mut vec = SmallVec::with_capacity(cell.refs().len());
for ref_pos in 0..cell.refs().len() {
let cell_ref = &cell.refs()[ref_pos];
vec.push(get_position(cell_ref, cells_by_hash)?);
}
Ok(vec)
}
fn get_position(cell: &TonCell, call_by_hash: &HashMap<TonHash, IndexedCell>) -> Result<usize, TonCoreError> {
let hash = cell.hash()?;
call_by_hash
.get(hash)
.ok_or_else(|| TonCoreError::Custom(format!("cell with hash {hash:?} not found in available hashes")))
.map(|indexed_cell| *indexed_cell.index.borrow().deref())
}