Cheap reader for bytes slice (#2261)

* initial draft implementation

* change name to follow rust convention.

* revert use of HasTargetBytes instead of HasMutatorBytes for BytesSubInputMut

* clippy

* nostd

* clippy

* clippy

* * HasLen required if implementing HasTargetBytes.
* Added a checked version of the read to slice.

* clippy

* fix name.
better doc.

* added a common bytes trait for HasTargetBytes and HasMutatorBytes.

* change interface

* fix tests

* clippers

* use byte slice for subbytes

* adapt to main

* fix doc

* mut sub slice version. return subinput to old state, and add subslice stubs

* better api, doc fixes.

* Don't clone, reshuffle

* Move and rename

* Uh-oh

* move to bolts. rename things.

* nostd

* format

* alloc

* fix doc

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dominik Maier <dmnk@google.com>
This commit is contained in:
Romain Malmain 2024-07-30 13:46:40 +02:00 committed by GitHub
parent 211809dddb
commit c319fe2033
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 485 additions and 125 deletions

View File

@ -1,38 +1,17 @@
//! [`BytesSubInput`] is a wrapper input that can be used to mutate parts of a byte slice //! [`BytesSubInput`] is a wrapper input that can be used to mutate parts of a byte slice
use alloc::vec::Vec; use alloc::vec::{self, Vec};
use core::{ use core::{
cmp::{min, Ordering}, cmp::Ordering,
ops::{Bound, Range, RangeBounds}, ops::{Range, RangeBounds},
}; };
use libafl_bolts::HasLen; use libafl_bolts::{
subrange::{end_index, start_index, sub_range},
HasLen,
};
use super::HasMutatorBytes; use crate::inputs::HasMutatorBytes;
/// Gets the relevant concrete start index from [`RangeBounds`] (inclusive)
fn start_index<R>(range: &R) -> usize
where
R: RangeBounds<usize>,
{
match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(start) => *start,
Bound::Excluded(start) => start + 1,
}
}
/// Gets the relevant concrete end index from [`RangeBounds`] (exclusive)
fn end_index<R>(range: &R, max_len: usize) -> usize
where
R: RangeBounds<usize>,
{
match range.end_bound() {
Bound::Unbounded => max_len,
Bound::Included(end) => end + 1,
Bound::Excluded(end) => *end,
}
}
/// The [`BytesSubInput`] makes it possible to use [`crate::mutators::Mutator`]`s` that work on /// The [`BytesSubInput`] makes it possible to use [`crate::mutators::Mutator`]`s` that work on
/// inputs implementing the [`HasMutatorBytes`] for a sub-range of this input. /// inputs implementing the [`HasMutatorBytes`] for a sub-range of this input.
@ -85,12 +64,9 @@ where
/// assert_eq!(bytes_input.bytes(), [1, 2, 3, 4, 42, 42, 42, 5]); /// assert_eq!(bytes_input.bytes(), [1, 2, 3, 4, 42, 42, 42, 5]);
/// ``` /// ```
/// ///
/// The input supports all methods in the [`HasMutatorBytes`] trait. /// The input supports all methods in the [`HasMutatorBytes`] trait if the parent input also implements this trait.
#[derive(Debug)] #[derive(Debug)]
pub struct BytesSubInput<'a, I> pub struct BytesSubInput<'a, I: ?Sized> {
where
I: HasMutatorBytes + ?Sized,
{
/// The (complete) parent input we will work on /// The (complete) parent input we will work on
pub(crate) parent_input: &'a mut I, pub(crate) parent_input: &'a mut I,
/// The range inside the parent input we will work on /// The range inside the parent input we will work on
@ -99,7 +75,7 @@ where
impl<'a, I> BytesSubInput<'a, I> impl<'a, I> BytesSubInput<'a, I>
where where
I: HasMutatorBytes + ?Sized + HasLen, I: HasMutatorBytes + ?Sized,
{ {
/// Creates a new [`BytesSubInput`] that's a view on an input with mutator bytes. /// Creates a new [`BytesSubInput`] that's a view on an input with mutator bytes.
/// The sub input can then be used to mutate parts of the original input. /// The sub input can then be used to mutate parts of the original input.
@ -117,65 +93,11 @@ where
}, },
} }
} }
/// The inclusive start index in the parent buffer
fn start_index(&self) -> usize {
self.range.start
}
/// The exclusive end index in the parent buffer
fn end_index(&self) -> usize {
self.range.end
}
/// Creates a sub range in the current own range
fn sub_range<R2>(&self, range: R2) -> (Bound<usize>, Bound<usize>)
where
R2: RangeBounds<usize>,
{
let start = match (self.range.start_bound(), range.start_bound()) {
(Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded,
(Bound::Excluded(bound), Bound::Unbounded)
| (Bound::Unbounded, Bound::Excluded(bound)) => Bound::Excluded(*bound),
(Bound::Included(bound), Bound::Unbounded)
| (Bound::Unbounded, Bound::Included(bound)) => Bound::Included(*bound),
(Bound::Included(own), Bound::Included(other)) => Bound::Included(own + other),
(Bound::Included(own), Bound::Excluded(other))
| (Bound::Excluded(own), Bound::Included(other)) => Bound::Excluded(own + other),
(Bound::Excluded(own), Bound::Excluded(other)) => Bound::Excluded(own + other + 1),
};
let end = match (self.range.end_bound(), range.end_bound()) {
(Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded,
(Bound::Excluded(bound), Bound::Unbounded) => Bound::Excluded(*bound),
(Bound::Unbounded, Bound::Excluded(bound)) => {
Bound::Excluded(self.end_index() - *bound)
}
(Bound::Included(bound), Bound::Unbounded) => Bound::Included(*bound),
(Bound::Unbounded, Bound::Included(bound)) => {
Bound::Included(self.end_index() - *bound)
}
(Bound::Included(own), Bound::Included(other)) => {
Bound::Included(min(*own, self.start_index() + other))
}
(Bound::Included(own), Bound::Excluded(other)) => {
Bound::Included(min(*own, self.start_index() + other - 1))
}
(Bound::Excluded(own), Bound::Included(other)) => {
Bound::Included(min(*own - 1, self.start_index() + other))
}
(Bound::Excluded(own), Bound::Excluded(other)) => {
Bound::Excluded(min(*own, self.start_index() + other))
}
};
(start, end)
}
} }
impl<'a, I> HasMutatorBytes for BytesSubInput<'a, I> impl<'a, I> HasMutatorBytes for BytesSubInput<'a, I>
where where
I: HasMutatorBytes + HasLen, I: HasMutatorBytes,
{ {
#[inline] #[inline]
fn bytes(&self) -> &[u8] { fn bytes(&self) -> &[u8] {
@ -188,8 +110,8 @@ where
} }
fn resize(&mut self, new_len: usize, value: u8) { fn resize(&mut self, new_len: usize, value: u8) {
let start_index = self.start_index(); let start_index = self.range.start;
let end_index = self.end_index(); let end_index = self.range.end;
let old_len = end_index - start_index; let old_len = end_index - start_index;
match new_len.cmp(&old_len) { match new_len.cmp(&old_len) {
@ -238,7 +160,7 @@ where
} }
fn extend<'b, IT: IntoIterator<Item = &'b u8>>(&mut self, iter: IT) { fn extend<'b, IT: IntoIterator<Item = &'b u8>>(&mut self, iter: IT) {
let old_len = self.end_index() - self.start_index(); let old_len = self.len();
let new_values: Vec<u8> = iter.into_iter().copied().collect(); let new_values: Vec<u8> = iter.into_iter().copied().collect();
self.resize(old_len + new_values.len(), 0); self.resize(old_len + new_values.len(), 0);
@ -249,24 +171,21 @@ where
/// with the given `replace_with` iterator and yields the removed items. /// with the given `replace_with` iterator and yields the removed items.
/// `replace_with` does not need to be the same length as range. /// `replace_with` does not need to be the same length as range.
/// Refer to the docs of [`Vec::splice`] /// Refer to the docs of [`Vec::splice`]
fn splice<R2, IT>( fn splice<R2, IT>(&mut self, range: R2, replace_with: IT) -> vec::Splice<'_, IT::IntoIter>
&mut self,
range: R2,
replace_with: IT,
) -> alloc::vec::Splice<'_, IT::IntoIter>
where where
R2: RangeBounds<usize>, R2: RangeBounds<usize>,
IT: IntoIterator<Item = u8>, IT: IntoIterator<Item = u8>,
{ {
let range = self.sub_range(range); let range = sub_range(&self.range, range);
self.parent_input.splice(range, replace_with) self.parent_input.splice(range, replace_with)
} }
fn drain<R2>(&mut self, range: R2) -> alloc::vec::Drain<'_, u8> fn drain<R2>(&mut self, range: R2) -> vec::Drain<'_, u8>
where where
R2: RangeBounds<usize>, R2: RangeBounds<usize>,
{ {
let drain = self.parent_input.drain(self.sub_range(range)); let sub_range = sub_range(&self.range, range);
let drain = self.parent_input.drain(sub_range);
self.range.end -= drain.len(); self.range.end -= drain.len();
drain drain
} }
@ -274,11 +193,11 @@ where
impl<'a, I> HasLen for BytesSubInput<'a, I> impl<'a, I> HasLen for BytesSubInput<'a, I>
where where
I: HasMutatorBytes + HasLen, I: HasMutatorBytes,
{ {
#[inline] #[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.range.end - self.range.start self.range.len()
} }
} }
@ -303,6 +222,20 @@ mod tests {
#[test] #[test]
fn test_bytessubinput() { fn test_bytessubinput() {
let (bytes_input, _) = init_bytes_input();
let sub_input = bytes_input.sub_bytes(0..1);
assert_eq!(*sub_input.as_slice(), [1]);
let sub_input = bytes_input.sub_bytes(1..=2);
assert_eq!(*sub_input.as_slice(), [2, 3]);
let sub_input = bytes_input.sub_bytes(..);
assert_eq!(*sub_input.as_slice(), [1, 2, 3, 4, 5, 6, 7]);
}
#[test]
fn test_mutablebytessubinput() {
let (mut bytes_input, len_orig) = init_bytes_input(); let (mut bytes_input, len_orig) = init_bytes_input();
let mut sub_input = bytes_input.sub_input(0..1); let mut sub_input = bytes_input.sub_input(0..1);
@ -413,14 +346,27 @@ mod tests {
#[test] #[test]
fn test_ranges() { fn test_ranges() {
let bytes_input = BytesInput::new(vec![1, 2, 3]);
assert_eq!(bytes_input.sub_bytes(..1).start_index(), 0);
assert_eq!(bytes_input.sub_bytes(1..=1).start_index(), 1);
assert_eq!(bytes_input.sub_bytes(..1).end_index(), 1);
assert_eq!(bytes_input.sub_bytes(..=1).end_index(), 2);
assert_eq!(bytes_input.sub_bytes(1..=1).end_index(), 2);
assert_eq!(bytes_input.sub_bytes(1..).end_index(), 3);
assert_eq!(bytes_input.sub_bytes(..3).end_index(), 3);
}
#[test]
fn test_ranges_mut() {
let mut bytes_input = BytesInput::new(vec![1, 2, 3]); let mut bytes_input = BytesInput::new(vec![1, 2, 3]);
assert_eq!(bytes_input.sub_input(..1).start_index(), 0); assert_eq!(bytes_input.sub_bytes_mut(..1).start_index(), 0);
assert_eq!(bytes_input.sub_input(1..=1).start_index(), 1); assert_eq!(bytes_input.sub_bytes_mut(1..=1).start_index(), 1);
assert_eq!(bytes_input.sub_input(..1).end_index(), 1); assert_eq!(bytes_input.sub_bytes_mut(..1).end_index(), 1);
assert_eq!(bytes_input.sub_input(..=1).end_index(), 2); assert_eq!(bytes_input.sub_bytes_mut(..=1).end_index(), 2);
assert_eq!(bytes_input.sub_input(1..=1).end_index(), 2); assert_eq!(bytes_input.sub_bytes_mut(1..=1).end_index(), 2);
assert_eq!(bytes_input.sub_input(1..).end_index(), 3); assert_eq!(bytes_input.sub_bytes_mut(1..).end_index(), 3);
assert_eq!(bytes_input.sub_input(..3).end_index(), 3); assert_eq!(bytes_input.sub_bytes_mut(..3).end_index(), 3);
} }
} }

View File

@ -34,7 +34,11 @@ use std::{fs::File, hash::Hash, io::Read, path::Path};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use libafl_bolts::fs::write_file_atomic; use libafl_bolts::fs::write_file_atomic;
use libafl_bolts::{ownedref::OwnedSlice, Error, HasLen}; use libafl_bolts::{
ownedref::{OwnedMutSlice, OwnedSlice},
subrange::{SubRangeMutSlice, SubRangeSlice},
Error, HasLen,
};
#[cfg(feature = "nautilus")] #[cfg(feature = "nautilus")]
pub use nautilus::*; pub use nautilus::*;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -123,7 +127,14 @@ impl HasTargetBytes for NopInput {
} }
} }
impl HasLen for NopInput {
fn len(&self) -> usize {
0
}
}
// TODO change this to fn target_bytes(&self, buffer: &mut Vec<u8>) -> &[u8]; // TODO change this to fn target_bytes(&self, buffer: &mut Vec<u8>) -> &[u8];
/// Has a byte representation intended for the target.
/// Can be represented with a vector of bytes. /// Can be represented with a vector of bytes.
/// This representation is not necessarily deserializable. /// This representation is not necessarily deserializable.
/// Instead, it can be used as bytes input for a target /// Instead, it can be used as bytes input for a target
@ -132,7 +143,7 @@ pub trait HasTargetBytes {
fn target_bytes(&self) -> OwnedSlice<u8>; fn target_bytes(&self) -> OwnedSlice<u8>;
} }
/// Contains mutateable and resizable bytes /// Contains mutable and resizable bytes
pub trait HasMutatorBytes: HasLen { pub trait HasMutatorBytes: HasLen {
/// The bytes /// The bytes
fn bytes(&self) -> &[u8]; fn bytes(&self) -> &[u8];
@ -142,23 +153,39 @@ pub trait HasMutatorBytes: HasLen {
/// Resize the mutator bytes to a given new size. /// Resize the mutator bytes to a given new size.
/// Use `value` to fill new slots in case the buffer grows. /// Use `value` to fill new slots in case the buffer grows.
/// See [`alloc::vec::Vec::splice`]. /// See [`Vec::splice`].
fn resize(&mut self, new_len: usize, value: u8); fn resize(&mut self, new_len: usize, value: u8);
/// Extends the given buffer with an iterator. See [`alloc::vec::Vec::extend`] /// Extends the given buffer with an iterator. See [`alloc::vec::Vec::extend`]
fn extend<'a, I: IntoIterator<Item = &'a u8>>(&mut self, iter: I); fn extend<'a, I: IntoIterator<Item = &'a u8>>(&mut self, iter: I);
/// Splices the given target bytes according to [`alloc::vec::Vec::splice`]'s rules /// Splices the given target bytes according to [`Vec::splice`]'s rules
fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter> fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
where where
R: RangeBounds<usize>, R: RangeBounds<usize>,
I: IntoIterator<Item = u8>; I: IntoIterator<Item = u8>;
/// Drains the given target bytes according to [`alloc::vec::Vec::drain`]'s rules /// Drains the given target bytes according to [`Vec::drain`]'s rules
fn drain<R>(&mut self, range: R) -> Drain<'_, u8> fn drain<R>(&mut self, range: R) -> Drain<'_, u8>
where where
R: RangeBounds<usize>; R: RangeBounds<usize>;
/// Creates a [`SubRangeSlice`] from this input, that can be used to slice a byte array.
fn sub_bytes<R>(&self, range: R) -> SubRangeSlice<u8>
where
R: RangeBounds<usize>,
{
SubRangeSlice::new(OwnedSlice::from(self.bytes()), range)
}
/// Creates a [`SubRangeMutSlice`] from this input, that can be used to slice a byte array.
fn sub_bytes_mut<R>(&mut self, range: R) -> SubRangeMutSlice<u8>
where
R: RangeBounds<usize>,
{
SubRangeMutSlice::new(OwnedMutSlice::from(self.bytes_mut()), range)
}
/// Creates a [`BytesSubInput`] from this input, that can be used for local mutations. /// Creates a [`BytesSubInput`] from this input, that can be used for local mutations.
fn sub_input<R>(&mut self, range: R) -> BytesSubInput<Self> fn sub_input<R>(&mut self, range: R) -> BytesSubInput<Self>
where where

View File

@ -131,6 +131,8 @@ pub mod serdeany;
pub mod shmem; pub mod shmem;
#[cfg(feature = "std")] #[cfg(feature = "std")]
pub mod staterestore; pub mod staterestore;
#[cfg(feature = "alloc")]
pub mod subrange;
// TODO: reenable once ahash works in no-alloc // TODO: reenable once ahash works in no-alloc
#[cfg(any(feature = "xxh3", feature = "alloc"))] #[cfg(any(feature = "xxh3", feature = "alloc"))]
pub mod tuples; pub mod tuples;
@ -309,7 +311,7 @@ pub enum Error {
Unsupported(String, ErrorBacktrace), Unsupported(String, ErrorBacktrace),
/// Shutting down, not really an error. /// Shutting down, not really an error.
ShuttingDown, ShuttingDown,
/// OS error, wrapping a [`std::io::Error`] /// OS error, wrapping a [`io::Error`]
#[cfg(feature = "std")] #[cfg(feature = "std")]
OsError(io::Error, String, ErrorBacktrace), OsError(io::Error, String, ErrorBacktrace),
/// Something else happened /// Something else happened
@ -411,7 +413,7 @@ impl Error {
{ {
Error::OsError(err, msg.into(), ErrorBacktrace::new()) Error::OsError(err, msg.into(), ErrorBacktrace::new())
} }
/// OS error from [`std::io::Error::last_os_error`] with additional message /// OS error from [`io::Error::last_os_error`] with additional message
#[cfg(feature = "std")] #[cfg(feature = "std")]
#[must_use] #[must_use]
pub fn last_os_error<S>(msg: S) -> Self pub fn last_os_error<S>(msg: S) -> Self

View File

@ -9,8 +9,9 @@ use alloc::{
use core::{ use core::{
clone::Clone, clone::Clone,
fmt::Debug, fmt::Debug,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut, RangeBounds},
slice, slice,
slice::SliceIndex,
}; };
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@ -457,6 +458,17 @@ impl<'a, T> OwnedSlice<'a, T> {
pub fn iter(&self) -> Iter<'_, T> { pub fn iter(&self) -> Iter<'_, T> {
<&Self as IntoIterator>::into_iter(self) <&Self as IntoIterator>::into_iter(self)
} }
/// Returns a subslice of the slice.
#[must_use]
pub fn slice<R: RangeBounds<usize> + SliceIndex<[T], Output = [T]>>(
&'a self,
range: R,
) -> OwnedSlice<'a, T> {
OwnedSlice {
inner: OwnedSliceInner::Ref(&self[range]),
}
}
} }
impl<'a, 'it, T> IntoIterator for &'it OwnedSlice<'a, T> { impl<'a, 'it, T> IntoIterator for &'it OwnedSlice<'a, T> {

View File

@ -1,10 +1,8 @@
//! The random number generators of `LibAFL` //! The random number generators of `LibAFL`
use core::{ #[cfg(target_has_atomic = "ptr")]
debug_assert, use core::sync::atomic::Ordering;
fmt::Debug, use core::{debug_assert, fmt::Debug, sync::atomic::AtomicUsize};
sync::atomic::{AtomicUsize, Ordering},
};
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};

View File

@ -0,0 +1,375 @@
//! Subrange of things.
//! Convenient wrappers to handle sub-slices efficiently.
use core::{
cmp::min,
ops::{Bound, Range, RangeBounds},
};
use crate::{
ownedref::{OwnedMutSlice, OwnedSlice},
HasLen,
};
/// An immutable contiguous subslice of a byte slice.
/// It is mostly useful to cheaply wrap a subslice of a given input.
///
/// A mutable version is available: [`SubRangeMutSlice`].
#[derive(Debug)]
pub struct SubRangeSlice<'a, T> {
/// The (complete) parent input we will work on
parent_slice: OwnedSlice<'a, T>,
/// The range inside the parent input we will work on
range: Range<usize>,
}
/// A mutable contiguous subslice of a byte slice.
/// It is mostly useful to cheaply wrap a subslice of a given input.
///
/// An immutable version is available: [`SubRangeSlice`].
#[derive(Debug)]
pub struct SubRangeMutSlice<'a, T> {
/// The (complete) parent input we will work on
parent_slice: OwnedMutSlice<'a, T>,
/// The range inside the parent input we will work on
range: Range<usize>,
}
/// Slice wrapper keeping track of the current read position.
/// Convenient wrapper when the slice must be split in multiple sub-slices and read sequentially.
#[derive(Debug)]
pub struct SliceReader<'a, T> {
parent_slice: &'a [T],
pos: usize,
}
impl<'a, T> SliceReader<'a, T> {
/// Create a new [`SliceReader`].
/// The position of the reader is initialized to 0.
#[must_use]
pub fn new(parent_slice: &'a [T]) -> Self {
Self {
parent_slice,
pos: 0,
}
}
/// Read an immutable sub-slice from the parent slice, from the current cursor position up to `limit` elements.
/// If the resulting slice would go beyond the end of the parent slice, it will be truncated to the length of the parent slice.
/// This function does not provide any feedback on whether the slice was cropped or not.
#[must_use]
pub fn next_sub_slice_truncated(&mut self, limit: usize) -> SubRangeSlice<'a, T> {
let sub_slice = SubRangeSlice::with_slice(self.parent_slice, self.pos..(self.pos + limit));
self.pos += sub_slice.len();
sub_slice
}
/// Read an immutable sub-slice from the parent slice, from the current cursor position up to `limit` bytes.
/// If the resulting slice would go beyond the end of the parent slice, it will be limited to the length of the parent slice.
/// The function returns
/// - `Ok(Slice)` if the returned slice has `limit` elements.
/// - `Err(Partial(slice))` if the returned slice has strictly less than `limit` elements and is not empty.
/// - `Err(Empty)` if the reader was already at the end or `limit` equals zero.
pub fn next_sub_input(
&mut self,
limit: usize,
) -> Result<SubRangeSlice<'a, T>, PartialSubRangeSlice<'a, T>> {
let slice_to_return = self.next_sub_slice_truncated(limit);
let real_len = slice_to_return.len();
if real_len == 0 {
Err(PartialSubRangeSlice::Empty)
} else if real_len < limit {
Err(PartialSubRangeSlice::Partial(slice_to_return))
} else {
Ok(slice_to_return)
}
}
}
impl<'a, T> From<&'a [T]> for SliceReader<'a, T> {
fn from(input: &'a [T]) -> Self {
Self::new(input)
}
}
impl<'a, T> HasLen for SubRangeSlice<'a, T> {
#[inline]
fn len(&self) -> usize {
self.range.len()
}
}
impl<'a, T> HasLen for SubRangeMutSlice<'a, T> {
#[inline]
fn len(&self) -> usize {
self.range.len()
}
}
/// Gets the relevant concrete start index from [`RangeBounds`] (inclusive)
pub fn start_index<R>(range: &R) -> usize
where
R: RangeBounds<usize>,
{
match range.start_bound() {
Bound::Unbounded => 0,
Bound::Included(start) => *start,
Bound::Excluded(start) => start + 1,
}
}
/// Gets the relevant concrete end index from [`RangeBounds`] (exclusive)
pub fn end_index<R>(range: &R, max_len: usize) -> usize
where
R: RangeBounds<usize>,
{
let end = match range.end_bound() {
Bound::Unbounded => max_len,
Bound::Included(end) => end + 1,
Bound::Excluded(end) => *end,
};
min(end, max_len)
}
/// Gets the relevant subrange of a [`Range<usize>`] from [`RangeBounds`].
pub fn sub_range<R>(outer_range: &Range<usize>, inner_range: R) -> (Bound<usize>, Bound<usize>)
where
R: RangeBounds<usize>,
{
let start =
match (outer_range.start_bound(), inner_range.start_bound()) {
(Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded,
(Bound::Excluded(bound), Bound::Unbounded)
| (Bound::Unbounded, Bound::Excluded(bound)) => Bound::Excluded(*bound),
(Bound::Included(bound), Bound::Unbounded)
| (Bound::Unbounded, Bound::Included(bound)) => Bound::Included(*bound),
(Bound::Included(own), Bound::Included(other)) => Bound::Included(own + other),
(Bound::Included(own), Bound::Excluded(other))
| (Bound::Excluded(own), Bound::Included(other)) => Bound::Excluded(own + other),
(Bound::Excluded(own), Bound::Excluded(other)) => Bound::Excluded(own + other + 1),
};
let end = match (outer_range.end_bound(), inner_range.end_bound()) {
(Bound::Unbounded, Bound::Unbounded) => Bound::Unbounded,
(Bound::Excluded(bound), Bound::Unbounded) => Bound::Excluded(*bound),
(Bound::Unbounded, Bound::Excluded(bound)) => Bound::Excluded(outer_range.end - *bound),
(Bound::Included(bound), Bound::Unbounded) => Bound::Included(*bound),
(Bound::Unbounded, Bound::Included(bound)) => Bound::Included(outer_range.end - *bound),
(Bound::Included(own), Bound::Included(other)) => {
Bound::Included(min(*own, outer_range.start + other))
}
(Bound::Included(own), Bound::Excluded(other)) => {
Bound::Included(min(*own, outer_range.start + other - 1))
}
(Bound::Excluded(own), Bound::Included(other)) => {
Bound::Included(min(*own - 1, outer_range.start + other))
}
(Bound::Excluded(own), Bound::Excluded(other)) => {
Bound::Excluded(min(*own, outer_range.start + other))
}
};
(start, end)
}
/// Representation of a partial slice
/// This is used when providing a slice smaller than the expected one.
/// It notably happens when trying to read the end of an input.
#[derive(Debug)]
pub enum PartialSubRangeSlice<'a, T> {
/// The slice is empty, and thus not kept
Empty,
/// The slice is strictly smaller than the expected one.
Partial(SubRangeSlice<'a, T>),
}
impl<'a, T> PartialSubRangeSlice<'a, T> {
/// Consumes `PartialBytesSubInput` and returns true if it was empty, false otherwise.
#[must_use]
pub fn empty(self) -> bool {
matches!(self, PartialSubRangeSlice::Empty)
}
/// Consumes `PartialBytesSubInput` and returns the partial slice if it was a partial slice, None otherwise.
#[must_use]
pub fn partial(self) -> Option<SubRangeSlice<'a, T>> {
#[allow(clippy::match_wildcard_for_single_variants)]
match self {
PartialSubRangeSlice::Partial(partial_slice) => Some(partial_slice),
_ => None,
}
}
}
impl<'a, T> SubRangeSlice<'a, T> {
/// Creates a new [`SubRangeSlice`], a sub-slice representation of a byte array.
pub fn new<R>(parent_slice: OwnedSlice<'a, T>, range: R) -> Self
where
R: RangeBounds<usize>,
{
let parent_len = parent_slice.len();
SubRangeSlice {
parent_slice,
range: Range {
start: start_index(&range),
end: end_index(&range, parent_len),
},
}
}
/// Get the sub slice as bytes.
#[must_use]
pub fn as_slice(&self) -> &[T] {
&self.parent_slice[self.range.clone()]
}
/// Creates a new [`SubRangeSlice`] that's a sliced view on a bytes slice.
pub fn with_slice<R>(parent_slice: &'a [T], range: R) -> Self
where
R: RangeBounds<usize>,
{
Self::new(parent_slice.into(), range)
}
/// The parent input
#[must_use]
pub fn parent_slice(self) -> OwnedSlice<'a, T> {
self.parent_slice
}
/// The inclusive start index in the parent buffer
#[must_use]
pub fn start_index(&self) -> usize {
self.range.start
}
/// The exclusive end index in the parent buffer
#[must_use]
pub fn end_index(&self) -> usize {
self.range.end
}
/// Creates a sub range in the current own range
pub fn sub_range<R>(&self, range: R) -> (Bound<usize>, Bound<usize>)
where
R: RangeBounds<usize>,
{
sub_range(&self.range, range)
}
}
impl<'a, T> SubRangeMutSlice<'a, T> {
/// Creates a new [`SubRangeMutSlice`], a sub-slice representation of a byte array.
pub fn new<R>(parent_slice: OwnedMutSlice<'a, T>, range: R) -> Self
where
R: RangeBounds<usize>,
{
let parent_len = parent_slice.len();
SubRangeMutSlice {
parent_slice,
range: Range {
start: start_index(&range),
end: end_index(&range, parent_len),
},
}
}
/// Get the sub slice as bytes.
#[must_use]
pub fn as_slice(&self) -> &[T] {
&self.parent_slice[self.range.clone()]
}
/// Get the sub slice as bytes.
#[must_use]
pub fn as_slice_mut(&mut self) -> &mut [T] {
&mut self.parent_slice[self.range.clone()]
}
/// Creates a new [`SubRangeMutSlice`] that's a view on a bytes slice.
/// The sub-slice can then be used to mutate parts of the original bytes.
pub fn with_slice<R>(parent_slice: &'a mut [T], range: R) -> Self
where
R: RangeBounds<usize>,
{
Self::new(parent_slice.into(), range)
}
/// The parent input
#[must_use]
pub fn parent_slice(self) -> OwnedMutSlice<'a, T> {
self.parent_slice
}
/// The inclusive start index in the parent buffer
#[must_use]
pub fn start_index(&self) -> usize {
self.range.start
}
/// The exclusive end index in the parent buffer
#[must_use]
pub fn end_index(&self) -> usize {
self.range.end
}
/// Creates a sub range in the current own range
pub fn sub_range<R>(&self, range: R) -> (Bound<usize>, Bound<usize>)
where
R: RangeBounds<usize>,
{
sub_range(&self.range, range)
}
}
#[cfg(test)]
mod tests {
use super::SliceReader;
#[test]
fn test_bytesreader_toslice_unchecked() {
let bytes_input = vec![1, 2, 3, 4, 5, 6, 7];
let mut bytes_reader = SliceReader::new(&bytes_input);
let bytes_read = bytes_reader.next_sub_slice_truncated(2);
assert_eq!(*bytes_read.as_slice(), [1, 2]);
let bytes_read = bytes_reader.next_sub_slice_truncated(3);
assert_eq!(*bytes_read.as_slice(), [3, 4, 5]);
let bytes_read = bytes_reader.next_sub_slice_truncated(8);
assert_eq!(*bytes_read.as_slice(), [6, 7]);
let bytes_read = bytes_reader.next_sub_slice_truncated(8);
let bytes_read_ref: &[u8] = &[];
assert_eq!(&*bytes_read.as_slice(), bytes_read_ref);
}
#[test]
fn test_bytesreader_toslice() {
let bytes_input = vec![1, 2, 3, 4, 5, 6, 7];
let mut bytes_reader = SliceReader::new(&bytes_input);
let bytes_read = bytes_reader.next_sub_input(2);
assert_eq!(*bytes_read.unwrap().as_slice(), [1, 2]);
let bytes_read = bytes_reader.next_sub_input(3);
assert_eq!(*bytes_read.unwrap().as_slice(), [3, 4, 5]);
let bytes_read = bytes_reader.next_sub_input(8);
assert_eq!(
*bytes_read.unwrap_err().partial().unwrap().as_slice(),
[6, 7]
);
let bytes_read = bytes_reader.next_sub_input(8);
assert!(bytes_read.unwrap_err().empty());
}
}

View File

@ -15,7 +15,7 @@ fn criterion_benchmark(c: &mut Criterion) {
} }
c.bench_function("xxh3", |b| { c.bench_function("xxh3", |b| {
b.iter(|| xxh3::xxh3_64_with_seed(black_box(&bench_vec), 0)); b.iter(|| black_box(xxh3::xxh3_64_with_seed(&bench_vec, 0)));
}); });
/*c.bench_function("const_xxh3", |b| { /*c.bench_function("const_xxh3", |b| {
b.iter(|| const_xxh3::xxh3_64_with_seed(black_box(&bench_vec), 0)) b.iter(|| const_xxh3::xxh3_64_with_seed(black_box(&bench_vec), 0))
@ -29,7 +29,7 @@ fn criterion_benchmark(c: &mut Criterion) {
}); });
c.bench_function("fxhash", |b| { c.bench_function("fxhash", |b| {
b.iter(|| { b.iter(|| {
let mut hasher = black_box(rustc_hash::FxHasher::default()); let mut hasher = rustc_hash::FxHasher::default();
hasher.write(black_box(&bench_vec)); hasher.write(black_box(&bench_vec));
black_box(hasher.finish()); black_box(hasher.finish());
}); });