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
|
//! [`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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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> {
|
||||||
|
@ -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};
|
||||||
|
|
||||||
|
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| {
|
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());
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user