1use crate::cell::{Cell, CellBuilder, CellContext, CellFamily, DynCell, HashBytes, Load, Store};
4
5#[cfg(feature = "serde")]
6pub use self::serde::SerdeBoc;
7
8pub mod de;
10pub mod ser;
12
13#[cfg(feature = "serde")]
14mod serde;
15
16#[cfg(test)]
17mod tests;
18
19#[derive(Default, Copy, Clone, Eq, PartialEq)]
21pub enum BocTag {
22 Indexed,
24 IndexedCrc32,
26 #[default]
28 Generic,
29}
30
31impl BocTag {
32 const INDEXED: [u8; 4] = [0x68, 0xff, 0x65, 0xf3];
33 const INDEXED_CRC32: [u8; 4] = [0xac, 0xc3, 0xa7, 0x28];
34 const GENERIC: [u8; 4] = [0xb5, 0xee, 0x9c, 0x72];
35
36 pub const fn from_bytes(data: [u8; 4]) -> Option<Self> {
38 match data {
39 Self::GENERIC => Some(Self::Generic),
40 Self::INDEXED_CRC32 => Some(Self::IndexedCrc32),
41 Self::INDEXED => Some(Self::Indexed),
42 _ => None,
43 }
44 }
45
46 pub const fn to_bytes(self) -> [u8; 4] {
48 match self {
49 Self::Indexed => Self::INDEXED,
50 Self::IndexedCrc32 => Self::INDEXED_CRC32,
51 Self::Generic => Self::GENERIC,
52 }
53 }
54}
55
56pub struct Boc;
58
59impl Boc {
60 #[inline]
62 pub fn file_hash(data: impl AsRef<[u8]>) -> HashBytes {
63 use sha2::Digest;
64
65 sha2::Sha256::digest(data).into()
66 }
67
68 #[cfg(feature = "blake3")]
70 #[inline]
71 pub fn file_hash_blake(data: impl AsRef<[u8]>) -> HashBytes {
72 #[cfg(not(feature = "rayon"))]
73 {
74 blake3::hash(data.as_ref()).into()
75 }
76
77 #[cfg(feature = "rayon")]
78 {
79 const RAYON_THRESHOLD: usize = 256 * 1024;
81
82 let data = data.as_ref();
83 if data.len() < RAYON_THRESHOLD {
84 blake3::hash(data)
85 } else {
86 blake3::Hasher::new().update_rayon(data).finalize()
87 }
88 .into()
89 }
90 }
91
92 pub fn encode_hex<T>(cell: T) -> String
95 where
96 T: AsRef<DynCell>,
97 {
98 hex::encode(Self::encode(cell))
99 }
100
101 #[cfg(any(feature = "base64", test))]
104 pub fn encode_base64<T>(cell: T) -> String
105 where
106 T: AsRef<DynCell>,
107 {
108 crate::util::encode_base64(Self::encode(cell))
109 }
110
111 #[cfg(feature = "rayon")]
116 pub fn encode_hex_rayon<T>(cell: T) -> String
117 where
118 T: AsRef<DynCell>,
119 {
120 hex::encode(Self::encode_rayon(cell))
121 }
122
123 #[cfg(all(any(feature = "base64", test), feature = "rayon"))]
128 pub fn encode_base64_rayon<T>(cell: T) -> String
129 where
130 T: AsRef<DynCell>,
131 {
132 crate::util::encode_base64(Self::encode_rayon(cell))
133 }
134
135 pub fn encode<T>(cell: T) -> Vec<u8>
137 where
138 T: AsRef<DynCell>,
139 {
140 fn encode_impl(cell: &DynCell) -> Vec<u8> {
141 let mut result = Vec::new();
142 ser::BocHeader::<ahash::RandomState>::with_root(cell).encode(&mut result);
143 result
144 }
145 encode_impl(cell.as_ref())
146 }
147
148 #[cfg(feature = "rayon")]
152 pub fn encode_rayon<T>(cell: T) -> Vec<u8>
153 where
154 T: AsRef<DynCell>,
155 {
156 fn encode_impl(cell: &DynCell) -> Vec<u8> {
157 let mut result = Vec::new();
158 ser::BocHeader::<ahash::RandomState>::with_root(cell).encode_rayon(&mut result);
159 result
160 }
161 encode_impl(cell.as_ref())
162 }
163
164 pub fn encode_pair<T1, T2>((cell1, cell2): (T1, T2)) -> Vec<u8>
166 where
167 T1: AsRef<DynCell>,
168 T2: AsRef<DynCell>,
169 {
170 fn encode_pair_impl(cell1: &DynCell, cell2: &DynCell) -> Vec<u8> {
171 let mut result = Vec::new();
172 let mut encoder = ser::BocHeader::<ahash::RandomState>::with_root(cell1);
173 encoder.add_root(cell2);
174 encoder.encode(&mut result);
175 result
176 }
177 encode_pair_impl(cell1.as_ref(), cell2.as_ref())
178 }
179
180 pub fn decode_hex<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
183 fn decode_hex_impl(data: &[u8]) -> Result<Cell, de::Error> {
184 match hex::decode(data) {
185 Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
186 Err(_) => Err(de::Error::UnknownBocTag),
187 }
188 }
189 decode_hex_impl(data.as_ref())
190 }
191
192 #[cfg(any(feature = "base64", test))]
195 #[inline]
196 pub fn decode_base64<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
197 fn decode_base64_impl(data: &[u8]) -> Result<Cell, de::Error> {
198 match crate::util::decode_base64(data) {
199 Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
200 Err(_) => Err(de::Error::UnknownBocTag),
201 }
202 }
203 decode_base64_impl(data.as_ref())
204 }
205
206 #[inline]
208 pub fn decode<T>(data: T) -> Result<Cell, de::Error>
209 where
210 T: AsRef<[u8]>,
211 {
212 fn decode_impl(data: &[u8]) -> Result<Cell, de::Error> {
213 Boc::decode_ext(data, &mut Cell::empty_context())
214 }
215 decode_impl(data.as_ref())
216 }
217
218 #[inline]
220 pub fn decode_pair<T>(data: T) -> Result<(Cell, Cell), de::Error>
221 where
222 T: AsRef<[u8]>,
223 {
224 fn decode_pair_impl(data: &[u8]) -> Result<(Cell, Cell), de::Error> {
225 Boc::decode_pair_ext(data, &mut Cell::empty_context())
226 }
227 decode_pair_impl(data.as_ref())
228 }
229
230 pub fn decode_ext(data: &[u8], context: &mut dyn CellContext) -> Result<Cell, de::Error> {
232 use self::de::*;
233
234 let header = ok!(de::BocHeader::decode(
235 data,
236 &Options {
237 max_roots: Some(1),
238 min_roots: Some(1),
239 },
240 ));
241
242 if let Some(&root) = header.roots().first() {
243 let cells = ok!(header.finalize(context));
244 if let Some(root) = cells.get(root) {
245 return Ok(root);
246 }
247 }
248
249 Err(de::Error::RootCellNotFound)
250 }
251
252 pub fn decode_pair_ext(
254 data: &[u8],
255 context: &mut dyn CellContext,
256 ) -> Result<(Cell, Cell), de::Error> {
257 use self::de::*;
258
259 let header = ok!(de::BocHeader::decode(
260 data,
261 &Options {
262 max_roots: Some(2),
263 min_roots: Some(2),
264 },
265 ));
266
267 let mut roots = header.roots().iter();
268 if let (Some(&root1), Some(&root2)) = (roots.next(), roots.next()) {
269 let cells = ok!(header.finalize(context));
270 if let (Some(root1), Some(root2)) = (cells.get(root1), cells.get(root2)) {
271 return Ok((root1, root2));
272 }
273 }
274
275 Err(de::Error::RootCellNotFound)
276 }
277
278 #[cfg(feature = "serde")]
280 pub fn serialize<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
281 where
282 SerdeBoc<T>: ::serde::Serialize,
283 S: ::serde::Serializer,
284 {
285 use ::serde::Serialize;
286
287 SerdeBoc::from(value).serialize(serializer)
288 }
289
290 #[cfg(feature = "serde")]
292 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
293 where
294 SerdeBoc<T>: ::serde::Deserialize<'de>,
295 D: ::serde::Deserializer<'de>,
296 {
297 use ::serde::Deserialize;
298
299 SerdeBoc::<T>::deserialize(deserializer).map(SerdeBoc::into_inner)
300 }
301}
302
303pub struct BocRepr;
305
306impl BocRepr {
307 pub fn encode_hex<T>(data: T) -> Result<String, crate::error::Error>
310 where
311 T: Store,
312 {
313 let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
314 Ok(hex::encode(boc))
315 }
316
317 #[cfg(any(feature = "base64", test))]
320 pub fn encode_base64<T>(data: T) -> Result<String, crate::error::Error>
321 where
322 T: Store,
323 {
324 let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
325 Ok(crate::util::encode_base64(boc))
326 }
327
328 #[cfg(feature = "rayon")]
333 pub fn encode_hex_rayon<T>(data: T) -> Result<String, crate::error::Error>
334 where
335 T: Store,
336 {
337 let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
338 Ok(hex::encode(boc))
339 }
340
341 #[cfg(all(any(feature = "base64", test), feature = "rayon"))]
346 pub fn encode_base64_rayon<T>(data: T) -> Result<String, crate::error::Error>
347 where
348 T: Store,
349 {
350 let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
351 Ok(crate::util::encode_base64(boc))
352 }
353
354 pub fn encode<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
356 where
357 T: Store,
358 {
359 Self::encode_ext(data, &mut Cell::empty_context())
360 }
361
362 #[cfg(feature = "rayon")]
366 pub fn encode_rayon<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
367 where
368 T: Store,
369 {
370 Self::encode_rayon_ext(data, &mut Cell::empty_context())
371 }
372
373 #[inline]
376 pub fn decode_hex<T, D>(data: D) -> Result<T, BocReprError>
377 where
378 for<'a> T: Load<'a>,
379 D: AsRef<[u8]>,
380 {
381 fn decode_hex_impl<T>(data: &[u8]) -> Result<T, BocReprError>
382 where
383 for<'a> T: Load<'a>,
384 {
385 match hex::decode(data) {
386 Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
387 Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
388 }
389 }
390 decode_hex_impl::<T>(data.as_ref())
391 }
392
393 #[cfg(any(feature = "base64", test))]
396 #[inline]
397 pub fn decode_base64<T, D>(data: D) -> Result<T, BocReprError>
398 where
399 for<'a> T: Load<'a>,
400 D: AsRef<[u8]>,
401 {
402 fn decode_base64_impl<T>(data: &[u8]) -> Result<T, BocReprError>
403 where
404 for<'a> T: Load<'a>,
405 {
406 match crate::util::decode_base64(data) {
407 Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
408 Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
409 }
410 }
411 decode_base64_impl::<T>(data.as_ref())
412 }
413
414 #[inline]
416 pub fn decode<T, D>(data: D) -> Result<T, BocReprError>
417 where
418 for<'a> T: Load<'a>,
419 D: AsRef<[u8]>,
420 {
421 fn decode_impl<T>(data: &[u8]) -> Result<T, BocReprError>
422 where
423 for<'a> T: Load<'a>,
424 {
425 BocRepr::decode_ext(data, &mut Cell::empty_context())
426 }
427 decode_impl::<T>(data.as_ref())
428 }
429
430 pub fn encode_ext<T>(
432 data: T,
433 context: &mut dyn CellContext,
434 ) -> Result<Vec<u8>, crate::error::Error>
435 where
436 T: Store,
437 {
438 fn encode_ext_impl(
439 data: &dyn Store,
440 context: &mut dyn CellContext,
441 ) -> Result<Vec<u8>, crate::error::Error> {
442 let mut builder = CellBuilder::new();
443 ok!(data.store_into(&mut builder, context));
444 let cell = ok!(builder.build_ext(context));
445 Ok(Boc::encode(cell))
446 }
447 encode_ext_impl(&data, context)
448 }
449
450 #[cfg(feature = "rayon")]
454 pub fn encode_rayon_ext<T>(
455 data: T,
456 context: &mut dyn CellContext,
457 ) -> Result<Vec<u8>, crate::error::Error>
458 where
459 T: Store,
460 {
461 fn encode_ext_impl(
462 data: &dyn Store,
463 context: &mut dyn CellContext,
464 ) -> Result<Vec<u8>, crate::error::Error> {
465 let mut builder = CellBuilder::new();
466 ok!(data.store_into(&mut builder, context));
467 let cell = ok!(builder.build_ext(context));
468 Ok(Boc::encode_rayon(cell))
469 }
470 encode_ext_impl(&data, context)
471 }
472
473 pub fn decode_ext<T>(data: &[u8], context: &mut dyn CellContext) -> Result<T, BocReprError>
475 where
476 for<'a> T: Load<'a>,
477 {
478 let cell = match Boc::decode_ext(data, context) {
479 Ok(cell) => cell,
480 Err(e) => return Err(BocReprError::InvalidBoc(e)),
481 };
482
483 match cell.as_ref().parse::<T>() {
484 Ok(data) => Ok(data),
485 Err(e) => Err(BocReprError::InvalidData(e)),
486 }
487 }
488
489 #[cfg(feature = "serde")]
492 pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
493 where
494 S: ::serde::Serializer,
495 T: Store,
496 {
497 use ::serde::ser::{Error, Serialize};
498
499 let context = &mut Cell::empty_context();
500
501 let mut builder = CellBuilder::new();
502 if data.store_into(&mut builder, context).is_err() {
503 return Err(Error::custom("cell overflow"));
504 }
505
506 let cell = match builder.build_ext(context) {
507 Ok(cell) => cell,
508 Err(_) => return Err(Error::custom("failed to store into builder")),
509 };
510
511 cell.as_ref().serialize(serializer)
512 }
513
514 #[cfg(feature = "serde")]
517 pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
518 where
519 D: ::serde::Deserializer<'de>,
520 for<'a> T: Load<'a>,
521 {
522 use ::serde::de::Error;
523
524 let cell = ok!(Boc::deserialize::<Cell, _>(deserializer));
525 match cell.as_ref().parse::<T>() {
526 Ok(data) => Ok(data),
527 Err(_) => Err(Error::custom("failed to decode object from cells")),
528 }
529 }
530}
531
532#[derive(Debug, thiserror::Error)]
534pub enum BocReprError {
535 #[error("invalid BOC")]
537 InvalidBoc(#[source] de::Error),
538 #[error("failed to decode object from cells")]
540 InvalidData(#[source] crate::error::Error),
541}