const USE_FASTPATH_CHUNKS_FIRST: bool = true;
const USE_FASTPATH_CHUNKS_LAST: bool = USE_FASTPATH_CHUNKS_FIRST;
const USE_ALIGN_CHUNKS_TEST: bool = true;
const BCHUNK_HASH_TABLE_ACCUMULATE_STEPS: usize = 4;
const HASH_TABLE_KEY_UNSET: u64 = ::std::u64::MAX;
const HASH_TABLE_KEY_FALLBACK: u64 = ::std::u64::MAX - 1;
const BCHUNK_HASH_TABLE_MUL: usize = 3;
const USE_MERGE_CHUNKS: bool = true;
const BCHUNK_SIZE_MIN_DIV: usize = 8;
const BCHUNK_SIZE_MAX_MUL: usize = 2;
const USE_VALIDATE_LIST_SIZE: bool = false;
const USE_VALIDATE_LIST_DATA_PARTIAL: bool = false;
const USE_PARANOID_CHECKS: bool = false;
const MEMPOOL_CHUNK_SIZE: usize = 512;
mod plain_ptr;
use plain_ptr::{
PtrMut,
PtrConst,
null_mut,
null_const,
};
mod mempool_elem;
use mempool_elem::{
MemPool,
MemPoolElemUtils,
};
mod list_base;
use list_base::{
ListBase,
ListBaseElemUtils,
};
use ::std::cmp::{
min,
max,
};
macro_rules! unlikely {
($body:expr) => {
$body
}
}
type HashKey = u64;
struct BArrayInfo {
chunk_stride: usize,
chunk_byte_size: usize,
chunk_byte_size_min: usize,
chunk_byte_size_max: usize,
accum_read_ahead_bytes: usize,
accum_steps: usize,
accum_read_ahead_len: usize,
}
struct BArrayMemory {
state: MemPool<BArrayState>,
chunk_list: MemPool<BChunkList>,
chunk_ref: MemPool<BChunkRef>,
chunk: MemPool<BChunk>,
}
pub struct BArrayStore {
info: BArrayInfo,
memory: BArrayMemory,
states: ListBase<BArrayState>,
}
pub struct BArrayState {
next: PtrMut<BArrayState>,
prev: PtrMut<BArrayState>,
chunk_list: PtrMut<BChunkList>,
}
struct BChunkList {
chunk_refs: ListBase<BChunkRef>,
chunk_refs_len: usize,
total_size: usize,
users: isize,
}
struct BChunk {
data: Vec<u8>,
users: isize,
key: HashKey,
}
struct BChunkRef {
next: PtrMut<BChunkRef>,
prev: PtrMut<BChunkRef>,
link: PtrMut<BChunk>,
}
struct BTableRef {
next: PtrMut<BTableRef>,
cref: PtrMut<BChunkRef>,
}
macro_rules! mempool_list_elem_impl {
($t:ty) => {
impl MemPoolElemUtils for $t {
#[inline] fn default_chunk_size() -> usize {
MEMPOOL_CHUNK_SIZE
}
#[inline] fn free_ptr_get(&self) -> *mut Self {
return self.next.as_ptr() as usize as *mut Self;
}
#[inline] fn free_ptr_set(&mut self, ptr: *mut Self) {
self.next = ::plain_ptr::PtrMut(ptr as usize as *mut _);
self.prev = PtrMut(self);
}
#[inline] fn free_ptr_test(&self) -> bool {
self.prev == PtrConst(self)
}
}
}
}
mempool_list_elem_impl!(BArrayState);
mempool_list_elem_impl!(BChunkRef);
impl MemPoolElemUtils for BChunkList {
#[inline] fn default_chunk_size() -> usize {
MEMPOOL_CHUNK_SIZE
}
#[inline] fn free_ptr_get(&self) -> *mut Self {
return self.chunk_refs.head.as_ptr() as usize as *mut Self;
}
#[inline] fn free_ptr_set(&mut self, ptr: *mut Self) {
self.chunk_refs.head = PtrMut(ptr as usize as *mut _);
self.chunk_refs.tail = PtrMut((self as *const _) as usize as *mut _);
}
#[inline] fn free_ptr_test(&self) -> bool {
self.chunk_refs.tail.as_ptr() as usize == (self as *const _ as usize)
}
}
impl MemPoolElemUtils for BChunk {
#[inline] fn default_chunk_size() -> usize {
MEMPOOL_CHUNK_SIZE
}
#[inline] fn free_ptr_get(&self) -> *mut Self {
return self.users as *mut Self;
}
#[inline] fn free_ptr_set(&mut self, ptr: *mut Self) {
self.users = ptr as isize;
self.key = self as *const _ as HashKey;
}
#[inline] fn free_ptr_test(&self) -> bool {
self.key == self as *const _ as HashKey
}
}
macro_rules! list_base_elem_impl {
($t:ty) => {
impl ListBaseElemUtils for $t {
#[inline] fn next_get(&self) -> PtrMut<Self> { self.next }
#[inline] fn prev_get(&self) -> PtrMut<Self> { self.prev }
#[inline] fn next_set(&mut self, ptr: PtrMut<Self>) { self.next = ptr; }
#[inline] fn prev_set(&mut self, ptr: PtrMut<Self>) { self.prev = ptr; }
}
}
}
list_base_elem_impl!(BArrayState);
list_base_elem_impl!(BChunkRef);
fn bchunk_new(
bs_mem: &mut BArrayMemory, data: Vec<u8>,
) -> PtrMut<BChunk> {
PtrMut(bs_mem.chunk.alloc_elem_from(
BChunk {
data: data,
users: 0,
key: HASH_TABLE_KEY_UNSET,
}
))
}
fn bchunk_new_copydata(
bs_mem: &mut BArrayMemory, data: &[u8],
) -> PtrMut<BChunk> {
let mut data_copy = Vec::with_capacity(data.len());
data_copy.extend_from_slice(data);
return bchunk_new(bs_mem, data_copy);
}
fn bchunk_decref(
bs_mem: &mut BArrayMemory, mut chunk: PtrMut<BChunk>,
) {
debug_assert!(chunk.users > 0);
if chunk.users == 1 {
unsafe { ::std::ptr::drop_in_place(&mut chunk.data) };
bs_mem.chunk.free_elem(chunk.as_ptr());
} else {
chunk.users -= 1;
}
}
fn bchunk_data_compare(
chunk: PtrMut<BChunk>,
data_base: &[u8],
data_base_len: usize,
offset: usize,
) -> bool {
if offset + chunk.data.len() <= data_base_len {
return &data_base[offset..(offset + chunk.data.len())] == &chunk.data[..];
} else {
return false;
}
}
fn bchunk_list_new(
bs_mem: &mut BArrayMemory,
total_size: usize,
) -> PtrMut<BChunkList> {
PtrMut(bs_mem.chunk_list.alloc_elem_from(
BChunkList {
chunk_refs: ListBase::new(),
chunk_refs_len: 0,
total_size: total_size,
users: 0,
}
))
}
fn bchunk_list_decref(
bs_mem: &mut BArrayMemory, mut chunk_list: PtrMut<BChunkList>,
) {
debug_assert!(chunk_list.users > 0);
if chunk_list.users == 1 {
let mut cref = chunk_list.chunk_refs.head;
while cref != null_mut() {
let cref_next = cref.next;
bchunk_decref(bs_mem, cref.link);
bs_mem.chunk_ref.free_elem(cref.as_ptr());
cref = cref_next;
}
bs_mem.chunk_list.free_elem(chunk_list.as_ptr());
} else {
chunk_list.users -= 1;
}
}
macro_rules! debug_assert_chunklist_size {
($chunk_list:expr, $n:expr) => {
{
if USE_VALIDATE_LIST_SIZE {
debug_assert_eq!(bchunk_list_size($chunk_list), $n)
}
}
}
}
fn bchunk_list_data_check(
chunk_list: PtrMut<BChunkList>, data: &[u8],
) -> bool {
let mut offset = 0;
for cref in chunk_list.chunk_refs.iter() {
if &data[offset..(offset + cref.link.data.len())] != &cref.link.data[..] {
return false;
}
offset += cref.link.data.len();
}
return true;
}
macro_rules! debug_assert_chunklist_data {
($chunk_list:expr, $data:expr) => {
{
if USE_VALIDATE_LIST_DATA_PARTIAL {
debug_assert!(bchunk_list_data_check($chunk_list, $data));
}
}
}
}
fn bchunk_list_ensure_min_size_last(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
mut chunk_list: PtrMut<BChunkList>,
) {
let mut cref = chunk_list.chunk_refs.tail;
if cref != null_mut() && cref.prev != null_mut() {
let chunk_curr: PtrMut<BChunk> = cref.link;
let chunk_prev: PtrMut<BChunk> = cref.prev.link;
if min(chunk_prev.data.len(), chunk_curr.data.len()) < info.chunk_byte_size_min {
let data_merge_len = chunk_prev.data.len() + chunk_curr.data.len();
if data_merge_len <= info.chunk_byte_size_max {
debug_assert!(chunk_list.chunk_refs.tail != chunk_list.chunk_refs.head);
cref.prev.next = null_mut();
chunk_list.chunk_refs.tail = cref.prev;
chunk_list.chunk_refs_len -= 1;
let mut data_merge: Vec<u8> = Vec::with_capacity(data_merge_len);
data_merge.extend_from_slice(&chunk_prev.data[..]);
data_merge.extend_from_slice(&chunk_curr.data[..]);
cref.prev.link = bchunk_new(bs_mem, data_merge);
cref.prev.link.users += 1;
bs_mem.chunk_ref.free_elem(cref.as_ptr());
} else {
let split = info.chunk_byte_size;
let data_prev_len = split;
let data_curr_len = data_merge_len - split;
let mut data_prev: Vec<u8> = Vec::with_capacity(data_prev_len);
let mut data_curr: Vec<u8> = Vec::with_capacity(data_curr_len);
if data_prev_len <= chunk_prev.data.len() {
data_prev.extend_from_slice(&chunk_prev.data[..]);
data_curr.extend_from_slice(
&chunk_prev.data[data_prev_len..chunk_prev.data.len()]);
data_curr.extend_from_slice(
&chunk_curr.data[..]);
} else {
debug_assert!(data_curr_len <= chunk_curr.data.len());
debug_assert!(data_prev_len >= chunk_prev.data.len());
let data_prev_grow_len = data_prev_len - chunk_prev.data.len();
data_prev.extend_from_slice(&chunk_prev.data[..]);
data_prev.extend_from_slice(&chunk_curr.data[0..data_prev_grow_len]);
data_curr.extend_from_slice(
&chunk_curr.data[data_prev_grow_len..(data_prev_grow_len + data_curr_len)]);
}
debug_assert_eq!(data_prev_len, data_prev.len());
debug_assert_eq!(data_curr_len, data_curr.len());
cref.prev.link = bchunk_new(bs_mem, data_prev);
cref.prev.link.users += 1;
cref.link = bchunk_new(bs_mem, data_curr);
cref.link.users += 1;
}
bchunk_decref(bs_mem, chunk_curr);
bchunk_decref(bs_mem, chunk_prev);
}
}
}
fn bchunk_list_calc_trim_len(
info: &BArrayInfo, data_len: usize,
) -> (usize, usize) {
let mut data_last_chunk_len: usize;
let mut data_trim_len: usize = data_len;
if USE_MERGE_CHUNKS {
if data_len > info.chunk_byte_size {
data_last_chunk_len = data_trim_len % info.chunk_byte_size;
data_trim_len = data_trim_len - data_last_chunk_len;
if data_last_chunk_len != 0 {
if data_last_chunk_len < info.chunk_byte_size_min {
data_trim_len -= info.chunk_byte_size;
data_last_chunk_len += info.chunk_byte_size;
}
}
} else {
data_trim_len = 0;
data_last_chunk_len = data_len;
}
debug_assert!((data_trim_len == 0) || (data_trim_len >= info.chunk_byte_size));
} else {
data_last_chunk_len = data_trim_len % info.chunk_byte_size;
data_trim_len = data_trim_len - data_last_chunk_len;
}
debug_assert_eq!(data_trim_len + data_last_chunk_len, data_len);
(data_trim_len, data_last_chunk_len)
}
fn bchunk_list_append_only(
bs_mem: &mut BArrayMemory,
mut chunk_list: PtrMut<BChunkList>, mut chunk: PtrMut<BChunk>,
) {
let cref = PtrMut(bs_mem.chunk_ref.alloc_elem_from(
BChunkRef {
next: null_mut(),
prev: null_mut(),
link: chunk,
})
);
chunk_list.chunk_refs.push_back(cref);
chunk_list.chunk_refs_len += 1;
chunk.users += 1
}
fn bchunk_list_append_data(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
chunk_list: PtrMut<BChunkList>,
data: &[u8],
) {
debug_assert!(data.len() != 0);
if USE_MERGE_CHUNKS {
debug_assert!(data.len() <= info.chunk_byte_size_max);
if !chunk_list.chunk_refs.is_empty() {
let mut cref: PtrMut<BChunkRef> = chunk_list.chunk_refs.tail;
let chunk_prev: PtrMut<BChunk> = cref.link;
if min(chunk_prev.data.len(), data.len()) < info.chunk_byte_size_min {
let data_merge_len = chunk_prev.data.len() + data.len();
if cref.link.users == 1 {
cref.link.data.extend_from_slice(data);
} else {
let mut data_merge: Vec<u8> = Vec::with_capacity(data_merge_len);
data_merge.extend_from_slice(&chunk_prev.data[..]);
data_merge.extend_from_slice(data);
cref.link = bchunk_new(bs_mem, data_merge);
cref.link.users += 1;
bchunk_decref(bs_mem, chunk_prev);
}
debug_assert_eq!(data_merge_len, cref.link.data.len());
return;
}
}
}
let chunk: PtrMut<BChunk> = bchunk_new_copydata(bs_mem, data);
bchunk_list_append_only(bs_mem, chunk_list, chunk);
if false && USE_MERGE_CHUNKS {
bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list);
}
}
fn bchunk_list_append_data_n(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
chunk_list: PtrMut<BChunkList>,
data: &[u8],
) {
let (data_trim_len, data_last_chunk_len) = bchunk_list_calc_trim_len(info, data.len());
if data_trim_len != 0 {
let mut i_prev;
{
let i = info.chunk_byte_size;
bchunk_list_append_data(info, bs_mem, chunk_list, &data[0..i]);
i_prev = i;
}
while i_prev != data_trim_len {
let i = i_prev + info.chunk_byte_size;
let chunk = bchunk_new_copydata(bs_mem, &data[i_prev..i]);
bchunk_list_append_only(bs_mem, chunk_list, chunk);
i_prev = i;
}
if data_last_chunk_len != 0 {
let chunk = bchunk_new_copydata(
bs_mem, &data[i_prev..(i_prev + data_last_chunk_len)]);
bchunk_list_append_only(bs_mem, chunk_list, chunk);
}
} else {
if data_last_chunk_len != 0 {
debug_assert_eq!(data.len(), data_last_chunk_len);
bchunk_list_append_data(info, bs_mem, chunk_list, data);
}
}
if USE_MERGE_CHUNKS {
if data.len() > info.chunk_byte_size {
debug_assert!(chunk_list.chunk_refs.tail.link.data.len() >= info.chunk_byte_size_min);
}
}
}
fn bchunk_list_append(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
chunk_list: PtrMut<BChunkList>,
chunk: PtrMut<BChunk>,
) {
bchunk_list_append_only(bs_mem, chunk_list, chunk);
if USE_MERGE_CHUNKS {
bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list);
}
}
fn bchunk_list_fill_from_array(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
chunk_list: PtrMut<BChunkList>,
data: &[u8],
) {
debug_assert!(chunk_list.chunk_refs.is_empty());
let (data_trim_len, data_last_chunk_len) = bchunk_list_calc_trim_len(info, data.len());
let mut i_prev = 0;
while i_prev != data_trim_len {
let i = i_prev + info.chunk_byte_size;
let chunk = bchunk_new_copydata(bs_mem, &data[i_prev..i]);
bchunk_list_append_only(bs_mem, chunk_list, chunk);
i_prev = i;
}
if data_last_chunk_len != 0 {
let chunk = bchunk_new_copydata(bs_mem, &data[i_prev..(i_prev + data_last_chunk_len)]);
bchunk_list_append_only(bs_mem, chunk_list, chunk);
}
if USE_MERGE_CHUNKS {
if data.len() > info.chunk_byte_size {
debug_assert!(chunk_list.chunk_refs.tail.link.data.len() >= info.chunk_byte_size_min);
}
}
if false && USE_MERGE_CHUNKS {
bchunk_list_ensure_min_size_last(info, bs_mem, chunk_list);
}
debug_assert_chunklist_size!(chunk_list, data.len());
debug_assert_chunklist_data!(chunk_list, data);
}
const HASH_INIT: u32 = 5381;
#[inline]
fn hash_data_single(p: u8) -> u32 {
return ((HASH_INIT << 5) + HASH_INIT).wrapping_add((p as i8) as u32);
}
fn hash_data(key: &[u8]) -> u32 {
let mut h: u32 = HASH_INIT;
for p in key {
h = h.wrapping_shl(5).wrapping_add(h).wrapping_add((*p as i8) as u32);
}
return h;
}
fn hash_array_from_data(
info: &BArrayInfo, data_slice: &[u8],
hash_array: &mut [HashKey],
) {
if info.chunk_stride != 1 {
let mut i_step = 0;
let mut i = 0;
while i_step != data_slice.len() {
let i_next = i_step + info.chunk_stride;
hash_array[i] = hash_data(&data_slice[i_step..i_next]) as HashKey;
i_step = i_next;
i += 1;
}
} else {
for i in 0..data_slice.len() {
hash_array[i] = hash_data_single(data_slice[i]) as HashKey;
}
}
}
fn hash_array_from_cref(
info: &BArrayInfo, mut cref: PtrMut<BChunkRef>, data_len: usize,
hash_array: &mut [HashKey],
) {
let hash_array_len = data_len / info.chunk_stride;
let mut i: usize = 0;
loop {
let mut i_next: usize = hash_array_len - i;
let mut data_trim_len = i_next * info.chunk_stride;
if data_trim_len > cref.link.data.len() {
data_trim_len = cref.link.data.len();
i_next = data_trim_len / info.chunk_stride;
}
debug_assert!(data_trim_len <= cref.link.data.len());
hash_array_from_data(
info, &cref.link.data[0..data_trim_len], &mut hash_array[i..(i + i_next)]);
i += i_next;
cref = cref.next;
if !((i < hash_array_len) && (cref != null_const())) {
break;
}
}
debug_assert!(i == hash_array_len);
}
fn hash_accum(hash_array: &mut [HashKey], hash_array_len: usize, mut iter_steps: usize) {
if unlikely!(iter_steps > hash_array_len) {
iter_steps = hash_array_len;
}
let hash_array_search_len: usize = hash_array_len - iter_steps;
while iter_steps != 0 {
let hash_offset: usize = iter_steps;
for i in 0..hash_array_search_len {
hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1);
}
iter_steps -= 1;
}
}
fn hash_accum_single(hash_array: &mut [HashKey], mut iter_steps: usize) {
debug_assert!(iter_steps <= hash_array.len());
if unlikely!(!(iter_steps <= hash_array.len())) {
iter_steps = hash_array.len();
}
let mut iter_steps_sub = iter_steps;
while iter_steps != 0 {
let hash_array_search_len: usize = hash_array.len() - iter_steps_sub;
let hash_offset: usize = iter_steps;
for i in 0..hash_array_search_len {
hash_array[i] += (hash_array[i + hash_offset]) * ((hash_array[i] & 0xff) + 1);
}
iter_steps -= 1;
iter_steps_sub += iter_steps;
}
}
fn key_from_chunk_ref(
info: &BArrayInfo, cref: PtrMut<BChunkRef>,
hash_store: &mut [HashKey],
) -> HashKey {
let mut chunk: PtrMut<BChunk> = cref.link;
debug_assert_ne!(0, (info.accum_read_ahead_bytes * info.chunk_stride));
if info.accum_read_ahead_bytes <= chunk.data.len() {
let mut key: HashKey = chunk.key;
if key != HASH_TABLE_KEY_UNSET {
} else {
hash_array_from_cref(info, cref, info.accum_read_ahead_bytes, hash_store);
hash_accum_single(hash_store, info.accum_steps);
key = hash_store[0];
if key == HASH_TABLE_KEY_UNSET {
key = HASH_TABLE_KEY_FALLBACK;
}
chunk.key = key;
}
return key;
} else {
hash_array_from_cref(info, cref, info.accum_read_ahead_bytes, hash_store);
hash_accum_single(hash_store, info.accum_steps);
let mut key: HashKey = hash_store[0];
if unlikely!(key == HASH_TABLE_KEY_UNSET) {
key = HASH_TABLE_KEY_FALLBACK;
}
return key;
}
}
fn table_lookup(
info: &BArrayInfo, table: &Vec<PtrMut<BTableRef>>, table_len: usize, i_table_start: usize,
data: &[u8], data_len: usize, offset: usize, table_hash_array: &Vec<HashKey>,
) -> PtrMut<BChunkRef> {
let size_left: usize = data_len - offset;
let key: HashKey = table_hash_array[((offset - i_table_start) / info.chunk_stride)];
let key_index = (key % (table_len as HashKey)) as usize;
let mut tref: PtrMut<BTableRef> = table[key_index];
while tref != null_const() {
let cref: PtrMut<BChunkRef> = tref.cref;
if cref.link.key == key {
let chunk_test: PtrMut<BChunk> = cref.link;
if chunk_test.data.len() <= size_left {
if bchunk_data_compare(chunk_test, data, data_len, offset) {
return cref;
}
}
}
tref = tref.next;
}
null_mut()
}
fn bchunk_list_from_data_merge(
info: &BArrayInfo, bs_mem: &mut BArrayMemory,
data: &[u8], data_len_original: usize,
chunk_list_reference: PtrMut<BChunkList>,
) -> PtrMut<BChunkList> {
debug_assert_chunklist_size!(chunk_list_reference, chunk_list_reference.total_size);
let mut cref_match_first: PtrMut<BChunkRef> = null_mut();
let mut chunk_list_reference_skip_len: usize = 0;
let mut chunk_list_reference_skip_bytes: usize = 0;
let mut i_prev = 0;
if USE_FASTPATH_CHUNKS_FIRST {
let mut full_match: bool = true;
let mut cref: PtrMut<BChunkRef> = chunk_list_reference.chunk_refs.head;
while i_prev < data_len_original {
if cref != null_mut() &&
bchunk_data_compare(cref.link, data, data_len_original, i_prev)
{
cref_match_first = cref;
chunk_list_reference_skip_len += 1;
chunk_list_reference_skip_bytes += cref.link.data.len();
i_prev += cref.link.data.len();
cref = cref.next;
} else {
full_match = false;
break;
}
}
if full_match {
if chunk_list_reference.total_size == data_len_original {
return chunk_list_reference;
}
}
}
let chunk_list: PtrMut<BChunkList> = bchunk_list_new(bs_mem, data_len_original);
if cref_match_first != null_const() {
let mut chunk_size_step: usize = 0;
let mut cref: PtrMut<BChunkRef> = chunk_list_reference.chunk_refs.head;
loop {
let chunk: PtrMut<BChunk> = cref.link;
chunk_size_step += chunk.data.len();
bchunk_list_append_only(bs_mem, chunk_list, chunk);
debug_assert_chunklist_size!(chunk_list, chunk_size_step);
debug_assert_chunklist_data!(chunk_list, data);
if cref == cref_match_first {
break;
} else {
cref = cref.next;
}
}
if chunk_size_step == data_len_original {
return chunk_list;
}
i_prev = chunk_size_step;
} else {
i_prev = 0;
}
let mut data_len: usize = data_len_original;
let mut chunk_list_reference_last: PtrMut<BChunkRef> = null_mut();
if USE_FASTPATH_CHUNKS_LAST {
if !chunk_list_reference.chunk_refs.is_empty() {
let mut cref: PtrMut<BChunkRef> = chunk_list_reference.chunk_refs.tail;
while
(cref.prev != null_mut()) &&
(cref != cref_match_first) &&
(cref.link.data.len() <= data_len - i_prev)
{
let chunk_test: PtrMut<BChunk> = cref.link;
let offset: usize = data_len - chunk_test.data.len();
if bchunk_data_compare(chunk_test, data, data_len, offset) {
data_len = offset;
chunk_list_reference_last = cref;
chunk_list_reference_skip_len += 1;
chunk_list_reference_skip_bytes += cref.link.data.len();
cref = cref.prev;
} else {
break;
}
}
}
}
let mut use_aligned: bool = false;
if USE_ALIGN_CHUNKS_TEST {
if chunk_list.total_size == chunk_list_reference.total_size {
if data_len - i_prev <= chunk_list.total_size / 4 {
use_aligned = true;
} else {
}
}
}
if use_aligned {
let mut cref: PtrMut<BChunkRef> = {
if cref_match_first != null_mut() {
cref_match_first.next
} else {
chunk_list_reference.chunk_refs.head
}
};
while i_prev != data_len {
let i: usize = i_prev + cref.link.data.len();
debug_assert!(i != i_prev);
if (cref != chunk_list_reference_last) &&
bchunk_data_compare(cref.link, data, data_len, i_prev)
{
bchunk_list_append(info, bs_mem, chunk_list, cref.link);
debug_assert_chunklist_size!(chunk_list, i);
debug_assert_chunklist_data!(chunk_list, data);
} else {
bchunk_list_append_data(info, bs_mem, chunk_list, &data[i_prev..i]);
debug_assert_chunklist_size!(chunk_list, i);
debug_assert_chunklist_data!(chunk_list, data);
}
cref = cref.next;
i_prev = i;
}
} else if
(data_len - i_prev >= info.chunk_byte_size) &&
(chunk_list_reference.chunk_refs_len >= chunk_list_reference_skip_len) &&
(chunk_list_reference.chunk_refs.head != null_mut())
{
let i_table_start = i_prev;
let table_hash_array_len: usize = (data_len - i_prev) / info.chunk_stride;
let mut table_hash_array: Vec<HashKey> = Vec::with_capacity(table_hash_array_len);
unsafe { table_hash_array.set_len(table_hash_array_len) };
hash_array_from_data(info, &data[i_prev..data_len], &mut table_hash_array[..]);
hash_accum(&mut table_hash_array[..], table_hash_array_len, info.accum_steps);
let chunk_list_reference_remaining_len: usize =
(chunk_list_reference.chunk_refs_len - chunk_list_reference_skip_len) + 1;
let mut table_ref_stack: Vec<BTableRef> =
Vec::with_capacity(chunk_list_reference_remaining_len);
let table_len = chunk_list_reference_remaining_len * BCHUNK_HASH_TABLE_MUL;
let mut table: Vec<PtrMut<BTableRef>> = vec![null_mut(); table_len];
{
let mut hash_store: Vec<HashKey> = Vec::with_capacity(info.accum_read_ahead_len);
unsafe { hash_store.set_len(info.accum_read_ahead_len) };
let mut chunk_list_reference_bytes_remaining: usize =
chunk_list_reference.total_size - chunk_list_reference_skip_bytes;
let mut cref: PtrMut<BChunkRef> = {
if cref_match_first != null_mut() {
chunk_list_reference_bytes_remaining += cref_match_first.link.data.len();
cref_match_first
} else {
chunk_list_reference.chunk_refs.head
}
};
if USE_PARANOID_CHECKS {
let mut test_bytes_len: usize = 0;
let mut cr: PtrMut<BChunkRef> = cref;
while cr != chunk_list_reference_last {
test_bytes_len += cr.link.data.len();
cr = cr.next;
}
debug_assert!(test_bytes_len == chunk_list_reference_bytes_remaining);
}
while
(cref != chunk_list_reference_last) &&
(chunk_list_reference_bytes_remaining >= info.accum_read_ahead_bytes)
{
let key: HashKey = key_from_chunk_ref(info, cref, &mut hash_store[..]);
let key_index: usize = (key % table_len as HashKey) as usize;
let tref_prev: PtrMut<BTableRef> = table[key_index];
debug_assert!(table_ref_stack.len() < chunk_list_reference_remaining_len);
table_ref_stack.push(BTableRef { cref: cref, next: tref_prev });
table[key_index] = PtrMut(table_ref_stack.last_mut().unwrap());
chunk_list_reference_bytes_remaining -= cref.link.data.len();
cref = cref.next;
}
debug_assert!(table_ref_stack.len() <= chunk_list_reference_remaining_len);
drop(hash_store);
}
debug_assert!(i_prev <= data_len);
let mut i = i_prev;
while i < data_len {
let mut cref_found: PtrMut<BChunkRef> = table_lookup(
info,
&table, table_len, i_table_start,
data, data_len, i, &table_hash_array);
if cref_found != null_const() {
debug_assert!(i < data_len);
if i != i_prev {
bchunk_list_append_data_n(info, bs_mem, chunk_list, &data[i_prev..i]);
i_prev = i;
if false && i_prev != 0 { } }
{
let chunk_found: PtrMut<BChunk> = cref_found.link;
i += chunk_found.data.len();
bchunk_list_append(info, bs_mem, chunk_list, chunk_found);
}
i_prev = i;
debug_assert!(i_prev <= data_len);
debug_assert_chunklist_size!(chunk_list, i_prev);
debug_assert_chunklist_data!(chunk_list, data);
while
(cref_found.next != null_mut()) &&
(cref_found.next != chunk_list_reference_last)
{
cref_found = cref_found.next;
let chunk_found: PtrMut<BChunk> = cref_found.link;
if bchunk_data_compare(chunk_found, data, data_len, i_prev) {
i += chunk_found.data.len();
bchunk_list_append(info, bs_mem, chunk_list, chunk_found);
i_prev = i;
debug_assert!(i_prev <= data_len);
debug_assert_chunklist_size!(chunk_list, i_prev);
debug_assert_chunklist_data!(chunk_list, data);
} else {
break;
}
}
} else {
i = i + info.chunk_stride;
}
}
drop(table_hash_array);
drop(table);
drop(table_ref_stack);
}
debug_assert_chunklist_size!(chunk_list, i_prev);
debug_assert_chunklist_data!(chunk_list, data);
if i_prev != data_len {
bchunk_list_append_data_n(info, bs_mem, chunk_list, &data[i_prev..data_len]);
i_prev = data_len;
}
debug_assert!(i_prev == data_len);
if USE_FASTPATH_CHUNKS_LAST {
if chunk_list_reference_last != null_mut() {
let mut cref: PtrMut<BChunkRef> = chunk_list_reference_last;
while cref != null_mut() {
let chunk: PtrMut<BChunk> = cref.link;
i_prev += chunk.data.len();
bchunk_list_append_only(bs_mem, chunk_list, chunk);
debug_assert_chunklist_data!(chunk_list, data);
cref = cref.next;
}
}
}
debug_assert!(i_prev == data_len_original);
debug_assert_chunklist_size!(chunk_list, data_len_original);
debug_assert_chunklist_size!(chunk_list_reference, chunk_list_reference.total_size);
debug_assert_chunklist_data!(chunk_list, data);
return chunk_list;
}
impl BArrayStore {
pub fn new(
stride: usize,
chunk_count: usize,
) -> BArrayStore {
let accum_steps = BCHUNK_HASH_TABLE_ACCUMULATE_STEPS - 1;
let accum_read_ahead_len = ((((accum_steps * (accum_steps + 1))) / 2) + 1) as usize;
let accum_read_ahead_bytes = accum_read_ahead_len * stride;
BArrayStore {
info: BArrayInfo {
chunk_stride: stride,
chunk_byte_size: chunk_count * stride,
chunk_byte_size_min: max(1, chunk_count / BCHUNK_SIZE_MIN_DIV) * stride,
chunk_byte_size_max: (chunk_count * BCHUNK_SIZE_MAX_MUL) * stride,
accum_steps: accum_steps,
accum_read_ahead_len: accum_read_ahead_len,
accum_read_ahead_bytes: accum_read_ahead_bytes,
},
memory: BArrayMemory {
state: MemPool::new(),
chunk_list: MemPool::new(),
chunk_ref: MemPool::new(),
chunk: MemPool::new(),
},
states: ListBase::new(),
}
}
fn free_data(&mut self) {
for mut chunk in self.memory.chunk.iter_mut() {
unsafe { ::std::ptr::drop_in_place(&mut chunk.data); }
}
}
pub fn clear(
&mut self,
) {
self.free_data();
self.states.clear();
self.memory.chunk_list.clear();
self.memory.chunk_ref.clear();
self.memory.chunk.clear();
}
pub fn calc_size_expanded_get(
&self,
) -> usize {
let mut size_accum: usize = 0;
for state in self.states.iter() {
size_accum += state.chunk_list.total_size;
}
size_accum
}
pub fn calc_size_compacted_get(
&self,
) -> usize {
let mut size_total: usize = 0;
for chunk in self.memory.chunk.iter() {
debug_assert!(chunk.users > 0);
size_total += chunk.data.len();
}
size_total
}
pub fn state_add(
&mut self,
data: &[u8],
state_reference: Option<*const BArrayState>,
) -> *mut BArrayState {
debug_assert_eq!(0, data.len() % self.info.chunk_stride);
if USE_PARANOID_CHECKS {
if let Some(state_reference) = state_reference {
assert!(self.states.index_at(PtrConst(state_reference)).is_some());
}
}
let mut chunk_list = {
if let Some(state_reference) = state_reference {
bchunk_list_from_data_merge(
&self.info, &mut self.memory,
data, data.len(),
PtrConst(state_reference).chunk_list,
)
} else {
let chunk_list = bchunk_list_new(&mut self.memory, data.len());
bchunk_list_fill_from_array(
&self.info, &mut self.memory,
chunk_list,
data,
);
chunk_list
}
};
chunk_list.users += 1;
let state = PtrMut(self.memory.state.alloc_elem_from(
BArrayState {
next: null_mut(),
prev: null_mut(),
chunk_list: chunk_list,
})
);
self.states.push_back(state);
if USE_PARANOID_CHECKS {
let data_test = BArrayStore::state_data_get_alloc(state.as_ptr());
assert_eq!(data_test.len(), data.len());
assert!(data_test == data);
}
return state.as_ptr();
}
pub fn state_remove(
&mut self,
state: *mut BArrayState,
) {
let state = PtrMut(state);
if USE_PARANOID_CHECKS {
assert!(self.states.index_at(state).is_some());
}
bchunk_list_decref(&mut self.memory, state.chunk_list);
self.states.remove(state);
self.memory.state.free_elem(state.as_ptr());
}
pub fn state_size_get(
state: *const BArrayState,
) -> usize {
return unsafe { (*state).chunk_list.total_size };
}
pub fn state_data_get(
state: *const BArrayState,
data: &mut [u8],
) {
let state = PtrConst(state);
if USE_PARANOID_CHECKS {
let mut data_test_len: usize = 0;
for cref in state.chunk_list.chunk_refs.iter() {
data_test_len += cref.link.data.len();
}
assert_eq!(data_test_len, state.chunk_list.total_size);
assert_eq!(data_test_len, data.len());
}
debug_assert_eq!(state.chunk_list.total_size, data.len());
let mut data_step = 0;
for cref in state.chunk_list.chunk_refs.iter() {
let data_step_next = data_step + cref.link.data.len();
debug_assert!(cref.link.users > 0);
{
let aaa = &cref.link.data[..];
data[data_step..data_step_next].clone_from_slice(aaa);
}
data_step = data_step_next;
}
}
pub fn state_data_get_alloc(
state: *const BArrayState,
) -> Vec<u8> {
let state = PtrConst(state);
let mut data: Vec<u8> = Vec::with_capacity(state.chunk_list.total_size);
unsafe { data.set_len(state.chunk_list.total_size) };
BArrayStore::state_data_get(state.as_ptr(), &mut data[..]);
return data;
}
pub fn is_valid(
&self,
) -> bool {
for state in self.states.iter() {
let chunk_list: PtrMut<BChunkList> = state.chunk_list;
if bchunk_list_size(chunk_list) != chunk_list.total_size {
return false;
}
if chunk_list.chunk_refs.len_calc() != chunk_list.chunk_refs_len {
return false;
}
if USE_MERGE_CHUNKS {
if chunk_list.total_size > self.info.chunk_byte_size_min {
for cref in chunk_list.chunk_refs.iter() {
if cref.link.data.len() < self.info.chunk_byte_size_min {
return false;
}
}
}
}
}
{
use std::collections::HashMap;
use std::collections::hash_map::Entry::{
Occupied,
Vacant,
};
macro_rules! GHASH_PTR_ADD_USER {
($gh:expr, $pt:expr) => {
match $gh.entry($pt.as_ptr()) {
Occupied(mut val) => {
*val.get_mut() += 1;
},
Vacant(entry) => {
entry.insert(1);
}
}
}
}
let mut chunk_list_map: HashMap<*const BChunkList, isize> = HashMap::new();
let mut chunk_map: HashMap<*const BChunk, isize> = HashMap::new();
let mut totrefs: usize = 0;
for state in self.states.iter() {
GHASH_PTR_ADD_USER!(chunk_list_map, state.chunk_list);
}
for (chunk_list, users) in chunk_list_map.iter() {
if !(unsafe { (**chunk_list).users } == *users) {
return false;
}
}
if !(self.memory.chunk_list.len() == chunk_list_map.len()) {
return false;
}
for (chunk_list, _users) in chunk_list_map.iter() {
for cref in unsafe { (**chunk_list) .chunk_refs.iter() } {
GHASH_PTR_ADD_USER!(chunk_map, cref.link);
totrefs += 1;
}
}
if self.memory.chunk.len() != chunk_map.len() {
return false;
}
if self.memory.chunk_ref.len() != totrefs {
return false;
}
for (chunk, users) in chunk_map.iter() {
if !(unsafe { (**chunk).users } == *users) {
return false;
}
}
}
return true;
}
}
impl Drop for BArrayStore {
fn drop(&mut self) {
self.free_data();
}
}
fn bchunk_list_size(chunk_list: PtrMut<BChunkList>) -> usize {
let mut total_size: usize = 0;
for cref in chunk_list.chunk_refs.iter() {
total_size += cref.link.data.len();
}
return total_size;
}