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