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::{ensure, Error};
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    /// Returns the length of the range, or an error if the range is `!RangeExt::is_valid()`.
14    /// Since this is intended to be used primarily for possibly-untrusted serialized ranges, the
15    /// error returned is FxfsError::Inconsistent.
16    fn length(&self) -> Result<T, Error>;
17    /// Returns true if the range is aligned to the given block size.
18    fn is_aligned(&self, block_size: impl Into<T>) -> bool;
19
20    /// Splits the half-open range `[range.start, range.end)` into the ranges `[range.start,
21    /// split_point)` and `[split_point, range.end)`. If either of the new ranges would be empty,
22    /// then `None` is returned in its place and `Some(range)` is returned for the other. `range`
23    /// must not be empty.
24    fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>);
25}
26
27impl<T: Sub<Output = T> + Copy + Ord + Debug + Rem<Output = T> + PartialEq + Default> RangeExt<T>
28    for Range<T>
29{
30    fn is_valid(&self) -> bool {
31        self.start <= self.end
32    }
33    fn length(&self) -> Result<T, Error> {
34        ensure!(self.is_valid(), FxfsError::Inconsistent);
35        Ok(self.end - self.start)
36    }
37    fn is_aligned(&self, block_size: impl Into<T>) -> bool {
38        let bs = block_size.into();
39        self.start % bs == T::default() && self.end % bs == T::default()
40    }
41
42    fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>) {
43        debug_assert!(!self.is_empty());
44        if split_point <= self.start {
45            (None, Some(self))
46        } else if split_point >= self.end {
47            (Some(self), None)
48        } else {
49            (Some(self.start..split_point), Some(split_point..self.end))
50        }
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::RangeExt;
57
58    #[test]
59    fn test_split_range() {
60        assert_eq!((10..20).split(0), (None, Some(10..20)));
61        assert_eq!((10..20).split(9), (None, Some(10..20)));
62        assert_eq!((10..20).split(10), (None, Some(10..20)));
63        assert_eq!((10..20).split(11), (Some(10..11), Some(11..20)));
64        assert_eq!((10..20).split(15), (Some(10..15), Some(15..20)));
65        assert_eq!((10..20).split(19), (Some(10..19), Some(19..20)));
66        assert_eq!((10..20).split(20), (Some(10..20), None));
67        assert_eq!((10..20).split(25), (Some(10..20), None));
68    }
69}