diff --git a/src/bytes.rs b/src/bytes.rs index aed4d7cdb..634e47eb3 100644 --- a/src/bytes.rs +++ b/src/bytes.rs @@ -385,6 +385,13 @@ impl Bytes { ret } + /// Returns a slice of self for the provided range, or `None` if the range + /// is out of bounds. + pub fn try_slice(&self, range: impl RangeBounds) -> Option { + let (begin, end) = crate::try_range(range, self.len())?; + Some(self.slice(begin..end)) + } + /// Returns a slice of self that is equivalent to the given `subset`. /// /// When processing a `Bytes` buffer with other tools, one often gets a diff --git a/src/lib.rs b/src/lib.rs index 1916b6043..6cb3987a3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -166,6 +166,30 @@ fn range(range: impl core::ops::RangeBounds, len: usize) -> (usize, usize (begin, end) } +/// Performs bounds checking of a range without panicking. +#[inline(always)] +fn try_range(range: impl core::ops::RangeBounds, len: usize) -> Option<(usize, usize)> { + use core::ops::Bound; + + let begin = match range.start_bound() { + Bound::Included(&n) => n, + Bound::Excluded(&n) => n.checked_add(1)?, + Bound::Unbounded => 0, + }; + + let end = match range.end_bound() { + Bound::Included(&n) => n.checked_add(1)?, + Bound::Excluded(&n) => n, + Bound::Unbounded => len, + }; + + if begin <= end && end <= len { + Some((begin, end)) + } else { + None + } +} + /// Error type for the `try_get_` methods of [`Buf`]. /// Indicates that there were not enough remaining /// bytes in the buffer while attempting diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index 5d01b5b82..e136bc266 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -141,6 +141,26 @@ fn slice() { assert_eq!(b, b"lo world"[..]); } +#[test] +fn try_slice() { + let a = Bytes::from(&b"hello world"[..]); + + assert_eq!(a.try_slice(3..5), Some(Bytes::from(&b"lo"[..]))); + assert_eq!(a.try_slice(..5), Some(Bytes::from(&b"hello"[..]))); + assert_eq!(a.try_slice(3..), Some(Bytes::from(&b"lo world"[..]))); + assert_eq!(a.try_slice(3..3), Some(Bytes::new())); +} + +#[test] +fn try_slice_invalid_range() { + let a = Bytes::from(&b"hello world"[..]); + + assert_eq!(a.try_slice(5..44), None); + assert_eq!(a.try_slice(44..49), None); + assert_eq!(a.try_slice(5..3), None); + assert_eq!(a.try_slice(..=usize::MAX), None); +} + #[test] #[should_panic] fn slice_oob_1() {