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:
parent
211809dddb
commit
c319fe2033
@ -1,38 +1,17 @@
|
||||
//! [`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::{
|
||||
cmp::{min, Ordering},
|
||||
ops::{Bound, Range, RangeBounds},
|
||||
cmp::Ordering,
|
||||
ops::{Range, RangeBounds},
|
||||
};
|
||||
|
||||
use libafl_bolts::HasLen;
|
||||
use libafl_bolts::{
|
||||
subrange::{end_index, start_index, sub_range},
|
||||
HasLen,
|
||||
};
|
||||
|
||||
use super::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,
|
||||
}
|
||||
}
|
||||
use crate::inputs::HasMutatorBytes;
|
||||
|
||||
/// 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.
|
||||
@ -85,12 +64,9 @@ where
|
||||
/// 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)]
|
||||
pub struct BytesSubInput<'a, I>
|
||||
where
|
||||
I: HasMutatorBytes + ?Sized,
|
||||
{
|
||||
pub struct BytesSubInput<'a, I: ?Sized> {
|
||||
/// The (complete) parent input we will work on
|
||||
pub(crate) parent_input: &'a mut I,
|
||||
/// The range inside the parent input we will work on
|
||||
@ -99,7 +75,7 @@ where
|
||||
|
||||
impl<'a, I> BytesSubInput<'a, I>
|
||||
where
|
||||
I: HasMutatorBytes + ?Sized + HasLen,
|
||||
I: HasMutatorBytes + ?Sized,
|
||||
{
|
||||
/// 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.
|
||||
@ -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>
|
||||
where
|
||||
I: HasMutatorBytes + HasLen,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
#[inline]
|
||||
fn bytes(&self) -> &[u8] {
|
||||
@ -188,8 +110,8 @@ where
|
||||
}
|
||||
|
||||
fn resize(&mut self, new_len: usize, value: u8) {
|
||||
let start_index = self.start_index();
|
||||
let end_index = self.end_index();
|
||||
let start_index = self.range.start;
|
||||
let end_index = self.range.end;
|
||||
let old_len = end_index - start_index;
|
||||
|
||||
match new_len.cmp(&old_len) {
|
||||
@ -238,7 +160,7 @@ where
|
||||
}
|
||||
|
||||
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();
|
||||
self.resize(old_len + new_values.len(), 0);
|
||||
@ -249,24 +171,21 @@ where
|
||||
/// with the given `replace_with` iterator and yields the removed items.
|
||||
/// `replace_with` does not need to be the same length as range.
|
||||
/// Refer to the docs of [`Vec::splice`]
|
||||
fn splice<R2, IT>(
|
||||
&mut self,
|
||||
range: R2,
|
||||
replace_with: IT,
|
||||
) -> alloc::vec::Splice<'_, IT::IntoIter>
|
||||
fn splice<R2, IT>(&mut self, range: R2, replace_with: IT) -> vec::Splice<'_, IT::IntoIter>
|
||||
where
|
||||
R2: RangeBounds<usize>,
|
||||
IT: IntoIterator<Item = u8>,
|
||||
{
|
||||
let range = self.sub_range(range);
|
||||
let range = sub_range(&self.range, range);
|
||||
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
|
||||
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();
|
||||
drain
|
||||
}
|
||||
@ -274,11 +193,11 @@ where
|
||||
|
||||
impl<'a, I> HasLen for BytesSubInput<'a, I>
|
||||
where
|
||||
I: HasMutatorBytes + HasLen,
|
||||
I: HasMutatorBytes,
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.range.end - self.range.start
|
||||
self.range.len()
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,6 +222,20 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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 sub_input = bytes_input.sub_input(0..1);
|
||||
@ -413,14 +346,27 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
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]);
|
||||
|
||||
assert_eq!(bytes_input.sub_input(..1).start_index(), 0);
|
||||
assert_eq!(bytes_input.sub_input(1..=1).start_index(), 1);
|
||||
assert_eq!(bytes_input.sub_input(..1).end_index(), 1);
|
||||
assert_eq!(bytes_input.sub_input(..=1).end_index(), 2);
|
||||
assert_eq!(bytes_input.sub_input(1..=1).end_index(), 2);
|
||||
assert_eq!(bytes_input.sub_input(1..).end_index(), 3);
|
||||
assert_eq!(bytes_input.sub_input(..3).end_index(), 3);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(..1).start_index(), 0);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(1..=1).start_index(), 1);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(..1).end_index(), 1);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(..=1).end_index(), 2);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(1..=1).end_index(), 2);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(1..).end_index(), 3);
|
||||
assert_eq!(bytes_input.sub_bytes_mut(..3).end_index(), 3);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ use std::{fs::File, hash::Hash, io::Read, path::Path};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
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")]
|
||||
pub use nautilus::*;
|
||||
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];
|
||||
/// Has a byte representation intended for the target.
|
||||
/// Can be represented with a vector of bytes.
|
||||
/// This representation is not necessarily deserializable.
|
||||
/// Instead, it can be used as bytes input for a target
|
||||
@ -132,7 +143,7 @@ pub trait HasTargetBytes {
|
||||
fn target_bytes(&self) -> OwnedSlice<u8>;
|
||||
}
|
||||
|
||||
/// Contains mutateable and resizable bytes
|
||||
/// Contains mutable and resizable bytes
|
||||
pub trait HasMutatorBytes: HasLen {
|
||||
/// The bytes
|
||||
fn bytes(&self) -> &[u8];
|
||||
@ -142,23 +153,39 @@ pub trait HasMutatorBytes: HasLen {
|
||||
|
||||
/// Resize the mutator bytes to a given new size.
|
||||
/// 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);
|
||||
|
||||
/// Extends the given buffer with an iterator. See [`alloc::vec::Vec::extend`]
|
||||
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>
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
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>
|
||||
where
|
||||
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.
|
||||
fn sub_input<R>(&mut self, range: R) -> BytesSubInput<Self>
|
||||
where
|
||||
|
@ -131,6 +131,8 @@ pub mod serdeany;
|
||||
pub mod shmem;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod staterestore;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub mod subrange;
|
||||
// TODO: reenable once ahash works in no-alloc
|
||||
#[cfg(any(feature = "xxh3", feature = "alloc"))]
|
||||
pub mod tuples;
|
||||
@ -309,7 +311,7 @@ pub enum Error {
|
||||
Unsupported(String, ErrorBacktrace),
|
||||
/// Shutting down, not really an error.
|
||||
ShuttingDown,
|
||||
/// OS error, wrapping a [`std::io::Error`]
|
||||
/// OS error, wrapping a [`io::Error`]
|
||||
#[cfg(feature = "std")]
|
||||
OsError(io::Error, String, ErrorBacktrace),
|
||||
/// Something else happened
|
||||
@ -411,7 +413,7 @@ impl Error {
|
||||
{
|
||||
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")]
|
||||
#[must_use]
|
||||
pub fn last_os_error<S>(msg: S) -> Self
|
||||
|
@ -9,8 +9,9 @@ use alloc::{
|
||||
use core::{
|
||||
clone::Clone,
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
ops::{Deref, DerefMut, RangeBounds},
|
||||
slice,
|
||||
slice::SliceIndex,
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
@ -457,6 +458,17 @@ impl<'a, T> OwnedSlice<'a, T> {
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
<&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> {
|
||||
|
@ -1,10 +1,8 @@
|
||||
//! The random number generators of `LibAFL`
|
||||
|
||||
use core::{
|
||||
debug_assert,
|
||||
fmt::Debug,
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
#[cfg(target_has_atomic = "ptr")]
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::{debug_assert, fmt::Debug, sync::atomic::AtomicUsize};
|
||||
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
|
375
libafl_bolts/src/subrange.rs
Normal file
375
libafl_bolts/src/subrange.rs
Normal 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());
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ fn criterion_benchmark(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
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| {
|
||||
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| {
|
||||
b.iter(|| {
|
||||
let mut hasher = black_box(rustc_hash::FxHasher::default());
|
||||
let mut hasher = rustc_hash::FxHasher::default();
|
||||
hasher.write(black_box(&bench_vec));
|
||||
black_box(hasher.finish());
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user