#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
extern crate rand;
use rand::prelude::*;
use std::f64::consts;
use std::mem;
const SIZEOF_U64: usize = mem::size_of::<u64>();
#[derive(Debug)]
pub struct Chunker<'a> {
slice: &'a [u8],
window_size: usize,
max_chunksize: usize,
min_chunksize: usize,
salt: u64,
bytes_processed: usize,
bytes_remaining: usize,
}
#[derive(Debug)]
pub enum ChunkerError {
InsufficientMaxSize,
InsufficientTargetSize,
}
impl<'a> Chunker<'a> {
pub fn with_params(
slice: &[u8],
target_chunksize_bytes: usize,
max_chunksize_bytes: usize,
salt: u64,
) -> Result<Chunker, ChunkerError> {
if 2 * target_chunksize_bytes > max_chunksize_bytes {
return Err(ChunkerError::InsufficientMaxSize);
}
if target_chunksize_bytes < 64 {
return Err(ChunkerError::InsufficientTargetSize);
}
let target_window_size = (target_chunksize_bytes as f64 / (consts::E - 1.0)) as usize;
let my_window_size = (target_window_size as f64 * 0.56) as usize;
let min_chunksize = target_chunksize_bytes - target_window_size;
let chunker: Chunker = Chunker {
slice,
window_size: my_window_size,
salt,
max_chunksize: max_chunksize_bytes,
min_chunksize,
bytes_processed: 0,
bytes_remaining: slice.len(),
};
Ok(chunker)
}
pub fn get_random_salt() -> u64 {
let mut rng = rand::thread_rng();
rng.next_u64()
}
}
impl<'a> Iterator for Chunker<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<&'a [u8]> {
if self.bytes_remaining == 0 {
return None;
}
let next_slice = next_chunked_slice(
&self.slice[(self.bytes_processed)..],
self.window_size,
self.min_chunksize,
self.max_chunksize,
self.salt,
);
self.bytes_processed += next_slice.len();
self.bytes_remaining -= next_slice.len();
Some(next_slice)
}
}
fn next_chunked_slice(
remaining: &[u8],
window_size: usize,
min_chunksize: usize,
max_chunksize: usize,
salt: u64,
) -> &[u8] {
let remaining_bytes_length = remaining.len();
if remaining_bytes_length <= min_chunksize + window_size {
return &remaining[..remaining_bytes_length];
}
let mut marker_position = 0;
let end_index = remaining_bytes_length - SIZEOF_U64;
for i in min_chunksize..end_index {
if i == max_chunksize {
return &remaining[..i];
}
let current_as_u64 = &remaining[i] as *const u8 as *const u64;
let marker_as_u64 = &remaining[marker_position] as *const u8 as *const u64;
if !swapped_salted_isgt(current_as_u64, marker_as_u64, salt) {
marker_position = i;
continue;
}
if i == marker_position + window_size {
return &remaining[..i];
}
}
let cutpoint = if max_chunksize < remaining_bytes_length {
max_chunksize
} else {
remaining_bytes_length
};
&remaining[..cutpoint]
}
#[inline]
fn swapped_salted_isgt(first: *const u64, second: *const u64, salt: u64) -> bool {
let compare_first = unsafe { (*first).swap_bytes() } ^ salt;
let compare_second = unsafe { (*second).swap_bytes() } ^ salt;
if compare_first > compare_second {
return true;
}
false
}