use crate::cid::{Cid, CidBuilder, HashAlgorithm};
use crate::error::{Error, Result};
use crate::types::BlockSize;
use bytes::Bytes;
use serde::{Deserialize, Serialize};
pub const MAX_BLOCK_SIZE: usize = 2 * 1024 * 1024;
pub const MIN_BLOCK_SIZE: usize = 1;
#[derive(Debug, Clone)]
pub struct Block {
cid: Cid,
data: Bytes,
}
impl Block {
pub fn new(data: Bytes) -> Result<Self> {
Self::validate_size(data.len())?;
let cid = CidBuilder::new().build(&data)?;
Ok(Self { cid, data })
}
fn validate_size(size: usize) -> Result<()> {
if size < MIN_BLOCK_SIZE {
return Err(Error::InvalidData(format!(
"Block size {} is below minimum {}",
size, MIN_BLOCK_SIZE
)));
}
if size > MAX_BLOCK_SIZE {
return Err(Error::InvalidData(format!(
"Block size {} exceeds maximum {} (2 MiB)",
size, MAX_BLOCK_SIZE
)));
}
Ok(())
}
pub fn from_parts(cid: Cid, data: Bytes) -> Self {
Self { cid, data }
}
#[inline]
pub fn cid(&self) -> &Cid {
&self.cid
}
#[inline]
pub fn data(&self) -> &Bytes {
&self.data
}
#[inline]
pub fn size(&self) -> BlockSize {
self.data.len() as BlockSize
}
pub fn verify(&self) -> Result<bool> {
let computed_cid = CidBuilder::new().build(&self.data)?;
Ok(computed_cid == self.cid)
}
#[inline]
pub fn into_parts(self) -> (Cid, Bytes) {
(self.cid, self.data)
}
pub fn slice(&self, range: impl std::ops::RangeBounds<usize>) -> Bytes {
use std::ops::Bound;
let len = self.data.len();
let start = match range.start_bound() {
Bound::Included(&n) => n,
Bound::Excluded(&n) => n + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(&n) => n + 1,
Bound::Excluded(&n) => n,
Bound::Unbounded => len,
};
self.data.slice(start..end)
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.data
}
#[inline]
pub fn clone_data(&self) -> Bytes {
self.data.clone()
}
#[inline]
#[must_use]
pub fn shares_data(&self, other: &Block) -> bool {
self.data.as_ptr() == other.data.as_ptr()
}
pub fn metadata(&self) -> BlockMetadata {
BlockMetadata::new(self.cid, self.size())
}
pub fn builder() -> BlockBuilder {
BlockBuilder::new()
}
#[inline]
pub fn len(&self) -> usize {
self.data.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
pub fn from_vec(vec: Vec<u8>) -> Result<Self> {
Self::new(Bytes::from(vec))
}
pub fn from_slice(slice: &[u8]) -> Result<Self> {
Self::new(Bytes::copy_from_slice(slice))
}
pub fn from_static(slice: &'static [u8]) -> Result<Self> {
Self::new(Bytes::from_static(slice))
}
}
#[derive(Debug, Clone)]
pub struct BlockBuilder {
cid_builder: CidBuilder,
}
impl Default for BlockBuilder {
fn default() -> Self {
Self {
cid_builder: CidBuilder::new(),
}
}
}
impl BlockBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
self.cid_builder = self.cid_builder.hash_algorithm(algorithm);
self
}
pub fn cid_version(mut self, version: cid::Version) -> Self {
self.cid_builder = self.cid_builder.version(version);
self
}
pub fn codec(mut self, codec: u64) -> Self {
self.cid_builder = self.cid_builder.codec(codec);
self
}
pub fn build(self, data: Bytes) -> Result<Block> {
Block::validate_size(data.len())?;
let cid = self.cid_builder.build(&data)?;
Ok(Block { cid, data })
}
pub fn build_from_slice(self, data: &[u8]) -> Result<Block> {
self.build(Bytes::copy_from_slice(data))
}
}
impl From<&Block> for Cid {
fn from(block: &Block) -> Self {
block.cid
}
}
impl AsRef<[u8]> for Block {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl std::ops::Deref for Block {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.as_bytes()
}
}
impl TryFrom<Vec<u8>> for Block {
type Error = Error;
fn try_from(vec: Vec<u8>) -> Result<Self> {
Block::from_vec(vec)
}
}
impl TryFrom<&[u8]> for Block {
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self> {
Block::from_slice(slice)
}
}
impl TryFrom<Bytes> for Block {
type Error = Error;
fn try_from(bytes: Bytes) -> Result<Self> {
Block::new(bytes)
}
}
impl PartialEq for Block {
fn eq(&self, other: &Self) -> bool {
self.cid == other.cid
}
}
impl Eq for Block {}
impl std::hash::Hash for Block {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.cid.to_bytes().hash(state);
}
}
impl PartialOrd for Block {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Block {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.cid.to_bytes().cmp(&other.cid.to_bytes())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockMetadata {
pub cid: crate::cid::SerializableCid,
pub size: BlockSize,
pub links: usize,
}
impl BlockMetadata {
pub fn new(cid: Cid, size: BlockSize) -> Self {
Self {
cid: crate::cid::SerializableCid(cid),
size,
links: 0,
}
}
pub fn with_links(mut self, links: usize) -> Self {
self.links = links;
self
}
}