Skip to main content

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    /// Splits the half-open range `[range.start, range.end)` into the ranges `[range.start,
27    /// split_point)` and `[split_point, range.end)`. If either of the new ranges would be empty,
28    /// then `None` is returned in its place and `Some(range)` is returned for the other. `range`
29    /// must not be empty.
30    fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>);
31}
32
33impl<T: Sub<Output = T> + Copy + Ord + Debug + Rem<Output = T> + PartialEq + Default> RangeExt<T>
34    for Range<T>
35{
36    fn is_valid(&self) -> bool {
37        self.start <= self.end
38    }
39
40    fn length(&self) -> Result<T, Error> {
41        ensure!(self.is_valid(), FxfsError::Inconsistent);
42        Ok(self.end - self.start)
43    }
44
45    unsafe fn unchecked_length(&self) -> T {
46        self.end - self.start
47    }
48
49    fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>) {
50        debug_assert!(!self.is_empty());
51        if split_point <= self.start {
52            (None, Some(self))
53        } else if split_point >= self.end {
54            (Some(self), None)
55        } else {
56            (Some(self.start..split_point), Some(split_point..self.end))
57        }
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::RangeExt;
64
65    #[test]
66    fn test_split_range() {
67        assert_eq!((10..20).split(0), (None, Some(10..20)));
68        assert_eq!((10..20).split(9), (None, Some(10..20)));
69        assert_eq!((10..20).split(10), (None, Some(10..20)));
70        assert_eq!((10..20).split(11), (Some(10..11), Some(11..20)));
71        assert_eq!((10..20).split(15), (Some(10..15), Some(15..20)));
72        assert_eq!((10..20).split(19), (Some(10..19), Some(19..20)));
73        assert_eq!((10..20).split(20), (Some(10..20), None));
74        assert_eq!((10..20).split(25), (Some(10..20), None));
75    }
76}