1use crate::errors::FxfsError;
6use anyhow::{Error, ensure};
7use std::fmt::Debug;
8use std::ops::{Range, Rem, Sub};
9
10pub trait RangeExt<T> {
11 fn is_valid(&self) -> bool;
13
14 fn length(&self) -> Result<T, Error>;
18
19 unsafe fn unchecked_length(&self) -> T;
25
26 fn is_aligned(&self, block_size: impl Into<T>) -> bool;
28
29 fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>);
34}
35
36impl<T: Sub<Output = T> + Copy + Ord + Debug + Rem<Output = T> + PartialEq + Default> RangeExt<T>
37 for Range<T>
38{
39 fn is_valid(&self) -> bool {
40 self.start <= self.end
41 }
42
43 fn length(&self) -> Result<T, Error> {
44 ensure!(self.is_valid(), FxfsError::Inconsistent);
45 Ok(self.end - self.start)
46 }
47
48 unsafe fn unchecked_length(&self) -> T {
49 self.end - self.start
50 }
51
52 fn is_aligned(&self, block_size: impl Into<T>) -> bool {
53 let bs = block_size.into();
54 self.start % bs == T::default() && self.end % bs == T::default()
55 }
56
57 fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>) {
58 debug_assert!(!self.is_empty());
59 if split_point <= self.start {
60 (None, Some(self))
61 } else if split_point >= self.end {
62 (Some(self), None)
63 } else {
64 (Some(self.start..split_point), Some(split_point..self.end))
65 }
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::RangeExt;
72
73 #[test]
74 fn test_split_range() {
75 assert_eq!((10..20).split(0), (None, Some(10..20)));
76 assert_eq!((10..20).split(9), (None, Some(10..20)));
77 assert_eq!((10..20).split(10), (None, Some(10..20)));
78 assert_eq!((10..20).split(11), (Some(10..11), Some(11..20)));
79 assert_eq!((10..20).split(15), (Some(10..15), Some(15..20)));
80 assert_eq!((10..20).split(19), (Some(10..19), Some(19..20)));
81 assert_eq!((10..20).split(20), (Some(10..20), None));
82 assert_eq!((10..20).split(25), (Some(10..20), None));
83 }
84}