use crate::constants::{BLOCK_HEADER_SIZE, BLOCK_SIZE};
pub fn is_block_boundary(pos: u64) -> bool {
pos.is_multiple_of(BLOCK_SIZE)
}
pub fn round_down_to_block_boundary(pos: u64) -> u64 {
pos - (pos % BLOCK_SIZE)
}
pub fn remaining_in_block(pos: u64) -> u64 {
let offset = pos % BLOCK_SIZE;
if offset == 0 { 0 } else { BLOCK_SIZE - offset }
}
pub fn is_possible_chunk_boundary(pos: u64) -> bool {
(pos % BLOCK_SIZE) >= BLOCK_HEADER_SIZE
}
pub fn round_up_to_possible_chunk_boundary(pos: u64) -> u64 {
let offset = pos % BLOCK_SIZE;
if offset >= BLOCK_HEADER_SIZE {
pos
} else {
round_down_to_block_boundary(pos) + BLOCK_HEADER_SIZE
}
}
pub fn remaining_in_block_header(pos: u64) -> usize {
let offset = pos % BLOCK_SIZE;
if offset < BLOCK_HEADER_SIZE {
(BLOCK_HEADER_SIZE - offset) as usize
} else {
0
}
}
pub fn add_with_overhead(chunk_begin: u64, length: u64) -> u64 {
let mut pos = chunk_begin;
let mut remaining = length;
loop {
if remaining == 0 {
break;
}
let header_skip = remaining_in_block_header(pos) as u64;
pos += header_skip;
let avail = remaining_in_block(pos);
if avail == 0 {
pos += BLOCK_HEADER_SIZE;
continue;
}
if remaining <= avail {
pos += remaining;
remaining = 0;
} else {
remaining -= avail;
pos += avail;
}
}
pos
}
pub fn distance_without_overhead(chunk_begin: u64, pos: u64) -> u64 {
let mut cur = chunk_begin;
let mut data_bytes = 0u64;
while cur < pos {
let header_skip = remaining_in_block_header(cur) as u64;
cur += header_skip;
if cur >= pos {
break;
}
let avail = remaining_in_block(cur);
let avail = if avail == 0 {
BLOCK_SIZE - BLOCK_HEADER_SIZE
} else {
avail
};
let step = avail.min(pos - cur);
data_bytes += step;
cur += step;
}
data_bytes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_block_boundary() {
assert!(is_block_boundary(0));
assert!(is_block_boundary(65536));
assert!(is_block_boundary(131072));
assert!(!is_block_boundary(1));
assert!(!is_block_boundary(65535));
assert!(!is_block_boundary(65537));
}
#[test]
fn test_round_down_to_block_boundary() {
assert_eq!(round_down_to_block_boundary(0), 0);
assert_eq!(round_down_to_block_boundary(1), 0);
assert_eq!(round_down_to_block_boundary(65535), 0);
assert_eq!(round_down_to_block_boundary(65536), 65536);
assert_eq!(round_down_to_block_boundary(65537), 65536);
}
#[test]
fn test_remaining_in_block() {
assert_eq!(remaining_in_block(0), 0);
assert_eq!(remaining_in_block(1), 65535);
assert_eq!(remaining_in_block(65535), 1);
assert_eq!(remaining_in_block(65536), 0);
assert_eq!(remaining_in_block(65537), 65535);
}
#[test]
fn test_is_possible_chunk_boundary() {
assert!(!is_possible_chunk_boundary(0));
assert!(!is_possible_chunk_boundary(23));
assert!(is_possible_chunk_boundary(24));
assert!(is_possible_chunk_boundary(65535));
assert!(!is_possible_chunk_boundary(65536));
assert!(is_possible_chunk_boundary(65536 + 24));
}
#[test]
fn test_round_up_to_possible_chunk_boundary() {
assert_eq!(round_up_to_possible_chunk_boundary(0), 24);
assert_eq!(round_up_to_possible_chunk_boundary(23), 24);
assert_eq!(round_up_to_possible_chunk_boundary(24), 24);
assert_eq!(round_up_to_possible_chunk_boundary(100), 100);
assert_eq!(round_up_to_possible_chunk_boundary(65536), 65536 + 24);
assert_eq!(round_up_to_possible_chunk_boundary(65536 + 24), 65536 + 24);
}
#[test]
fn test_remaining_in_block_header() {
assert_eq!(remaining_in_block_header(0), 24);
assert_eq!(remaining_in_block_header(1), 23);
assert_eq!(remaining_in_block_header(23), 1);
assert_eq!(remaining_in_block_header(24), 0);
assert_eq!(remaining_in_block_header(65536), 24);
assert_eq!(remaining_in_block_header(65536 + 24), 0);
}
#[test]
fn test_add_with_overhead_simple() {
assert_eq!(add_with_overhead(24, 100), 124);
}
#[test]
fn test_add_with_overhead_zero_length() {
assert_eq!(add_with_overhead(24, 0), 24);
assert_eq!(add_with_overhead(100, 0), 100);
}
#[test]
fn test_add_with_overhead_crosses_block_boundary() {
let start = 24u64;
let length = 65512 + 100; let result = add_with_overhead(start, length);
assert_eq!(result, 65536 + 24 + 100);
}
#[test]
fn test_add_with_overhead_exact_block_end() {
let start = 24u64;
let length = 65512u64;
let result = add_with_overhead(start, length);
assert_eq!(result, 65536);
}
#[test]
fn test_distance_without_overhead_simple() {
assert_eq!(distance_without_overhead(24, 124), 100);
}
#[test]
fn test_distance_without_overhead_crosses_block() {
let end = 65536 + 24 + 100;
assert_eq!(distance_without_overhead(24, end as u64), 65512 + 100);
}
#[test]
fn test_add_distance_roundtrip() {
let starts = [24u64, 100, 65536 + 24, 131072 + 24];
let lengths = [0u64, 1, 65512, 65513, 200_000];
for &start in &starts {
for &len in &lengths {
let end = add_with_overhead(start, len);
let recovered = distance_without_overhead(start, end);
assert_eq!(
recovered, len,
"round-trip failed for start={start}, len={len}"
);
}
}
}
}