use crate::cell::{Cell, CellBuilder, CellContext, CellFamily, DynCell, HashBytes, Load, Store};
#[cfg(feature = "serde")]
pub use self::serde::SerdeBoc;
pub mod de;
pub mod ser;
#[cfg(feature = "serde")]
mod serde;
#[cfg(test)]
mod tests;
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub enum BocTag {
Indexed,
IndexedCrc32,
#[default]
Generic,
}
impl BocTag {
const INDEXED: [u8; 4] = [0x68, 0xff, 0x65, 0xf3];
const INDEXED_CRC32: [u8; 4] = [0xac, 0xc3, 0xa7, 0x28];
const GENERIC: [u8; 4] = [0xb5, 0xee, 0x9c, 0x72];
pub const fn from_bytes(data: [u8; 4]) -> Option<Self> {
match data {
Self::GENERIC => Some(Self::Generic),
Self::INDEXED_CRC32 => Some(Self::IndexedCrc32),
Self::INDEXED => Some(Self::Indexed),
_ => None,
}
}
pub const fn to_bytes(self) -> [u8; 4] {
match self {
Self::Indexed => Self::INDEXED,
Self::IndexedCrc32 => Self::INDEXED_CRC32,
Self::Generic => Self::GENERIC,
}
}
}
pub struct Boc;
impl Boc {
#[inline]
pub fn file_hash(data: impl AsRef<[u8]>) -> HashBytes {
use sha2::Digest;
sha2::Sha256::digest(data).into()
}
#[cfg(feature = "blake3")]
#[inline]
pub fn file_hash_blake(data: impl AsRef<[u8]>) -> HashBytes {
#[cfg(not(feature = "rayon"))]
{
blake3::hash(data.as_ref()).into()
}
#[cfg(feature = "rayon")]
{
const RAYON_THRESHOLD: usize = 256 * 1024;
let data = data.as_ref();
if data.len() < RAYON_THRESHOLD {
blake3::hash(data)
} else {
blake3::Hasher::new().update_rayon(data).finalize()
}
.into()
}
}
pub fn encode_hex<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
hex::encode(Self::encode(cell))
}
#[cfg(any(feature = "base64", test))]
pub fn encode_base64<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
crate::util::encode_base64(Self::encode(cell))
}
#[cfg(feature = "rayon")]
pub fn encode_hex_rayon<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
hex::encode(Self::encode_rayon(cell))
}
#[cfg(all(any(feature = "base64", test), feature = "rayon"))]
pub fn encode_base64_rayon<T>(cell: T) -> String
where
T: AsRef<DynCell>,
{
crate::util::encode_base64(Self::encode_rayon(cell))
}
pub fn encode<T>(cell: T) -> Vec<u8>
where
T: AsRef<DynCell>,
{
fn encode_impl(cell: &DynCell) -> Vec<u8> {
let mut result = Vec::new();
ser::BocHeader::<ahash::RandomState>::with_root(cell).encode(&mut result);
result
}
encode_impl(cell.as_ref())
}
#[cfg(feature = "rayon")]
pub fn encode_rayon<T>(cell: T) -> Vec<u8>
where
T: AsRef<DynCell>,
{
fn encode_impl(cell: &DynCell) -> Vec<u8> {
let mut result = Vec::new();
ser::BocHeader::<ahash::RandomState>::with_root(cell).encode_rayon(&mut result);
result
}
encode_impl(cell.as_ref())
}
pub fn encode_pair<T1, T2>((cell1, cell2): (T1, T2)) -> Vec<u8>
where
T1: AsRef<DynCell>,
T2: AsRef<DynCell>,
{
fn encode_pair_impl(cell1: &DynCell, cell2: &DynCell) -> Vec<u8> {
let mut result = Vec::new();
let mut encoder = ser::BocHeader::<ahash::RandomState>::with_root(cell1);
encoder.add_root(cell2);
encoder.encode(&mut result);
result
}
encode_pair_impl(cell1.as_ref(), cell2.as_ref())
}
pub fn decode_hex<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
fn decode_hex_impl(data: &[u8]) -> Result<Cell, de::Error> {
match hex::decode(data) {
Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(de::Error::UnknownBocTag),
}
}
decode_hex_impl(data.as_ref())
}
#[cfg(any(feature = "base64", test))]
#[inline]
pub fn decode_base64<T: AsRef<[u8]>>(data: T) -> Result<Cell, de::Error> {
fn decode_base64_impl(data: &[u8]) -> Result<Cell, de::Error> {
match crate::util::decode_base64(data) {
Ok(data) => Boc::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(de::Error::UnknownBocTag),
}
}
decode_base64_impl(data.as_ref())
}
#[inline]
pub fn decode<T>(data: T) -> Result<Cell, de::Error>
where
T: AsRef<[u8]>,
{
fn decode_impl(data: &[u8]) -> Result<Cell, de::Error> {
Boc::decode_ext(data, &mut Cell::empty_context())
}
decode_impl(data.as_ref())
}
#[inline]
pub fn decode_pair<T>(data: T) -> Result<(Cell, Cell), de::Error>
where
T: AsRef<[u8]>,
{
fn decode_pair_impl(data: &[u8]) -> Result<(Cell, Cell), de::Error> {
Boc::decode_pair_ext(data, &mut Cell::empty_context())
}
decode_pair_impl(data.as_ref())
}
pub fn decode_ext(data: &[u8], context: &mut dyn CellContext) -> Result<Cell, de::Error> {
use self::de::*;
let header = ok!(de::BocHeader::decode(
data,
&Options {
max_roots: Some(1),
min_roots: Some(1),
},
));
if let Some(&root) = header.roots().first() {
let cells = ok!(header.finalize(context));
if let Some(root) = cells.get(root) {
return Ok(root);
}
}
Err(de::Error::RootCellNotFound)
}
pub fn decode_pair_ext(
data: &[u8],
context: &mut dyn CellContext,
) -> Result<(Cell, Cell), de::Error> {
use self::de::*;
let header = ok!(de::BocHeader::decode(
data,
&Options {
max_roots: Some(2),
min_roots: Some(2),
},
));
let mut roots = header.roots().iter();
if let (Some(&root1), Some(&root2)) = (roots.next(), roots.next()) {
let cells = ok!(header.finalize(context));
if let (Some(root1), Some(root2)) = (cells.get(root1), cells.get(root2)) {
return Ok((root1, root2));
}
}
Err(de::Error::RootCellNotFound)
}
#[cfg(feature = "serde")]
pub fn serialize<T, S>(value: T, serializer: S) -> Result<S::Ok, S::Error>
where
SerdeBoc<T>: ::serde::Serialize,
S: ::serde::Serializer,
{
use ::serde::Serialize;
SerdeBoc::from(value).serialize(serializer)
}
#[cfg(feature = "serde")]
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
SerdeBoc<T>: ::serde::Deserialize<'de>,
D: ::serde::Deserializer<'de>,
{
use ::serde::Deserialize;
SerdeBoc::<T>::deserialize(deserializer).map(SerdeBoc::into_inner)
}
}
pub struct BocRepr;
impl BocRepr {
pub fn encode_hex<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
Ok(hex::encode(boc))
}
#[cfg(any(feature = "base64", test))]
pub fn encode_base64<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_ext(data, &mut Cell::empty_context()));
Ok(crate::util::encode_base64(boc))
}
#[cfg(feature = "rayon")]
pub fn encode_hex_rayon<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
Ok(hex::encode(boc))
}
#[cfg(all(any(feature = "base64", test), feature = "rayon"))]
pub fn encode_base64_rayon<T>(data: T) -> Result<String, crate::error::Error>
where
T: Store,
{
let boc = ok!(Self::encode_rayon_ext(data, &mut Cell::empty_context()));
Ok(crate::util::encode_base64(boc))
}
pub fn encode<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
where
T: Store,
{
Self::encode_ext(data, &mut Cell::empty_context())
}
#[cfg(feature = "rayon")]
pub fn encode_rayon<T>(data: T) -> Result<Vec<u8>, crate::error::Error>
where
T: Store,
{
Self::encode_rayon_ext(data, &mut Cell::empty_context())
}
#[inline]
pub fn decode_hex<T, D>(data: D) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
D: AsRef<[u8]>,
{
fn decode_hex_impl<T>(data: &[u8]) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
{
match hex::decode(data) {
Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
}
}
decode_hex_impl::<T>(data.as_ref())
}
#[cfg(any(feature = "base64", test))]
#[inline]
pub fn decode_base64<T, D>(data: D) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
D: AsRef<[u8]>,
{
fn decode_base64_impl<T>(data: &[u8]) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
{
match crate::util::decode_base64(data) {
Ok(data) => BocRepr::decode_ext(data.as_slice(), &mut Cell::empty_context()),
Err(_) => Err(BocReprError::InvalidBoc(de::Error::UnknownBocTag)),
}
}
decode_base64_impl::<T>(data.as_ref())
}
#[inline]
pub fn decode<T, D>(data: D) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
D: AsRef<[u8]>,
{
fn decode_impl<T>(data: &[u8]) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
{
BocRepr::decode_ext(data, &mut Cell::empty_context())
}
decode_impl::<T>(data.as_ref())
}
pub fn encode_ext<T>(
data: T,
context: &mut dyn CellContext,
) -> Result<Vec<u8>, crate::error::Error>
where
T: Store,
{
fn encode_ext_impl(
data: &dyn Store,
context: &mut dyn CellContext,
) -> Result<Vec<u8>, crate::error::Error> {
let mut builder = CellBuilder::new();
ok!(data.store_into(&mut builder, context));
let cell = ok!(builder.build_ext(context));
Ok(Boc::encode(cell))
}
encode_ext_impl(&data, context)
}
#[cfg(feature = "rayon")]
pub fn encode_rayon_ext<T>(
data: T,
context: &mut dyn CellContext,
) -> Result<Vec<u8>, crate::error::Error>
where
T: Store,
{
fn encode_ext_impl(
data: &dyn Store,
context: &mut dyn CellContext,
) -> Result<Vec<u8>, crate::error::Error> {
let mut builder = CellBuilder::new();
ok!(data.store_into(&mut builder, context));
let cell = ok!(builder.build_ext(context));
Ok(Boc::encode_rayon(cell))
}
encode_ext_impl(&data, context)
}
pub fn decode_ext<T>(data: &[u8], context: &mut dyn CellContext) -> Result<T, BocReprError>
where
for<'a> T: Load<'a>,
{
let cell = match Boc::decode_ext(data, context) {
Ok(cell) => cell,
Err(e) => return Err(BocReprError::InvalidBoc(e)),
};
match cell.as_ref().parse::<T>() {
Ok(data) => Ok(data),
Err(e) => Err(BocReprError::InvalidData(e)),
}
}
#[cfg(feature = "serde")]
pub fn serialize<S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer,
T: Store,
{
use ::serde::ser::{Error, Serialize};
let context = &mut Cell::empty_context();
let mut builder = CellBuilder::new();
if data.store_into(&mut builder, context).is_err() {
return Err(Error::custom("cell overflow"));
}
let cell = match builder.build_ext(context) {
Ok(cell) => cell,
Err(_) => return Err(Error::custom("failed to store into builder")),
};
cell.as_ref().serialize(serializer)
}
#[cfg(feature = "serde")]
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
D: ::serde::Deserializer<'de>,
for<'a> T: Load<'a>,
{
use ::serde::de::Error;
let cell = ok!(Boc::deserialize::<Cell, _>(deserializer));
match cell.as_ref().parse::<T>() {
Ok(data) => Ok(data),
Err(_) => Err(Error::custom("failed to decode object from cells")),
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum BocReprError {
#[error("invalid BOC")]
InvalidBoc(#[source] de::Error),
#[error("failed to decode object from cells")]
InvalidData(#[source] crate::error::Error),
}