#[cfg(creusot)]
use creusot_std::model::DeepModel;
pub const MAX_RANGES: usize = 16;
#[cfg_attr(creusot, derive(DeepModel))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Range {
pub start: u32,
pub end: u32,
}
impl Range {
#[inline]
pub const fn new(start: u32, end: u32) -> Self {
Self { start, end }
}
}
#[derive(Debug, Clone, Copy)]
pub struct ByteRangeTracker {
ranges: [Range; MAX_RANGES],
count: u8,
}
#[cfg_attr(creusot, derive(DeepModel))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ByteRangeError {
AlreadyInitialized { start: u32, end: u32 },
NotInitialized { start: u32, end: u32 },
TooManyRanges,
InvalidRange { start: u32, end: u32 },
}
impl ByteRangeTracker {
pub const fn new() -> Self {
Self {
ranges: [Range { start: 0, end: 0 }; MAX_RANGES],
count: 0,
}
}
pub fn is_empty(&self) -> bool {
self.count == 0
}
#[cfg(any(test, kani))]
pub fn range_count(&self) -> usize {
self.count as usize
}
pub fn is_uninit(&self, start: u32, end: u32) -> bool {
if start >= end {
return true; }
for i in 0..self.count as usize {
let Range {
start: r_start,
end: r_end,
} = self.ranges[i];
if start < r_end && end > r_start {
return false;
}
}
true
}
pub fn is_init(&self, start: u32, end: u32) -> bool {
if start >= end {
return true; }
for i in 0..self.count as usize {
let Range {
start: r_start,
end: r_end,
} = self.ranges[i];
if r_start <= start && end <= r_end {
return true;
}
}
false
}
pub fn mark_init(&mut self, start: u32, end: u32) -> Result<(), ByteRangeError> {
if start >= end {
return Err(ByteRangeError::InvalidRange { start, end });
}
if !self.is_uninit(start, end) {
return Err(ByteRangeError::AlreadyInitialized { start, end });
}
let mut merge_start = start;
let mut merge_end = end;
let mut remove_mask = 0u16;
for i in 0..self.count as usize {
let Range {
start: r_start,
end: r_end,
} = self.ranges[i];
if r_end == start {
merge_start = r_start;
remove_mask |= 1 << i;
} else if end == r_start {
merge_end = r_end;
remove_mask |= 1 << i;
}
}
let ranges_to_remove = remove_mask.count_ones() as usize;
let new_count = self.count as usize - ranges_to_remove + 1;
if new_count > MAX_RANGES {
return Err(ByteRangeError::TooManyRanges);
}
let mut write_idx = 0;
for read_idx in 0..self.count as usize {
if remove_mask & (1 << read_idx) == 0 {
self.ranges[write_idx] = self.ranges[read_idx];
write_idx += 1;
}
}
self.count = write_idx as u8;
let mut insert_idx = 0;
while insert_idx < self.count as usize && self.ranges[insert_idx].start < merge_start {
insert_idx += 1;
}
for i in (insert_idx..self.count as usize).rev() {
self.ranges[i + 1] = self.ranges[i];
}
self.ranges[insert_idx] = Range::new(merge_start, merge_end);
self.count += 1;
Ok(())
}
pub fn mark_uninit(&mut self, start: u32, end: u32) -> Result<(), ByteRangeError> {
if start >= end {
return Err(ByteRangeError::InvalidRange { start, end });
}
let mut containing_idx = None;
for i in 0..self.count as usize {
let Range {
start: r_start,
end: r_end,
} = self.ranges[i];
if r_start <= start && end <= r_end {
containing_idx = Some(i);
break;
}
}
let idx = containing_idx.ok_or(ByteRangeError::NotInitialized { start, end })?;
let Range {
start: r_start,
end: r_end,
} = self.ranges[idx];
if r_start == start && r_end == end {
for i in idx..self.count as usize - 1 {
self.ranges[i] = self.ranges[i + 1];
}
self.count -= 1;
} else if r_start == start {
self.ranges[idx] = Range::new(end, r_end);
} else if r_end == end {
self.ranges[idx] = Range::new(r_start, start);
} else {
if self.count as usize >= MAX_RANGES {
return Err(ByteRangeError::TooManyRanges);
}
self.ranges[idx] = Range::new(r_start, start);
for i in (idx + 1..self.count as usize).rev() {
self.ranges[i + 1] = self.ranges[i];
}
self.ranges[idx + 1] = Range::new(end, r_end);
self.count += 1;
}
Ok(())
}
pub fn clear_range(&mut self, start: u32, end: u32) -> Result<(), ByteRangeError> {
if start >= end {
return Err(ByteRangeError::InvalidRange { start, end });
}
let mut new_ranges = [Range::new(0, 0); MAX_RANGES];
let mut new_count = 0usize;
for i in 0..self.count as usize {
let Range {
start: r_start,
end: r_end,
} = self.ranges[i];
if end <= r_start || start >= r_end {
new_ranges[new_count] = Range::new(r_start, r_end);
new_count += 1;
continue;
}
if r_start < start {
new_ranges[new_count] = Range::new(r_start, start);
new_count += 1;
}
if r_end > end {
new_ranges[new_count] = Range::new(end, r_end);
new_count += 1;
}
}
self.ranges = new_ranges;
self.count = new_count as u8;
Ok(())
}
pub fn ranges(&self) -> &[Range] {
&self.ranges[..self.count as usize]
}
}
impl Default for ByteRangeTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_tracker_is_empty() {
let tracker = ByteRangeTracker::new();
assert!(tracker.is_empty());
assert_eq!(tracker.range_count(), 0);
}
#[test]
fn mark_init_single_range() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
assert!(!tracker.is_empty());
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 4));
assert!(tracker.is_init(1, 3)); assert!(!tracker.is_init(0, 5)); }
#[test]
fn mark_init_disjoint_ranges() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
tracker.mark_init(8, 12).unwrap();
assert_eq!(tracker.range_count(), 2);
assert!(tracker.is_init(0, 4));
assert!(tracker.is_init(8, 12));
assert!(!tracker.is_init(4, 8)); }
#[test]
fn mark_init_adjacent_merges() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
tracker.mark_init(4, 8).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 8));
}
#[test]
fn mark_init_adjacent_merges_reverse_order() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(4, 8).unwrap();
tracker.mark_init(0, 4).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 8));
}
#[test]
fn mark_init_bridges_gap() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
tracker.mark_init(8, 12).unwrap();
tracker.mark_init(4, 8).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 12));
}
#[test]
fn mark_init_overlap_fails() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
let err = tracker.mark_init(4, 12).unwrap_err();
assert!(matches!(err, ByteRangeError::AlreadyInitialized { .. }));
}
#[test]
fn mark_init_exact_overlap_fails() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
let err = tracker.mark_init(0, 8).unwrap_err();
assert!(matches!(err, ByteRangeError::AlreadyInitialized { .. }));
}
#[test]
fn mark_uninit_exact() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
tracker.mark_uninit(0, 8).unwrap();
assert!(tracker.is_empty());
}
#[test]
fn mark_uninit_from_start() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
tracker.mark_uninit(0, 4).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(4, 8));
assert!(!tracker.is_init(0, 4));
}
#[test]
fn mark_uninit_from_end() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
tracker.mark_uninit(4, 8).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 4));
assert!(!tracker.is_init(4, 8));
}
#[test]
fn mark_uninit_from_middle_splits() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 12).unwrap();
tracker.mark_uninit(4, 8).unwrap();
assert_eq!(tracker.range_count(), 2);
assert!(tracker.is_init(0, 4));
assert!(tracker.is_init(8, 12));
assert!(!tracker.is_init(4, 8));
}
#[test]
fn clear_range_partial_overlap() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 12).unwrap();
tracker.clear_range(4, 8).unwrap();
assert_eq!(tracker.range_count(), 2);
assert!(tracker.is_init(0, 4));
assert!(tracker.is_init(8, 12));
assert!(!tracker.is_init(4, 8));
}
#[test]
fn clear_range_full_overlap() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
tracker.mark_init(6, 10).unwrap();
tracker.clear_range(0, 10).unwrap();
assert!(tracker.is_empty());
}
#[test]
fn mark_uninit_not_init_fails() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 4).unwrap();
let err = tracker.mark_uninit(4, 8).unwrap_err();
assert!(matches!(err, ByteRangeError::NotInitialized { .. }));
}
#[test]
fn mark_uninit_partial_overlap_fails() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(0, 8).unwrap();
let err = tracker.mark_uninit(4, 12).unwrap_err();
assert!(matches!(err, ByteRangeError::NotInitialized { .. }));
}
#[test]
fn invalid_range_fails() {
let mut tracker = ByteRangeTracker::new();
let err = tracker.mark_init(8, 4).unwrap_err();
assert!(matches!(err, ByteRangeError::InvalidRange { .. }));
let err = tracker.mark_init(4, 4).unwrap_err();
assert!(matches!(err, ByteRangeError::InvalidRange { .. }));
}
#[test]
fn is_uninit_checks() {
let mut tracker = ByteRangeTracker::new();
assert!(tracker.is_uninit(0, 100));
tracker.mark_init(10, 20).unwrap();
assert!(tracker.is_uninit(0, 10));
assert!(tracker.is_uninit(20, 30));
assert!(!tracker.is_uninit(5, 15)); assert!(!tracker.is_uninit(10, 20)); assert!(!tracker.is_uninit(15, 25)); }
#[test]
fn sorted_order_maintained() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(20, 24).unwrap();
tracker.mark_init(0, 4).unwrap();
tracker.mark_init(10, 14).unwrap();
let ranges = tracker.ranges();
assert_eq!(ranges.len(), 3);
assert_eq!(ranges[0], Range::new(0, 4));
assert_eq!(ranges[1], Range::new(10, 14));
assert_eq!(ranges[2], Range::new(20, 24));
}
#[test]
fn complex_lifecycle() {
let mut tracker = ByteRangeTracker::new();
tracker.mark_init(4, 8).unwrap(); tracker.mark_init(12, 16).unwrap(); tracker.mark_init(0, 4).unwrap(); tracker.mark_init(8, 12).unwrap();
assert_eq!(tracker.range_count(), 1);
assert!(tracker.is_init(0, 16));
tracker.mark_uninit(8, 12).unwrap();
tracker.mark_uninit(0, 4).unwrap();
tracker.mark_uninit(12, 16).unwrap();
tracker.mark_uninit(4, 8).unwrap();
assert!(tracker.is_empty());
}
}
#[cfg(kani)]
mod kani_proofs {
use super::*;
#[kani::proof]
#[kani::unwind(5)]
fn init_then_uninit_is_empty() {
let start: u32 = kani::any();
let end: u32 = kani::any();
kani::assume(start < end);
kani::assume(end - start <= 64);
let mut tracker = ByteRangeTracker::new();
if tracker.mark_init(start, end).is_ok() {
kani::assert(!tracker.is_empty(), "should not be empty after init");
let result = tracker.mark_uninit(start, end);
kani::assert(result.is_ok(), "uninit of exact range should succeed");
kani::assert(tracker.is_empty(), "should be empty after uninit");
}
}
#[kani::proof]
#[kani::unwind(5)]
fn double_init_fails() {
let start: u32 = kani::any();
let end: u32 = kani::any();
kani::assume(start < end);
kani::assume(end <= 64);
let mut tracker = ByteRangeTracker::new();
if tracker.mark_init(start, end).is_ok() {
let result = tracker.mark_init(start, end);
kani::assert(result.is_err(), "double init should fail");
}
}
#[kani::proof]
#[kani::unwind(5)]
fn uninit_without_init_fails() {
let start: u32 = kani::any();
let end: u32 = kani::any();
kani::assume(start < end);
kani::assume(end <= 64);
let tracker = ByteRangeTracker::new();
let mut tracker = tracker;
let result = tracker.mark_uninit(start, end);
kani::assert(result.is_err(), "uninit without init should fail");
}
#[kani::proof]
#[kani::unwind(5)]
fn init_uninit_consistency() {
let start: u32 = kani::any();
let end: u32 = kani::any();
kani::assume(start < end);
kani::assume(end <= 32);
let mut tracker = ByteRangeTracker::new();
kani::assert(tracker.is_uninit(start, end), "fresh tracker is uninit");
kani::assert(!tracker.is_init(start, end), "fresh tracker is not init");
if tracker.mark_init(start, end).is_ok() {
kani::assert(tracker.is_init(start, end), "after init, is_init is true");
kani::assert(
!tracker.is_uninit(start, end),
"after init, is_uninit is false",
);
}
}
#[kani::proof]
#[kani::unwind(5)]
fn adjacent_merge() {
let mid: u32 = kani::any();
let size: u32 = kani::any();
kani::assume(mid >= 4 && mid <= 28);
kani::assume(size >= 1 && size <= 4);
let start1 = mid - size;
let end1 = mid;
let start2 = mid;
let end2 = mid + size;
let mut tracker = ByteRangeTracker::new();
if tracker.mark_init(start1, end1).is_ok() {
if tracker.mark_init(start2, end2).is_ok() {
kani::assert(tracker.range_count() == 1, "adjacent ranges should merge");
kani::assert(tracker.is_init(start1, end2), "merged range covers both");
}
}
}
#[kani::proof]
#[kani::unwind(5)]
fn disjoint_stay_separate() {
let mut tracker = ByteRangeTracker::new();
let r1_start: u32 = kani::any();
let r1_size: u32 = kani::any();
let gap: u32 = kani::any();
let r2_size: u32 = kani::any();
kani::assume(r1_start <= 8);
kani::assume(r1_size >= 1 && r1_size <= 4);
kani::assume(gap >= 1 && gap <= 4);
kani::assume(r2_size >= 1 && r2_size <= 4);
let r1_end = r1_start + r1_size;
let r2_start = r1_end + gap;
let r2_end = r2_start + r2_size;
kani::assume(r2_end <= 32);
if tracker.mark_init(r1_start, r1_end).is_ok() {
if tracker.mark_init(r2_start, r2_end).is_ok() {
kani::assert(tracker.range_count() == 2, "disjoint ranges stay separate");
kani::assert(tracker.is_uninit(r1_end, r2_start), "gap remains uninit");
}
}
}
#[kani::proof]
#[kani::unwind(5)]
fn middle_uninit_splits() {
let mut tracker = ByteRangeTracker::new();
if tracker.mark_init(0, 12).is_ok() {
kani::assert(tracker.range_count() == 1, "starts with one range");
if tracker.mark_uninit(4, 8).is_ok() {
kani::assert(tracker.range_count() == 2, "split into two ranges");
kani::assert(tracker.is_init(0, 4), "left part still init");
kani::assert(tracker.is_init(8, 12), "right part still init");
kani::assert(tracker.is_uninit(4, 8), "middle now uninit");
}
}
}
#[kani::proof]
#[kani::unwind(5)]
fn overlapping_init_fails() {
let mut tracker = ByteRangeTracker::new();
let start1: u32 = kani::any();
let end1: u32 = kani::any();
let start2: u32 = kani::any();
let end2: u32 = kani::any();
kani::assume(start1 < end1 && end1 <= 32);
kani::assume(start2 < end2 && end2 <= 32);
kani::assume(start2 < end1);
kani::assume(start1 < end2);
if tracker.mark_init(start1, end1).is_ok() {
let result = tracker.mark_init(start2, end2);
kani::assert(result.is_err(), "overlapping init should fail");
}
}
}