fxfs/
range.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::errors::FxfsError;
6use anyhow::{Error, ensure};
7use std::fmt::Debug;
8use std::ops::{Range, Rem, Sub};
9
10pub trait RangeExt<T> {
11    /// Returns whether the range is valid (i.e. start <= end).
12    fn is_valid(&self) -> bool;
13
14    /// Returns the length of the range, or an error if the range is `!RangeExt::is_valid()`.
15    /// Since this is intended to be used primarily for possibly-untrusted serialized ranges, the
16    /// error returned is FxfsError::Inconsistent.
17    fn length(&self) -> Result<T, Error>;
18
19    /// Returns the length of the range.
20    ///
21    /// # Safety
22    ///
23    /// The range must be valid (i.e. [`RangeExt::is_valid()`] must be true).
24    unsafe fn unchecked_length(&self) -> T;
25
26    /// Returns true if the range is aligned to the given block size.
27    fn is_aligned(&self, block_size: impl Into<T>) -> bool;
28
29    /// Splits the half-open range `[range.start, range.end)` into the ranges `[range.start,
30    /// split_point)` and `[split_point, range.end)`. If either of the new ranges would be empty,
31    /// then `None` is returned in its place and `Some(range)` is returned for the other. `range`
32    /// must not be empty.
33    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}