Eager and Fast FeedbackTuple Implementations (#144)

* Introduce eager and fast feedback_or! implementations (issue #135)

* rename FeedbackTuple to CombinedFeedback (as it is a struct not tuple) and add fast/eager AND
This commit is contained in:
Gal Tashma 2021-06-04 15:07:02 +03:00 committed by GitHub
parent 42997dbde9
commit 156ed08905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -108,26 +108,54 @@ where
{ {
} }
/// Compose [`Feedback`]`s` with an `AND` operation pub struct CombinedFeedback<A, B, I, S, FL>
pub struct AndFeedback<A, B, I, S>
where where
A: Feedback<I, S>, A: Feedback<I, S>,
B: Feedback<I, S>, B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input, I: Input,
{ {
/// The first [`Feedback`] to `AND`.
pub first: A, pub first: A,
/// The second [`Feedback`] to `AND`.
pub second: B, pub second: B,
/// The name
name: String, name: String,
phantom: PhantomData<(I, S)>, phantom: PhantomData<(I, S, FL)>,
} }
impl<A, B, I, S> Feedback<I, S> for AndFeedback<A, B, I, S> impl<A, B, I, S, FL> Named for CombinedFeedback<A, B, I, S, FL>
where where
A: Feedback<I, S>, A: Feedback<I, S>,
B: Feedback<I, S>, B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
{
fn name(&self) -> &str {
self.name.as_ref()
}
}
impl<A, B, I, S, FL> CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
{
pub fn new(first: A, second: B) -> Self {
let name = format!("{} ({},{})", FL::name(), first.name(), second.name());
Self {
first,
second,
name,
phantom: PhantomData,
}
}
}
impl<A, B, I, S, FL> Feedback<I, S> for CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input, I: Input,
{ {
fn is_interesting<EM, OT>( fn is_interesting<EM, OT>(
@ -142,14 +170,15 @@ where
EM: EventFirer<I, S>, EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let a = self FL::is_pair_interesting(
.first &mut self.first,
.is_interesting(state, manager, input, observers, exit_kind)?; &mut self.second,
let b = a state,
&& self manager,
.second input,
.is_interesting(state, manager, input, observers, exit_kind)?; observers,
Ok(b) exit_kind,
)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -167,27 +196,17 @@ where
EM: EventFirer<I, S>, EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// Execute this feedback FL::is_pair_interesting_with_perf(
let a = self.first.is_interesting_with_perf( &mut self.first,
&mut self.second,
state, state,
manager, manager,
input, input,
observers, observers,
&exit_kind, exit_kind,
feedback_stats, feedback_stats,
feedback_index, feedback_index,
)?; )
let b = a
&& self.second.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)?;
Ok(b)
} }
#[inline] #[inline]
@ -203,60 +222,62 @@ where
} }
} }
impl<A, B, I, S> Named for AndFeedback<A, B, I, S> pub trait FeedbackLogic<A, B, I, S>: 'static
where where
A: Feedback<I, S>, A: Feedback<I, S>,
B: Feedback<I, S>, B: Feedback<I, S>,
I: Input, I: Input,
{ {
#[inline] fn name() -> &'static str;
fn name(&self) -> &str {
&self.name fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple;
#[cfg(feature = "introspection")]
fn is_pair_interesting_with_perf<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple;
}
pub struct LogicEagerOr {}
pub struct LogicFastOr {}
pub struct LogicEagerAnd {}
pub struct LogicFastAnd {}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerOr
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn name() -> &'static str {
"Eager OR"
} }
}
impl<A, B, I, S> AndFeedback<A, B, I, S> fn is_pair_interesting<EM, OT>(
where first: &mut A,
A: Feedback<I, S>, second: &mut B,
B: Feedback<I, S>,
I: Input,
{
/// Creates a new [`AndFeedback`], resulting in the `AND` of two feedbacks.
pub fn new(first: A, second: B) -> Self {
let name = format!("And({}, {})", first.name(), second.name());
Self {
first,
second,
name,
phantom: PhantomData,
}
}
}
/// Compose feedbacks with an OR operation
pub struct OrFeedback<A, B, I, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
/// The first [`Feedback`]
pub first: A,
/// The second [`Feedback`], `OR`ed with the first.
pub second: B,
/// The name
name: String,
phantom: PhantomData<(I, S)>,
}
impl<A, B, I, S> Feedback<I, S> for OrFeedback<A, B, I, S>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn is_interesting<EM, OT>(
&mut self,
state: &mut S, state: &mut S,
manager: &mut EM, manager: &mut EM,
input: &I, input: &I,
@ -267,19 +288,15 @@ where
EM: EventFirer<I, S>, EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let a = self let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
.first let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
.is_interesting(state, manager, input, observers, exit_kind)?; Ok(a || b)
let b = a
|| self
.second
.is_interesting(state, manager, input, observers, exit_kind)?;
Ok(b)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
fn is_interesting_with_perf<EM, OT>( fn is_pair_interesting_with_perf<EM, OT>(
&mut self, first: &mut A,
second: &mut B,
state: &mut S, state: &mut S,
manager: &mut EM, manager: &mut EM,
input: &I, input: &I,
@ -293,7 +310,7 @@ where
OT: ObserversTuple, OT: ObserversTuple,
{ {
// Execute this feedback // Execute this feedback
let a = self.first.is_interesting_with_perf( let a = first.is_interesting_with_perf(
state, state,
manager, manager,
input, input,
@ -302,62 +319,254 @@ where
feedback_stats, feedback_stats,
feedback_index, feedback_index,
)?; )?;
let b = a
|| self.second.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)?;
Ok(b)
}
#[inline] let b = second.is_interesting_with_perf(
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> { state,
self.first.append_metadata(state, testcase)?; manager,
self.second.append_metadata(state, testcase) input,
} observers,
&exit_kind,
#[inline] feedback_stats,
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> { feedback_index + 1,
self.first.discard_metadata(state, input)?; )?;
self.second.discard_metadata(state, input) Ok(a || b)
} }
} }
impl<A, B, I, S> Named for OrFeedback<A, B, I, S> impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastOr
where where
A: Feedback<I, S>, A: Feedback<I, S>,
B: Feedback<I, S>, B: Feedback<I, S>,
I: Input, I: Input,
{ {
#[inline] fn name() -> &'static str {
fn name(&self) -> &str { "Fast OR"
&self.name
} }
}
impl<A, B, I, S> OrFeedback<A, B, I, S> fn is_pair_interesting<EM, OT>(
where first: &mut A,
A: Feedback<I, S>, second: &mut B,
B: Feedback<I, S>, state: &mut S,
I: Input, manager: &mut EM,
{ input: &I,
/// Creates a new [`OrFeedback`] for two feedbacks. observers: &OT,
pub fn new(first: A, second: B) -> Self { exit_kind: &ExitKind,
let name = format!("Or({}, {})", first.name(), second.name()); ) -> Result<bool, Error>
Self { where
first, EM: EventFirer<I, S>,
second, OT: ObserversTuple,
name, {
phantom: PhantomData, let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if a {
return Ok(true);
} }
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_with_perf<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
// Execute this feedback
let a = first.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index,
)?;
if a {
return Ok(true);
}
second.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)
} }
} }
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn name() -> &'static str {
"Eager AND"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a && b)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_with_perf<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
// Execute this feedback
let a = first.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index,
)?;
let b = second.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)?;
Ok(a && b)
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
{
fn name() -> &'static str {
"Fast AND"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if a == false {
return Ok(false);
}
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_with_perf<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
feedback_stats: &mut [u64; NUM_FEEDBACKS],
feedback_index: usize,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
// Execute this feedback
let a = first.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index,
)?;
if a == false {
return Ok(false);
}
second.is_interesting_with_perf(
state,
manager,
input,
observers,
&exit_kind,
feedback_stats,
feedback_index + 1,
)
}
}
/// Combine two feedbacks with an eager AND operation,
/// will call all feedbacks functions even if not necessery to conclude the result
pub type EagerAndFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicEagerAnd>;
/// Combine two feedbacks with an fast AND operation,
/// might skip calling feedbacks functions if not necessery to conclude the result
pub type FastAndFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastAnd>;
/// Combine two feedbacks with an eager OR operation,
/// will call all feedbacks functions even if not necessery to conclude the result
pub type EagerOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicEagerOr>;
/// Combine two feedbacks with an fast OR operation,
/// might skip calling feedbacks functions if not necessery to conclude the result
/// This means any feedback that is not first might be skipped, use caution when using with
/// `TimeFeedback`
pub type FastOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastOr>;
/// Compose feedbacks with an OR operation /// Compose feedbacks with an OR operation
pub struct NotFeedback<A, I, S> pub struct NotFeedback<A, I, S>
where where
@ -438,7 +647,18 @@ macro_rules! feedback_and {
( $head:expr, $($tail:expr), +) => { ( $head:expr, $($tail:expr), +) => {
// recursive call // recursive call
$crate::feedbacks::AndFeedback::new($head , feedback_and!($($tail),+)) $crate::feedbacks::EagerAndFeedback::new($head , feedback_and!($($tail),+))
};
}
///
/// Variadic macro to create a chain of (fast) AndFeedback
#[macro_export]
macro_rules! feedback_and_fast {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
// recursive call
$crate::feedbacks::FastAndFeedback::new($head , feedback_and_fast!($($tail),+))
}; };
} }
@ -449,7 +669,17 @@ macro_rules! feedback_or {
( $head:expr, $($tail:expr), +) => { ( $head:expr, $($tail:expr), +) => {
// recursive call // recursive call
$crate::feedbacks::OrFeedback::new($head , feedback_or!($($tail),+)) $crate::feedbacks::EagerOrFeedback::new($head , feedback_or!($($tail),+))
};
}
#[macro_export]
macro_rules! feedback_or_fast {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
// recursive call
$crate::feedbacks::FastOrFeedback::new($head , feedback_or_fast!($($tail),+))
}; };
} }