Improve map feedback/observer (#665)
* improve * a * fix it back * more * NO * try * fix python * more * specialize map map feedback with u8 * more * fmt * usable_count = len * clp * restore iterator based map feedback * simd specialization * optimize hitcounts * fix hitcounts * no_std * moar unsafe * fix * clippy * clippy * opt non-specialized is_interesting * fmt * op post_exec * cleanup * even more * allow Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
parent
986030732a
commit
c9f802a3b8
@ -197,7 +197,8 @@ pub fn main() {
|
||||
state.add_metadata(tokens);
|
||||
|
||||
// Setup a mutational stage with a basic bytes mutator
|
||||
let mutator = StdScheduledMutator::new(havoc_mutations().merge(tokens_mutations()));
|
||||
let mutator =
|
||||
StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6);
|
||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||
|
||||
fuzzer
|
||||
|
@ -12,7 +12,7 @@ edition = "2021"
|
||||
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||
|
||||
[features]
|
||||
default = ["std", "derive", "llmp_compression", "rand_trait", "fork"]
|
||||
default = ["std", "derive", "llmp_compression", "rand_trait", "fork", "introspection"]
|
||||
std = ["serde_json", "serde_json/std", "hostname", "nix", "serde/std", "bincode", "wait-timeout", "regex", "byteorder", "once_cell", "uuid", "tui_monitor", "ctor", "backtrace", "num_cpus"] # print, env, launcher ... support
|
||||
derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
|
||||
fork = [] # uses the fork() syscall to spawn children, instead of launching a new command, if supported by the OS (has no effect on Windows, no_std).
|
||||
|
@ -308,7 +308,10 @@ where
|
||||
|
||||
/// Reset the map
|
||||
pub fn reset(&mut self) -> Result<(), Error> {
|
||||
self.history_map.iter_mut().for_each(|x| *x = T::default());
|
||||
let cnt = self.history_map.len();
|
||||
for i in 0..cnt {
|
||||
self.history_map[i] = T::default();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -353,79 +356,36 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting<EM, OT>(
|
||||
#[rustversion::nightly]
|
||||
default fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
_input: &I,
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let mut interesting = false;
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers.match_name::<O>(&self.observer_name).unwrap();
|
||||
let size = observer.usable_count();
|
||||
let initial = observer.initial();
|
||||
|
||||
let map_state = state
|
||||
.named_metadata_mut()
|
||||
.get_mut::<MapFeedbackMetadata<T>>(&self.name)
|
||||
.unwrap();
|
||||
if map_state.history_map.len() < observer.len() {
|
||||
map_state.history_map.resize(observer.len(), T::default());
|
||||
self.is_interesting_default(state, manager, input, observers, exit_kind)
|
||||
}
|
||||
|
||||
// assert!(size <= map_state.history_map.len(), "The size of the associated map observer cannot exceed the size of the history map of the feedback. If you are running multiple instances of slightly different fuzzers (e.g. one with ASan and another without) synchronized using LLMP please check the `configuration` field of the LLMP manager.");
|
||||
|
||||
assert!(size <= observer.len());
|
||||
|
||||
if self.novelties.is_some() {
|
||||
for (i, &item) in observer.as_ref_iter().enumerate() {
|
||||
let history = map_state.history_map[i];
|
||||
let reduced = R::reduce(history, item);
|
||||
if N::is_novel(history, reduced) {
|
||||
map_state.history_map[i] = reduced;
|
||||
interesting = true;
|
||||
self.novelties.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i, &item) in observer.as_ref_iter().enumerate() {
|
||||
let history = map_state.history_map[i];
|
||||
let reduced = R::reduce(history, item);
|
||||
if N::is_novel(history, reduced) {
|
||||
map_state.history_map[i] = reduced;
|
||||
interesting = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if interesting {
|
||||
let mut filled = 0;
|
||||
for i in 0..size {
|
||||
if map_state.history_map[i] != initial {
|
||||
filled += 1;
|
||||
if self.indexes.is_some() {
|
||||
self.indexes.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.fire(
|
||||
state,
|
||||
Event::UpdateUserStats {
|
||||
name: self.stats_name.to_string(),
|
||||
value: UserStats::Ratio(filled, size as u64),
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(interesting)
|
||||
#[rustversion::not(nightly)]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
input: &I,
|
||||
observers: &OT,
|
||||
exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
self.is_interesting_default(state, manager, input, observers, exit_kind)
|
||||
}
|
||||
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
|
||||
@ -452,6 +412,127 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Specialize for the common coverage map size, maximization of u8s
|
||||
#[rustversion::nightly]
|
||||
impl<I, O, S> Feedback<I, S> for MapFeedback<I, DifferentIsNovel, O, MaxReducer, S, u8>
|
||||
where
|
||||
O: MapObserver<Entry = u8> + AsSlice<u8>,
|
||||
for<'it> O: AsRefIterator<'it, Item = u8>,
|
||||
I: Input,
|
||||
S: HasNamedMetadata + HasClientPerfMonitor + Debug,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// 128 bits vectors
|
||||
type VectorType = core::simd::u8x16;
|
||||
|
||||
let mut interesting = false;
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers.match_name::<O>(&self.observer_name).unwrap();
|
||||
|
||||
let map_state = state
|
||||
.named_metadata_mut()
|
||||
.get_mut::<MapFeedbackMetadata<u8>>(&self.name)
|
||||
.unwrap();
|
||||
|
||||
let size = observer.usable_count();
|
||||
if map_state.history_map.len() < size {
|
||||
map_state.history_map.resize(size, u8::default());
|
||||
}
|
||||
|
||||
let map = observer.as_slice();
|
||||
debug_assert!(map.len() >= size);
|
||||
|
||||
let history_map = map_state.history_map.as_mut_slice();
|
||||
|
||||
// Non vector implementation for reference
|
||||
/*for (i, history) in history_map.iter_mut().enumerate() {
|
||||
let item = map[i];
|
||||
let reduced = MaxReducer::reduce(*history, item);
|
||||
if DifferentIsNovel::is_novel(*history, reduced) {
|
||||
*history = reduced;
|
||||
interesting = true;
|
||||
if self.novelties.is_some() {
|
||||
self.novelties.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
let steps = size / VectorType::LANES;
|
||||
let left = size % VectorType::LANES;
|
||||
|
||||
for step in 0..steps {
|
||||
let i = step * VectorType::LANES;
|
||||
let history = VectorType::from_slice(&history_map[i..]);
|
||||
let items = VectorType::from_slice(&map[i..]);
|
||||
|
||||
if items.max(history) != history {
|
||||
interesting = true;
|
||||
unsafe {
|
||||
for j in i..(i + VectorType::LANES) {
|
||||
let item = *map.get_unchecked(j);
|
||||
if item > *history_map.get_unchecked(j) {
|
||||
*history_map.get_unchecked_mut(j) = item;
|
||||
if self.novelties.is_some() {
|
||||
self.novelties.as_mut().unwrap().push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for j in (size - left)..size {
|
||||
unsafe {
|
||||
let item = *map.get_unchecked(j);
|
||||
if item > *history_map.get_unchecked(j) {
|
||||
interesting = true;
|
||||
*history_map.get_unchecked_mut(j) = item;
|
||||
if self.novelties.is_some() {
|
||||
self.novelties.as_mut().unwrap().push(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let initial = observer.initial();
|
||||
if interesting {
|
||||
let len = history_map.len();
|
||||
let mut filled = 0;
|
||||
for i in 0..len {
|
||||
if history_map[i] != initial {
|
||||
filled += 1;
|
||||
if self.indexes.is_some() {
|
||||
self.indexes.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.fire(
|
||||
state,
|
||||
Event::UpdateUserStats {
|
||||
name: self.stats_name.to_string(),
|
||||
value: UserStats::Ratio(filled, len as u64),
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(interesting)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, N, O, R, S, T> Named for MapFeedback<I, N, O, R, S, T>
|
||||
where
|
||||
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
|
||||
@ -490,10 +571,11 @@ impl<I, N, O, R, S, T> MapFeedback<I, N, O, R, S, T>
|
||||
where
|
||||
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
|
||||
R: Reducer<T>,
|
||||
N: IsNovel<T>,
|
||||
O: MapObserver<Entry = T>,
|
||||
for<'it> O: AsRefIterator<'it, Item = T>,
|
||||
S: HasNamedMetadata,
|
||||
N: IsNovel<T>,
|
||||
I: Input,
|
||||
S: HasNamedMetadata + HasClientPerfMonitor + Debug,
|
||||
{
|
||||
/// Create new `MapFeedback`
|
||||
#[must_use]
|
||||
@ -551,6 +633,76 @@ where
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
fn is_interesting_default<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let mut interesting = false;
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers.match_name::<O>(&self.observer_name).unwrap();
|
||||
|
||||
let map_state = state
|
||||
.named_metadata_mut()
|
||||
.get_mut::<MapFeedbackMetadata<T>>(&self.name)
|
||||
.unwrap();
|
||||
let size = observer.usable_count();
|
||||
if map_state.history_map.len() < size {
|
||||
map_state.history_map.resize(size, T::default());
|
||||
}
|
||||
|
||||
let history_map = map_state.history_map.as_mut_slice();
|
||||
|
||||
for (i, (item, history)) in observer
|
||||
.as_ref_iter()
|
||||
.zip(history_map.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
let reduced = R::reduce(*history, *item);
|
||||
if N::is_novel(*history, reduced) {
|
||||
*history = reduced;
|
||||
interesting = true;
|
||||
if self.novelties.is_some() {
|
||||
self.novelties.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let initial = observer.initial();
|
||||
if interesting {
|
||||
let len = history_map.len();
|
||||
let mut filled = 0;
|
||||
for i in 0..len {
|
||||
if history_map[i] != initial {
|
||||
filled += 1;
|
||||
if self.indexes.is_some() {
|
||||
self.indexes.as_mut().unwrap().push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
manager.fire(
|
||||
state,
|
||||
Event::UpdateUserStats {
|
||||
name: self.stats_name.to_string(),
|
||||
value: UserStats::Ratio(filled, len as u64),
|
||||
phantom: PhantomData,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(interesting)
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`ReachabilityFeedback`] reports if a target has been reached.
|
||||
|
@ -8,6 +8,8 @@ Welcome to `LibAFL`
|
||||
#![cfg_attr(unstable_feature, feature(specialization))]
|
||||
// For `type_id` and owned things
|
||||
#![cfg_attr(unstable_feature, feature(intrinsics))]
|
||||
// For `std::simd`
|
||||
#![cfg_attr(unstable_feature, feature(portable_simd))]
|
||||
#![warn(clippy::cargo)]
|
||||
#![deny(clippy::cargo_common_metadata)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
@ -68,6 +70,8 @@ Welcome to `LibAFL`
|
||||
while_true
|
||||
)
|
||||
)]
|
||||
// Till they fix this buggy lint in clippy
|
||||
#![allow(clippy::borrow_deref_ref)]
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_use]
|
||||
|
@ -58,17 +58,7 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned
|
||||
fn usable_count(&self) -> usize;
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let mut res = 0;
|
||||
for i in 0..cnt {
|
||||
if *self.get(i) != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
fn count_bytes(&self) -> u64;
|
||||
|
||||
/// Compute the hash of the map
|
||||
fn hash(&self) -> u64;
|
||||
@ -85,39 +75,13 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
for i in 0..cnt {
|
||||
*self.get_mut(i) = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn reset_map(&mut self) -> Result<(), Error>;
|
||||
|
||||
/// Get these observer's contents as [`Vec`]
|
||||
fn to_vec(&self) -> Vec<Self::Entry> {
|
||||
let cnt = self.usable_count();
|
||||
let mut res = Vec::with_capacity(cnt);
|
||||
for i in 0..cnt {
|
||||
res.push(*self.get(i));
|
||||
}
|
||||
res
|
||||
}
|
||||
fn to_vec(&self) -> Vec<Self::Entry>;
|
||||
|
||||
/// Get the number of set entries with the specified indexes
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && *self.get(*i) != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize;
|
||||
}
|
||||
|
||||
/// A Simple iterator calling `MapObserver::get`
|
||||
@ -333,6 +297,20 @@ where
|
||||
&mut self.as_mut_slice()[idx]
|
||||
}
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for x in map[0..cnt].iter() {
|
||||
if *x != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn usable_count(&self) -> usize {
|
||||
self.as_slice().len()
|
||||
@ -355,6 +333,32 @@ where
|
||||
fn to_vec(&self) -> Vec<T> {
|
||||
self.as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_mut_slice();
|
||||
for x in map[0..cnt].iter_mut() {
|
||||
*x = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && map[*i] != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsSlice<T> for StdMapObserver<'a, T>
|
||||
@ -598,6 +602,20 @@ where
|
||||
&mut self.as_mut_slice()[idx]
|
||||
}
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for x in map[0..cnt].iter() {
|
||||
if *x != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn usable_count(&self) -> usize {
|
||||
self.as_slice().len()
|
||||
}
|
||||
@ -606,9 +624,36 @@ where
|
||||
hash_slice(self.as_slice())
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_mut_slice();
|
||||
for x in map[0..cnt].iter_mut() {
|
||||
*x = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<T> {
|
||||
self.as_slice().to_vec()
|
||||
}
|
||||
|
||||
/// Get the number of set entries with the specified indexes
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && map[*i] != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, const N: usize> AsSlice<T> for ConstMapObserver<'a, T, N>
|
||||
@ -831,12 +876,52 @@ where
|
||||
&mut self.map.as_mut_slice()[idx]
|
||||
}
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for x in map[0..cnt].iter() {
|
||||
if *x != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
fn hash(&self) -> u64 {
|
||||
hash_slice(self.as_slice())
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_mut_slice();
|
||||
for x in map[0..cnt].iter_mut() {
|
||||
*x = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<T> {
|
||||
self.as_slice().to_vec()
|
||||
}
|
||||
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && map[*i] != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> AsSlice<T> for VariableMapObserver<'a, T>
|
||||
@ -924,9 +1009,27 @@ static COUNT_CLASS_LOOKUP: [u8; 256] = [
|
||||
128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128,
|
||||
];
|
||||
|
||||
static mut COUNT_CLASS_LOOKUP_16: Vec<u16> = vec![];
|
||||
|
||||
fn init_count_class_16() {
|
||||
unsafe {
|
||||
if !COUNT_CLASS_LOOKUP_16.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
COUNT_CLASS_LOOKUP_16 = vec![0; 65536];
|
||||
for i in 0..256 {
|
||||
for j in 0..256 {
|
||||
COUNT_CLASS_LOOKUP_16[(i << 8) + j] =
|
||||
(u16::from(COUNT_CLASS_LOOKUP[i]) << 8) | u16::from(COUNT_CLASS_LOOKUP[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, M> Observer<I, S> for HitcountsMapObserver<M>
|
||||
where
|
||||
M: MapObserver<Entry = u8> + Observer<I, S>,
|
||||
M: MapObserver<Entry = u8> + Observer<I, S> + AsMutSlice<u8>,
|
||||
for<'it> M: AsMutIterator<'it, Item = u8>,
|
||||
{
|
||||
#[inline]
|
||||
@ -935,9 +1038,23 @@ where
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(clippy::cast_ptr_alignment)]
|
||||
fn post_exec(&mut self, state: &mut S, input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
|
||||
for elem in self.as_mut_iter() {
|
||||
*elem = COUNT_CLASS_LOOKUP[*elem as usize];
|
||||
let map = self.as_mut_slice();
|
||||
let len = map.len();
|
||||
if (len & 1) != 0 {
|
||||
unsafe {
|
||||
*map.get_unchecked_mut(len - 1) =
|
||||
*COUNT_CLASS_LOOKUP.get_unchecked(*map.get_unchecked(len - 1) as usize);
|
||||
}
|
||||
}
|
||||
|
||||
let cnt = len / 2;
|
||||
let map16 = unsafe { core::slice::from_raw_parts_mut(map.as_mut_ptr() as *mut u16, cnt) };
|
||||
for (_i, item) in map16[0..cnt].iter_mut().enumerate() {
|
||||
unsafe {
|
||||
*item = *COUNT_CLASS_LOOKUP_16.get_unchecked(*item as usize);
|
||||
}
|
||||
}
|
||||
self.base.post_exec(state, input, exit_kind)
|
||||
}
|
||||
@ -995,12 +1112,27 @@ where
|
||||
self.base.get_mut(idx)
|
||||
}
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
self.base.count_bytes()
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
self.base.reset_map()
|
||||
}
|
||||
|
||||
fn hash(&self) -> u64 {
|
||||
self.base.hash()
|
||||
}
|
||||
fn to_vec(&self) -> Vec<u8> {
|
||||
self.base.to_vec()
|
||||
}
|
||||
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
self.base.how_many_set(indexes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> AsSlice<u8> for HitcountsMapObserver<M>
|
||||
@ -1028,6 +1160,7 @@ where
|
||||
{
|
||||
/// Creates a new [`MapObserver`]
|
||||
pub fn new(base: M) -> Self {
|
||||
init_count_class_16();
|
||||
Self { base }
|
||||
}
|
||||
}
|
||||
@ -1207,6 +1340,28 @@ where
|
||||
fn usable_count(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn to_vec(&self) -> Vec<Self::Entry> {
|
||||
let cnt = self.usable_count();
|
||||
let mut res = Vec::with_capacity(cnt);
|
||||
for i in 0..cnt {
|
||||
res.push(*self.get(i));
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
/// Get the number of set entries with the specified indexes
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && *self.get(*i) != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> MultiMapObserver<'a, T>
|
||||
@ -1434,6 +1589,20 @@ where
|
||||
&mut self.as_mut_slice()[idx]
|
||||
}
|
||||
|
||||
/// Count the set bytes in the map
|
||||
fn count_bytes(&self) -> u64 {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for x in map[0..cnt].iter() {
|
||||
if *x != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn usable_count(&self) -> usize {
|
||||
self.as_slice().len()
|
||||
@ -1458,9 +1627,34 @@ where
|
||||
self.initial = initial;
|
||||
}
|
||||
|
||||
/// Reset the map
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
// Normal memset, see https://rust.godbolt.org/z/Trs5hv
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_mut_slice();
|
||||
for x in map[0..cnt].iter_mut() {
|
||||
*x = initial;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn to_vec(&self) -> Vec<T> {
|
||||
self.as_slice().to_vec()
|
||||
}
|
||||
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
let initial = self.initial();
|
||||
let cnt = self.usable_count();
|
||||
let map = self.as_slice();
|
||||
let mut res = 0;
|
||||
for i in indexes {
|
||||
if *i < cnt && map[*i] != initial {
|
||||
res += 1;
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsSlice<T> for OwnedMapObserver<T>
|
||||
@ -1505,8 +1699,8 @@ where
|
||||
#[allow(missing_docs)]
|
||||
pub mod pybind {
|
||||
use super::{
|
||||
AsMutIterator, AsRefIterator, Debug, Error, HasLen, Iter, IterMut, MapObserver, Named,
|
||||
Observer, OwnedMapObserver, StdMapObserver, String, Vec,
|
||||
AsMutIterator, AsMutSlice, AsRefIterator, AsSlice, Debug, Error, HasLen, Iter, IterMut,
|
||||
MapObserver, Named, Observer, OwnedMapObserver, StdMapObserver, String, Vec,
|
||||
};
|
||||
use crate::observers::pybind::PythonObserver;
|
||||
use concat_idents::concat_idents;
|
||||
@ -1758,6 +1952,18 @@ pub mod pybind {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsSlice<$datatype> for $struct_name_trait {
|
||||
fn as_slice(&self) -> &[$datatype] {
|
||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_slice()) }} )
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMutSlice<$datatype> for $struct_name_trait {
|
||||
fn as_mut_slice(&mut self) -> &mut [$datatype] {
|
||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { unsafe { std::mem::transmute(m.as_mut_slice()) }} )
|
||||
}
|
||||
}
|
||||
|
||||
impl MapObserver for $struct_name_trait {
|
||||
type Entry = $datatype;
|
||||
|
||||
@ -1773,6 +1979,10 @@ pub mod pybind {
|
||||
unsafe { ptr.as_mut().unwrap() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn count_bytes(&self) -> u64 {
|
||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.count_bytes() })
|
||||
}
|
||||
#[inline]
|
||||
fn usable_count(&self) -> usize {
|
||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.usable_count() })
|
||||
@ -1798,9 +2008,20 @@ pub mod pybind {
|
||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.set_initial(initial) });
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reset_map(&mut self) -> Result<(), Error> {
|
||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.reset_map() })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn to_vec(&self) -> Vec<$datatype> {
|
||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.to_vec() })
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn how_many_set(&self, indexes: &[usize]) -> usize {
|
||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.how_many_set(indexes) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for $struct_name_trait {
|
||||
|
@ -813,7 +813,6 @@ pub mod pybind {
|
||||
}
|
||||
|
||||
#[pyo3(name = "match_name")]
|
||||
#[allow(clippy::all)]
|
||||
fn pymatch_name(&self, name: &str) -> Option<PythonObserver> {
|
||||
for ob in &self.list {
|
||||
if *ob.name() == *name {
|
||||
|
Loading…
x
Reference in New Issue
Block a user