| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use { |
| crate::errors::FxfsError, |
| anyhow::{ensure, Error}, |
| std::{ |
| fmt::Debug, |
| ops::{Range, Rem, Sub}, |
| }, |
| }; |
| |
| pub trait RangeExt<T> { |
| /// Returns whether the range is valid (i.e. start <= end). |
| fn is_valid(&self) -> bool; |
| /// Returns the length of the range, or an error if the range is `!RangeExt::is_valid()`. |
| /// Since this is intended to be used primarily for possibly-untrusted serialized ranges, the |
| /// error returned is FxfsError::Inconsistent. |
| fn length(&self) -> Result<T, Error>; |
| /// Returns true if the range is aligned to the given block size. |
| fn is_aligned(&self, block_size: impl Into<T>) -> bool; |
| |
| /// Splits the half-open range `[range.start, range.end)` into the ranges `[range.start, |
| /// split_point)` and `[split_point, range.end)`. If either of the new ranges would be empty, |
| /// then `None` is returned in its place and `Some(range)` is returned for the other. `range` |
| /// must not be empty. |
| fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>); |
| } |
| |
| impl<T: Sub<Output = T> + Copy + Ord + Debug + Rem<Output = T> + PartialEq + Default> RangeExt<T> |
| for Range<T> |
| { |
| fn is_valid(&self) -> bool { |
| self.start <= self.end |
| } |
| fn length(&self) -> Result<T, Error> { |
| ensure!(self.is_valid(), FxfsError::Inconsistent); |
| Ok(self.end - self.start) |
| } |
| fn is_aligned(&self, block_size: impl Into<T>) -> bool { |
| let bs = block_size.into(); |
| self.start % bs == T::default() && self.end % bs == T::default() |
| } |
| |
| fn split(self, split_point: T) -> (Option<Range<T>>, Option<Range<T>>) { |
| debug_assert!(!self.is_empty()); |
| if split_point <= self.start { |
| (None, Some(self)) |
| } else if split_point >= self.end { |
| (Some(self), None) |
| } else { |
| (Some(self.start..split_point), Some(split_point..self.end)) |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::RangeExt; |
| |
| #[test] |
| fn test_split_range() { |
| assert_eq!((10..20).split(0), (None, Some(10..20))); |
| assert_eq!((10..20).split(9), (None, Some(10..20))); |
| assert_eq!((10..20).split(10), (None, Some(10..20))); |
| assert_eq!((10..20).split(11), (Some(10..11), Some(11..20))); |
| assert_eq!((10..20).split(15), (Some(10..15), Some(15..20))); |
| assert_eq!((10..20).split(19), (Some(10..19), Some(19..20))); |
| assert_eq!((10..20).split(20), (Some(10..20), None)); |
| assert_eq!((10..20).split(25), (Some(10..20), None)); |
| } |
| } |