use bytes::Bytes;
use crate::cdc::FastCdc;
use crate::chunk::Chunk;
use crate::config::ChunkConfig;
#[derive(Debug)]
pub struct Chunker {
cdc: FastCdc,
pending: Option<Bytes>,
offset: u64,
config: ChunkConfig,
}
impl Chunker {
pub fn new(config: ChunkConfig) -> Self {
#[cfg(feature = "keyed-cdc")]
let key = config.keyed_gear_table_key();
#[cfg(not(feature = "keyed-cdc"))]
let key = None;
Self {
cdc: FastCdc::with_key(
config.min_size(),
config.avg_size(),
config.max_size(),
config.normalization_level(),
key,
),
pending: None,
offset: 0,
config,
}
}
fn compute_hash(&self, _data: &[u8]) -> Option<crate::chunk::ChunkHash> {
if !self.config.hash_config().enabled {
return None;
}
#[cfg(feature = "hash-blake3")]
return Some(crate::hash::Blake3Hasher::hash(_data));
#[cfg(not(feature = "hash-blake3"))]
return None;
}
fn create_chunk(&self, data: Bytes, offset: u64) -> Chunk {
let hash = self.compute_hash(data.as_ref());
Chunk {
data,
offset: Some(offset),
hash,
}
}
pub fn push(&mut self, data: Bytes) -> (Vec<Chunk>, Bytes) {
let mut chunks = Vec::new();
let mut new_chunk_start = 0;
for (i, &byte) in data.iter().enumerate() {
if self.cdc.update(byte) {
let chunk_data = if let Some(ref pending) = self.pending {
crate::util::combine_bytes(pending, &data[new_chunk_start..=i])
} else {
data.slice(new_chunk_start..=i)
};
let chunk_offset = self.offset;
chunks.push(self.create_chunk(chunk_data, chunk_offset));
let chunk_len =
self.pending.as_ref().map_or(0, |p| p.len()) + (i + 1 - new_chunk_start);
self.offset += chunk_len as u64;
new_chunk_start = i + 1;
self.pending = None;
}
}
if new_chunk_start < data.len() {
let remaining = data.slice(new_chunk_start..);
if let Some(pending) = self.pending.take() {
self.pending = Some(crate::util::combine_bytes(&pending, remaining.as_ref()));
} else {
self.pending = Some(remaining);
}
}
(chunks, self.pending.clone().unwrap_or_default())
}
pub fn finish(&mut self) -> Option<Chunk> {
if let Some(pending) = self.pending.take() {
if pending.is_empty() {
return None;
}
let chunk_offset = self.offset;
let chunk = self.create_chunk(pending, chunk_offset);
self.offset += chunk.len() as u64;
Some(chunk)
} else {
None
}
}
pub fn reset(&mut self) {
self.cdc.reset();
self.pending = None;
self.offset = 0;
}
pub fn offset(&self) -> u64 {
self.offset
}
pub fn pending_len(&self) -> usize {
self.pending.as_ref().map(|b| b.len()).unwrap_or(0)
}
pub fn config(&self) -> &ChunkConfig {
&self.config
}
}
impl Default for Chunker {
fn default() -> Self {
Self::new(ChunkConfig::default())
}
}