use crate::bmt::{BRANCHES, DEFAULT_BODY_SIZE, HASH_SIZE};
const fn compute_level_limit(branches: usize, body_size: usize) -> usize {
let body_bits = body_size.trailing_zeros() as usize;
let branch_bits = branches.trailing_zeros() as usize;
(64 - body_bits).div_ceil(branch_bits) + 1
}
pub(crate) const LEVEL_LIMIT: usize = compute_level_limit(BRANCHES, DEFAULT_BODY_SIZE);
const _: () = assert!(LEVEL_LIMIT == 9);
pub(crate) const REF_SIZE: usize = HASH_SIZE;
#[cfg(test)]
const REFS_PER_CHUNK: usize = BRANCHES;
const fn compute_spans(branches: usize) -> [u64; LEVEL_LIMIT] {
let mut spans = [0u64; LEVEL_LIMIT];
let mut span = 1u64;
let mut i = 0;
while i < LEVEL_LIMIT {
spans[i] = span;
span = span.saturating_mul(branches as u64);
i += 1;
}
spans
}
pub(crate) const ENCRYPTED_REF_SIZE: usize = crate::chunk::encryption::EncryptedChunkRef::SIZE;
pub(crate) const fn compute_spans_inline(branches: usize) -> [u64; LEVEL_LIMIT] {
compute_spans(branches)
}
pub(crate) const fn assert_valid_body_size<const BODY_SIZE: usize>() {
assert!(BODY_SIZE >= 64, "BODY_SIZE must be at least 64");
assert!(
BODY_SIZE.is_power_of_two(),
"BODY_SIZE must be a power of 2"
);
}
pub(crate) const fn tree_depth(length: u64, chunk_size: usize, ref_size: usize) -> usize {
if length == 0 {
return 0;
}
let branches = (chunk_size / ref_size) as u64;
let data_chunks = length.div_ceil(chunk_size as u64);
if data_chunks <= 1 {
return 1;
}
let mut depth = 1;
let mut chunks = data_chunks;
while chunks > 1 {
chunks = chunks.div_ceil(branches);
depth += 1;
}
depth
}
#[inline]
pub(crate) fn subspan_for_spans<const BODY_SIZE: usize>(
span: u64,
spans: &[u64; LEVEL_LIMIT],
) -> u64 {
for i in 0..LEVEL_LIMIT {
let level_span = spans[i] * BODY_SIZE as u64;
if span <= level_span {
return if i == 0 {
BODY_SIZE as u64
} else {
spans[i - 1] * BODY_SIZE as u64
};
}
}
spans[LEVEL_LIMIT - 2] * BODY_SIZE as u64
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bmt::DEFAULT_BODY_SIZE;
use crate::file::levels;
const SPANS: [u64; LEVEL_LIMIT] = compute_spans(REFS_PER_CHUNK);
fn span_for_level(level: usize, position: u64, chunk_size: usize) -> u64 {
let span_size = SPANS[level] * chunk_size as u64;
(position - 1) % span_size + 1
}
#[test]
fn test_spans_values() {
assert_eq!(SPANS[0], 1);
assert_eq!(SPANS[1], 128);
assert_eq!(SPANS[2], 128 * 128);
assert_eq!(SPANS[3], 128 * 128 * 128);
}
#[test]
fn test_levels_empty() {
assert_eq!(levels(0, DEFAULT_BODY_SIZE), 0);
}
#[test]
fn test_levels_single_chunk() {
assert_eq!(levels(1, DEFAULT_BODY_SIZE), 1);
assert_eq!(levels(4096, DEFAULT_BODY_SIZE), 1);
}
#[test]
fn test_levels_two_chunks() {
assert_eq!(levels(4097, DEFAULT_BODY_SIZE), 2);
assert_eq!(levels(524288, DEFAULT_BODY_SIZE), 2);
}
#[test]
fn test_levels_three_levels() {
assert_eq!(levels(524289, DEFAULT_BODY_SIZE), 3);
}
#[test]
fn test_levels_boundary_128_squared() {
let boundary = 128 * 128 * DEFAULT_BODY_SIZE;
assert_eq!(levels(boundary as u64, DEFAULT_BODY_SIZE), 3);
assert_eq!(levels(boundary as u64 + 1, DEFAULT_BODY_SIZE), 4);
}
#[test]
fn test_span_for_level() {
assert_eq!(span_for_level(0, 100, DEFAULT_BODY_SIZE), 100);
assert_eq!(span_for_level(0, 4096, DEFAULT_BODY_SIZE), 4096);
assert_eq!(span_for_level(1, 524288, DEFAULT_BODY_SIZE), 524288);
assert_eq!(span_for_level(1, 524289, DEFAULT_BODY_SIZE), 1);
}
}