-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
385 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,5 @@ crash-* | |
*.o | ||
corpus | ||
crashes | ||
perf.data* | ||
*.svg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
[package] | ||
name = "boolean-tree" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
bolero-generator = { path = "../../lib/bolero-generator" } | ||
|
||
[dev-dependencies] | ||
bolero = { path = "../../lib/bolero" } | ||
|
||
[workspace] | ||
members = ["."] | ||
|
||
[profile.fuzz] | ||
inherits = "dev" | ||
opt-level = 3 | ||
incremental = false | ||
codegen-units = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# boolean-tree | ||
|
||
A simple boolean logic language |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
use bolero_generator::TypeGenerator; | ||
|
||
thread_local! { | ||
static SHOULD_PANIC: bool = { | ||
#[cfg(bolero_should_panic)] | ||
return true; | ||
|
||
#[cfg(not(bolero_should_panic))] | ||
return std::env::var("SHOULD_PANIC").is_ok(); | ||
}; | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
#[derive(Clone, Debug, TypeGenerator)] | ||
pub enum Expr { | ||
Value(bool), | ||
And(Box<Expr>, Box<Expr>), | ||
Or(Box<Expr>, Box<Expr>), | ||
Xor(Box<Expr>, Box<Expr>), | ||
Nand(Box<Expr>, Box<Expr>), | ||
Not(Box<Expr>), | ||
} | ||
|
||
impl Expr { | ||
pub fn eval(&self) -> bool { | ||
match self { | ||
Expr::Value(value) => *value, | ||
Expr::And(a, b) => a.eval() && b.eval(), | ||
Expr::Or(a, b) => a.eval() || b.eval(), | ||
Expr::Xor(a, b) => a.eval() ^ b.eval(), | ||
Expr::Nand(a, b) => !(a.eval() && b.eval()), | ||
Expr::Not(_) if SHOULD_PANIC.with(|v| *v) => unreachable!(), | ||
Expr::Not(a) => !a.eval(), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use super::*; | ||
use bolero::check; | ||
|
||
#[test] | ||
fn model_test() { | ||
check!() | ||
.with_type::<Expr>() | ||
.with_max_depth(3) | ||
.for_each(|ops| { | ||
let _value = ops.eval(); | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
[package] | ||
name = "rle-stack" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dev-dependencies] | ||
bolero = { path = "../../lib/bolero" } | ||
|
||
[workspace] | ||
members = ["."] | ||
|
||
[profile.fuzz] | ||
inherits = "dev" | ||
opt-level = 3 | ||
incremental = false | ||
codegen-units = 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# rle-stack | ||
A stack which compresses value with run-length encoding |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
use core::fmt; | ||
|
||
thread_local! { | ||
static SHOULD_PANIC: bool = { | ||
#[cfg(bolero_should_panic)] | ||
return true; | ||
|
||
#[cfg(not(bolero_should_panic))] | ||
return std::env::var("SHOULD_PANIC").is_ok(); | ||
}; | ||
} | ||
|
||
type Counter = u16; | ||
|
||
//= https://en.wikipedia.org/wiki/Run-length_encoding | ||
//# Run-length encoding (RLE) is a form of lossless data compression in which | ||
//# runs of data (sequences in which the same data value occurs in many consecutive | ||
//# data elements) are stored as a single data value and count, rather than as | ||
//# the original run. | ||
|
||
pub struct RleStack<T> { | ||
stack: Vec<Entry<T>>, | ||
len: usize, | ||
} | ||
|
||
impl<T> Default for RleStack<T> { | ||
fn default() -> Self { | ||
Self { | ||
stack: vec![], | ||
len: 0, | ||
} | ||
} | ||
} | ||
|
||
impl<T: fmt::Debug> fmt::Debug for RleStack<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("RleStack") | ||
.field("stack", &self.stack) | ||
.field("len", &self.len) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<T: Clone + Eq> RleStack<T> { | ||
pub fn push(&mut self, value: T) { | ||
self.len += 1; | ||
|
||
if let Some(prev) = self.stack.last_mut() { | ||
if prev.merge(&value) { | ||
return; | ||
} | ||
} | ||
|
||
self.stack.push(Entry::new(value)); | ||
} | ||
|
||
pub fn pop(&mut self) -> Option<T> { | ||
let entry = self.stack.last_mut()?; | ||
|
||
self.len -= 1; | ||
|
||
if entry.additional == 0 { | ||
return self.stack.pop().map(|entry| entry.value); | ||
} | ||
entry.additional -= 1; | ||
Some(entry.value.clone()) | ||
} | ||
|
||
pub fn clear(&mut self) { | ||
// inject faults into the model if configured | ||
if SHOULD_PANIC.with(|v| *v) { | ||
return; | ||
} | ||
|
||
self.len = 0; | ||
self.stack.clear(); | ||
} | ||
|
||
pub fn len(&self) -> usize { | ||
self.len | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
self.len == 0 | ||
} | ||
|
||
pub fn iter(&self) -> Iter<T> { | ||
Iter { | ||
entry: 0, | ||
count: 0, | ||
stack: self, | ||
} | ||
} | ||
} | ||
|
||
pub struct Iter<'a, T> { | ||
entry: usize, | ||
count: Counter, | ||
stack: &'a RleStack<T>, | ||
} | ||
|
||
impl<'a, T> Iterator for Iter<'a, T> { | ||
type Item = &'a T; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
let entry = self.stack.stack.get(self.entry)?; | ||
|
||
if entry.additional <= self.count { | ||
self.entry += 1; | ||
self.count = 0; | ||
} else { | ||
self.count += 1; | ||
} | ||
|
||
Some(&entry.value) | ||
} | ||
} | ||
|
||
struct Entry<T> { | ||
value: T, | ||
additional: Counter, | ||
} | ||
|
||
impl<T: fmt::Debug> fmt::Debug for Entry<T> { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
f.debug_struct("Entry") | ||
.field("value", &self.value) | ||
.field("additional", &self.additional) | ||
.finish() | ||
} | ||
} | ||
|
||
impl<T> Entry<T> { | ||
pub fn new(value: T) -> Self { | ||
Self { | ||
value, | ||
additional: 0, | ||
} | ||
} | ||
} | ||
|
||
impl<T: Eq> Entry<T> { | ||
pub fn merge(&mut self, other: &T) -> bool { | ||
if &self.value == other { | ||
self.additional += 1; | ||
true | ||
} else { | ||
false | ||
} | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
use super::*; | ||
use bolero::{check, generator::*}; | ||
use core::fmt; | ||
|
||
#[derive(Clone, Copy, Debug, TypeGenerator)] | ||
enum Operation<T> { | ||
Push { count: u8, value: T }, | ||
Pop { count: u8 }, | ||
Clear, | ||
} | ||
|
||
#[derive(Default)] | ||
struct Model<T: Copy + fmt::Debug + Eq> { | ||
oracle: Vec<T>, | ||
subject: RleStack<T>, | ||
} | ||
|
||
impl<T: Copy + fmt::Debug + Eq> Model<T> { | ||
pub fn push(&mut self, value: T) { | ||
self.oracle.push(value); | ||
self.subject.push(value); | ||
self.invariants(); | ||
} | ||
|
||
pub fn pop(&mut self) -> Option<T> { | ||
let expected = self.oracle.pop(); | ||
let actual = self.subject.pop(); | ||
assert_eq!(expected, actual); | ||
self.invariants(); | ||
actual | ||
} | ||
|
||
pub fn clear(&mut self) { | ||
self.oracle.clear(); | ||
self.subject.clear(); | ||
self.invariants(); | ||
} | ||
|
||
pub fn apply(&mut self, operation: Operation<T>) { | ||
match operation { | ||
Operation::Push { count, value } => { | ||
for _ in 0..count { | ||
self.push(value); | ||
} | ||
} | ||
Operation::Pop { count } => { | ||
for _ in 0..count { | ||
self.pop(); | ||
} | ||
} | ||
Operation::Clear => { | ||
self.clear(); | ||
} | ||
} | ||
} | ||
|
||
pub fn finish(&self) { | ||
self.invariants(); | ||
let actual: Vec<_> = self.subject.iter().copied().collect(); | ||
assert_eq!( | ||
self.oracle, actual, | ||
"\n\nSubject state: {:#?}", | ||
self.subject | ||
); | ||
} | ||
|
||
fn invariants(&self) { | ||
assert_eq!( | ||
self.oracle.len(), | ||
self.subject.len(), | ||
"lengths do not match" | ||
); | ||
assert_eq!( | ||
self.oracle.is_empty(), | ||
self.subject.is_empty(), | ||
"is_empty does not match" | ||
); | ||
} | ||
} | ||
|
||
impl<T: Copy + fmt::Debug + Eq> Drop for Model<T> { | ||
fn drop(&mut self) { | ||
if !std::thread::panicking() { | ||
self.finish(); | ||
} | ||
} | ||
} | ||
|
||
#[test] | ||
fn model_test() { | ||
check!().with_type::<Vec<Operation<u8>>>().for_each(|ops| { | ||
let mut model = Model::default(); | ||
for op in ops { | ||
model.apply(*op); | ||
} | ||
}) | ||
} | ||
|
||
#[test] | ||
fn unit_test() { | ||
let mut model = <Model<u8>>::default(); | ||
|
||
assert!(model.pop().is_none()); | ||
model.clear(); | ||
assert!(model.pop().is_none()); | ||
|
||
model.push(123); | ||
assert_eq!(model.pop(), Some(123)); | ||
} |
Oops, something went wrong.