1use std::ops::Deref;
2
3use smallvec::SmallVec;
4
5use super::BocTag;
6use crate::cell::{Cell, CellContext, CellDescriptor, CellParts, LevelMask, MAX_REF_COUNT};
7use crate::util::{ArrayVec, read_be_u32_fast, read_be_u64_fast, unlikely};
8
9#[derive(Debug, Default, Clone)]
11pub struct Options {
12 pub min_roots: Option<usize>,
14 pub max_roots: Option<usize>,
16}
17
18impl Options {
19 pub const fn exact(number: usize) -> Self {
21 Self {
22 min_roots: Some(number),
23 max_roots: Some(number),
24 }
25 }
26}
27
28pub struct BocHeader<'a> {
30 ref_size: usize,
31 cells: SmallVec<[&'a [u8]; CELLS_ON_STACK]>,
32 roots: SmallVec<[u32; ROOTS_ON_STACK]>,
33}
34
35impl<'a> BocHeader<'a> {
36 #[allow(clippy::needless_late_init)]
38 pub fn decode(data: &'a [u8], options: &Options) -> Result<Self, Error> {
39 let mut reader = BocReader::new(data.len());
40
41 if unlikely(!reader.require(6)) {
45 return Err(Error::UnexpectedEof);
46 }
47 debug_assert!(data.len() >= 6);
48
49 let [flags, offset_size] = unsafe { *(data.as_ptr().add(4) as *const [u8; 2]) };
51
52 let has_index;
53 let has_crc;
54 let has_cache_bits;
55 let ref_size;
56 let supports_multiple_roots;
57
58 let boc_tag = unsafe { reader.read_boc_tag(data) };
60 match boc_tag {
61 Some(BocTag::Indexed) => {
62 has_index = true;
63 has_crc = false;
64 has_cache_bits = false;
65 ref_size = flags as usize;
66 supports_multiple_roots = false;
67 }
68 Some(BocTag::IndexedCrc32) => {
69 has_index = true;
70 has_crc = true;
71 has_cache_bits = false;
72 ref_size = flags as usize;
73 supports_multiple_roots = false;
74 }
75 Some(BocTag::Generic) => {
76 has_index = flags & 0b1000_0000 != 0;
77 has_crc = flags & 0b0100_0000 != 0;
78 has_cache_bits = flags & 0b0010_0000 != 0;
79 ref_size = (flags & 0b0000_0111) as usize;
80 supports_multiple_roots = true;
81 }
82 None => return Err(Error::UnknownBocTag),
83 }
84
85 if unlikely(has_cache_bits && !has_index) {
86 return Err(Error::InvalidHeader);
87 }
88 if unlikely(ref_size == 0 || ref_size > std::mem::size_of::<u32>()) {
89 return Err(Error::InvalidRefSize);
90 }
91 debug_assert!((1..=4).contains(&ref_size));
92
93 let offset_size = offset_size as usize;
94 if unlikely(offset_size == 0 || offset_size > std::mem::size_of::<usize>()) {
95 return Err(Error::InvalidOffsetSize);
96 }
97 debug_assert!((1..=8).contains(&offset_size));
98
99 reader.advance(6);
100
101 if unlikely(!reader.require(ref_size * 3 + offset_size)) {
106 return Err(Error::InvalidHeader);
107 }
108 debug_assert!(data.len() >= (6 + ref_size * 3 + offset_size));
109
110 let (cell_count, root_count, absent_count) = unsafe {
113 (
114 reader.read_next_be_uint_fast(data, ref_size),
115 reader.read_next_be_uint_fast(data, ref_size),
116 reader.read_next_be_uint_fast(data, ref_size),
117 )
118 };
119
120 if unlikely(root_count == 0) {
122 return Err(Error::RootCellNotFound);
123 }
124 if unlikely(!supports_multiple_roots && root_count > 1) {
125 return Err(Error::UnexpectedMultipleRoots);
126 }
127 if unlikely(root_count.saturating_add(absent_count) > cell_count) {
128 return Err(Error::TooManyRootCells);
129 }
130 if unlikely(absent_count > 0) {
131 return Err(Error::AbsentCellsNotSupported);
132 }
133 if let Some(min_roots) = options.min_roots
134 && unlikely(root_count < min_roots)
135 {
136 return Err(Error::TooFewRootCells);
137 }
138
139 {
140 let max_roots = options.max_roots.unwrap_or(MAX_ROOTS);
141 if unlikely(root_count > max_roots) {
142 return Err(Error::TooManyRootCells);
143 }
144 debug_assert!(absent_count == 0 && (1..=max_roots).contains(&root_count))
145 }
146
147 let total_cells_size = unsafe { reader.read_next_be_uint_full(data, offset_size) };
150
151 const MIN_CELL_SIZE: u64 = 2; let min_total_cell_size = (cell_count as u64) * (MIN_CELL_SIZE + ref_size as u64)
157 - (root_count * ref_size) as u64;
158 if unlikely(total_cells_size < min_total_cell_size) {
159 return Err(Error::InvalidTotalSize);
160 }
161
162 let max_cell_size = 2 + 4 * (2 + 32) + 128 + (MAX_REF_COUNT as u64) * ref_size as u64; if unlikely(total_cells_size > (cell_count as u64) * max_cell_size) {
170 return Err(Error::InvalidTotalSize);
171 }
172
173 if unlikely(!reader.require(root_count * ref_size)) {
175 return Err(Error::UnexpectedEof);
176 }
177 debug_assert!(data.len() >= (6 + ref_size * 3 + offset_size + root_count * ref_size));
178
179 let mut roots = SmallVec::with_capacity(root_count);
180 if supports_multiple_roots {
181 for _ in 0..root_count {
182 let root_index = unsafe { reader.read_next_be_uint_fast(data, ref_size) };
184 if unlikely(root_index >= cell_count) {
185 return Err(Error::RootOutOfBounds);
186 }
187 roots.push(root_index as u32);
188 }
189 } else {
190 roots.push(0);
191 }
192
193 let index_size = has_index as u64 * cell_count as u64 * offset_size as u64;
195 if unlikely(!reader.require((index_size + total_cells_size + has_crc as u64 * 4) as usize))
196 {
197 return Err(Error::UnexpectedEof);
198 }
199
200 if has_index {
201 reader.advance(cell_count * offset_size);
202 }
203
204 #[cfg(not(fuzzing))]
205 let cells_start_offset = reader.offset;
206
207 let mut cells = SmallVec::with_capacity(cell_count);
208
209 let data_ptr = data.as_ptr();
210 for _ in 0..cell_count {
211 let start_ptr = unsafe { data_ptr.add(reader.offset) };
213 let total_len = ok!(CellParts::read_raw_cell_from_ptr(
214 start_ptr,
215 reader.len - reader.offset,
216 ref_size
217 ));
218 reader.advance(total_len);
219
220 let cell = unsafe { std::slice::from_raw_parts(start_ptr, total_len) };
222 cells.push(cell);
223 }
224
225 #[cfg(not(fuzzing))]
227 if (cells_start_offset as u64).saturating_add(total_cells_size) != reader.offset as u64 {
228 return Err(Error::InvalidTotalSize);
229 }
230
231 #[cfg(not(fuzzing))]
233 if has_crc {
234 if unlikely(!reader.require(4)) {
235 return Err(Error::UnexpectedEof);
236 }
237
238 let is_checksum_correct = unsafe { reader.check_crc(data) };
240 if !is_checksum_correct {
241 return Err(Error::InvalidChecksum);
242 }
243 }
244
245 Ok(Self {
246 ref_size,
247 cells,
248 roots,
249 })
250 }
251
252 pub fn finalize(&self, context: &dyn CellContext) -> Result<ProcessedCells, Error> {
254 let ref_size = self.ref_size;
255 let cell_count = self.cells.len() as u32;
256
257 let mut res = SmallVec::<[Cell; CELLS_ON_STACK]>::new();
259 if res.try_reserve_exact(cell_count as usize).is_err() {
260 return Err(Error::InvalidTotalSize);
261 }
262
263 for raw_cell in self.cells().iter().rev() {
264 let ctx = unsafe {
266 ok!(CellParts::from_raw_cell(
267 raw_cell, &res, cell_count, ref_size
268 ))
269 };
270
271 let cell = match context.finalize_cell(ctx) {
272 Ok(cell) => cell,
273 Err(_) => return Err(Error::InvalidCell),
274 };
275 res.push(cell);
276 }
277
278 Ok(ProcessedCells(res))
279 }
280
281 pub fn ref_size(&self) -> usize {
283 self.ref_size
284 }
285
286 pub fn cells(&self) -> &[&'a [u8]] {
288 &self.cells
289 }
290
291 pub fn roots(&self) -> &[u32] {
293 &self.roots
294 }
295}
296
297pub struct ProcessedCells(SmallVec<[Cell; CELLS_ON_STACK]>);
299
300impl ProcessedCells {
301 pub fn get(&self, index: u32) -> Option<Cell> {
303 self.0.get(self.0.len() - index as usize - 1).cloned()
304 }
305}
306
307struct BocReader {
310 len: usize,
311 offset: usize,
312}
313
314impl BocReader {
315 #[inline(always)]
316 const fn new(len: usize) -> Self {
317 Self { len, offset: 0 }
318 }
319
320 #[inline(always)]
321 const fn require(&self, len: usize) -> bool {
322 self.offset + len <= self.len
323 }
324
325 #[inline(always)]
326 fn advance(&mut self, bytes: usize) {
327 self.offset += bytes;
328 }
329
330 #[inline(always)]
331 unsafe fn read_boc_tag(&self, data: &[u8]) -> Option<BocTag> {
332 unsafe { BocTag::from_bytes(*(data.as_ptr() as *const [u8; 4])) }
333 }
334
335 #[inline(always)]
341 unsafe fn read_next_be_uint_fast(&mut self, data: &[u8], size: usize) -> usize {
342 unsafe {
343 let res = read_be_u32_fast(data.as_ptr().add(self.offset), size) as usize;
344 self.advance(size);
345 res
346 }
347 }
348
349 #[inline(always)]
355 unsafe fn read_next_be_uint_full(&mut self, data: &[u8], size: usize) -> u64 {
356 unsafe {
357 let res = read_be_u64_fast(data.as_ptr().add(self.offset), size);
358 self.advance(size);
359 res
360 }
361 }
362
363 #[cfg(not(fuzzing))]
364 #[inline(always)]
365 unsafe fn check_crc(&self, data: &[u8]) -> bool {
366 unsafe {
367 let data_ptr = data.as_ptr();
368 let crc_start_ptr = data_ptr.add(self.offset);
369
370 let parsed_crc = u32::from_le_bytes(*(crc_start_ptr as *const [u8; 4]));
371 let real_crc = crc32c::crc32c(std::slice::from_raw_parts(data_ptr, self.offset));
372
373 parsed_crc == real_crc
374 }
375 }
376}
377
378impl Deref for BocReader {
379 type Target = usize;
380
381 #[inline(always)]
382 fn deref(&self) -> &Self::Target {
383 &self.offset
384 }
385}
386
387impl<'a> CellParts<'a> {
388 pub unsafe fn from_raw_cell(
397 raw_cell: &'a [u8],
398 cells: &[Cell],
399 cell_count: u32,
400 ref_size: usize,
401 ) -> Result<Self, Error> {
402 unsafe {
403 let raw_cell_ptr = raw_cell.as_ptr();
404
405 let descriptor = CellDescriptor::new(*(raw_cell_ptr as *const [u8; 2]));
406 let data_len = descriptor.byte_len() as usize;
407
408 let mut data_ptr = raw_cell_ptr.add(2);
409 if unlikely(descriptor.store_hashes()) {
410 let level = descriptor.level_mask().level();
411 debug_assert!(!descriptor.cell_type().is_pruned_branch());
412 data_ptr = data_ptr.add((32 + 2) * (level as usize + 1));
413 }
414
415 let data = std::slice::from_raw_parts(data_ptr, data_len);
416 data_ptr = data_ptr.add(data_len);
417
418 let bit_len = if descriptor.is_aligned() {
419 (data_len * 8) as u16
420 } else if let Some(data) = data.last() {
421 data_len as u16 * 8 - data.trailing_zeros() as u16 - 1
422 } else {
423 0
424 };
425
426 let mut references = ArrayVec::<Cell, MAX_REF_COUNT>::default();
427 let mut children_mask = LevelMask::EMPTY;
428
429 for _ in 0..descriptor.reference_count() {
430 let child_index = read_be_u32_fast(data_ptr, ref_size);
431 if child_index >= cell_count {
432 return Err(Error::InvalidRef);
433 }
434
435 let child = match cells.get((cell_count - child_index - 1) as usize) {
436 Some(child) => child.clone(),
437 None => return Err(Error::InvalidRefOrder),
438 };
439
440 children_mask |= child.descriptor().level_mask();
441
442 references.push(child);
443
444 data_ptr = data_ptr.add(ref_size);
445 }
446
447 Ok(CellParts {
448 bit_len,
449 descriptor,
450 children_mask,
451 references,
452 data,
453 })
454 }
455 }
456
457 pub fn read_raw_cell<'b>(bytes: &mut &'b [u8], ref_size: usize) -> Result<&'b [u8], Error> {
460 let total_len = ok!(Self::read_raw_cell_from_ptr(
461 bytes.as_ptr(),
462 bytes.len(),
463 ref_size
464 ));
465 let (cell, rest) = bytes.split_at(total_len);
466 *bytes = rest;
467 Ok(cell)
468 }
469
470 fn read_raw_cell_from_ptr(
471 bytes_ptr: *const u8,
472 bytes_len: usize,
473 ref_size: usize,
474 ) -> Result<usize, Error> {
475 const _: () = assert!(std::mem::size_of::<CellDescriptor>() == 2);
476
477 if unlikely(bytes_len < 2) {
478 return Err(Error::UnexpectedEof);
479 }
480
481 let descriptor = CellDescriptor::new(unsafe { *(bytes_ptr as *const [u8; 2]) });
484
485 if unlikely(descriptor.is_absent()) {
486 return Err(Error::AbsentCellsNotSupported);
487 }
488
489 let data_len = descriptor.byte_len() as usize;
492 let ref_count = descriptor.reference_count() as usize;
493 if unlikely(ref_count > MAX_REF_COUNT) {
494 return Err(Error::InvalidRef);
495 }
496
497 let mut data_offset = 0;
498 if unlikely(descriptor.store_hashes()) {
499 let level = descriptor.level_mask().level();
500 if descriptor.is_exotic() && ref_count == 0 && level > 0 {
501 return Err(Error::UnnormalizedCell);
503 }
504 data_offset = (32 + 2) * (level as usize + 1);
505 }
506
507 let total_len = 2 + data_offset + data_len + ref_count * ref_size;
508 if unlikely(bytes_len < total_len) {
509 return Err(Error::UnexpectedEof);
510 }
511
512 if data_len > 0 && !descriptor.is_aligned() {
513 let byte_with_tag = unsafe { *bytes_ptr.add(2 + data_offset + data_len - 1) };
515 if unlikely(byte_with_tag & 0x7f == 0) {
516 return Err(Error::UnnormalizedCell);
517 }
518 }
519
520 Ok(total_len)
521 }
522}
523
524const CELLS_ON_STACK: usize = 16;
525const ROOTS_ON_STACK: usize = 2;
526
527const MAX_ROOTS: usize = 32;
528
529#[derive(Debug, Copy, Clone, thiserror::Error)]
531pub enum Error {
532 #[error("unexpected EOF")]
534 UnexpectedEof,
535 #[error("unknown BOC tag")]
537 UnknownBocTag,
538 #[error("invalid header")]
540 InvalidHeader,
541 #[error("ref index does not fit in `u32` type")]
543 InvalidRefSize,
544 #[error("cell offset does not fit in `usize` type")]
546 InvalidOffsetSize,
547 #[error("root cell not found")]
549 RootCellNotFound,
550 #[error("unexpected multiple roots")]
552 UnexpectedMultipleRoots,
553 #[error("too many root cells")]
555 TooManyRootCells,
556 #[error("absent cells are not supported")]
558 AbsentCellsNotSupported,
559 #[error("too few root cells")]
561 TooFewRootCells,
562 #[error("invalid total cells size")]
564 InvalidTotalSize,
565 #[error("root index out of bounds")]
567 RootOutOfBounds,
568 #[error("cell ref count not in range 0..=4")]
570 InvalidRef,
571 #[error("unnormalized cell")]
573 UnnormalizedCell,
574 #[error("invalid children order")]
576 InvalidRefOrder,
577 #[error("invalid cell")]
579 InvalidCell,
580 #[error("invalid checksum")]
582 InvalidChecksum,
583}