use std::fmt;
use hex_slice::AsHex;
use rand::{random, Rng, XorShiftRng};
use std::cmp::min;
#[derive(Clone, PartialEq)]
pub struct InfoPool {
data: Vec<u8>,
}
pub struct InfoTap<'a> {
data: &'a mut Vec<u8>,
rng: XorShiftRng,
}
#[derive(Clone)]
pub struct InfoReplay<'a> {
data: &'a [u8],
off: usize,
}
impl fmt::Debug for InfoPool {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("InfoPool")
.field("data", &format_args!("{:x}", self.data.as_hex()))
.finish()
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DataError {
PoolExhausted,
SkipItem,
}
impl InfoPool {
pub fn of_vec(data: Vec<u8>) -> Self {
InfoPool { data: data }
}
pub fn new() -> Self {
Self { data: Vec::new() }
}
pub fn buffer(&self) -> &[u8] {
&*self.data
}
fn tap_with(&mut self, rng: XorShiftRng) -> InfoTap {
InfoTap {
data: &mut self.data,
rng: rng,
}
}
pub fn tap(&mut self) -> InfoTap {
self.tap_with(random::<XorShiftRng>())
}
pub fn replay(&self) -> InfoReplay {
InfoReplay {
data: &*self.data,
off: 0,
}
}
}
impl<'a> InfoTap<'a> {
pub fn next_byte(&mut self) -> u8 {
let next = self.rng.gen::<u8>();
self.data.push(next);
next
}
}
impl<'a> Iterator for InfoTap<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
Some(self.next_byte())
}
}
impl<'a> InfoReplay<'a> {
pub fn next_byte(&mut self) -> u8 {
let res = self.data.get(self.off).cloned();
self.off += 1;
res.unwrap_or(0)
}
}
impl<'a> Iterator for InfoReplay<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
Some(self.next_byte())
}
}
fn minimize_via_removal<F: Fn(InfoReplay) -> bool>(
p: &InfoPool,
candidate: &mut InfoPool,
pred: &F,
) -> Option<InfoPool> {
trace!("minimizing by removal: {:?}", p);
if p.data.len() == 0 {
return None;
}
let max_pow = 0usize.count_zeros();
let pow = max_pow - p.data.len().leading_zeros();
for granularity in 0..pow {
let width = 1 << (pow - granularity);
for chunk in 0..(1 << granularity) {
let start = min(chunk * width, p.data.len());
let end = min(start + width, p.data.len());
if start == end {
break;
}
candidate.data.clear();
candidate.data.extend(&p.data[0..start]);
candidate.data.extend(&p.data[end..]);
let test = pred(candidate.replay());
trace!(
"removed {},{}: {:?}; test result {}",
start,
end,
candidate,
test
);
if test {
if let Some(res) = minimize(&candidate, pred) {
trace!("Returning shrunk: {:?}", res);
return Some(res);
} else {
trace!("Returning original: {:?}", candidate);
return Some(candidate.clone());
}
}
}
}
None
}
fn minimize_via_scalar_shrink<F: Fn(InfoReplay) -> bool>(
p: &InfoPool,
candidate: &mut InfoPool,
pred: &F,
) -> Option<InfoPool> {
trace!("minimizing by scalar shrink: {:?}", p);
for i in 0..p.data.len() {
candidate.clone_from(&p);
let max_pow = 0u8.count_zeros();
let pow = max_pow - p.data[i].leading_zeros();
for bitoff in 0..pow {
candidate.data[i] = p.data[i] - (p.data[i] >> bitoff);
trace!(
"shrunk item -(bitoff:{}) {} {}->{}: {:?}",
bitoff,
i,
p.data[i],
candidate.data[i],
candidate
);
if candidate.buffer() == p.buffer() {
trace!("No change");
continue;
}
let test = pred(candidate.replay());
trace!("test result {}", test);
if test {
if let Some(res) = minimize(&candidate, pred) {
trace!("Returning shrunk: {:?}", res);
return Some(res);
} else {
trace!("Returning original: {:?}", candidate);
return Some(candidate.clone());
}
}
}
}
None
}
pub fn minimize<F: Fn(InfoReplay) -> bool>(p: &InfoPool, pred: &F) -> Option<InfoPool> {
let mut candidate = p.clone();
if let Some(res) = minimize_via_removal(p, &mut candidate, pred) {
return Some(res);
}
if let Some(res) = minimize_via_scalar_shrink(p, &mut candidate, pred) {
return Some(res);
}
trace!("Nothing smaller found than {:?}", p);
None
}
#[cfg(test)]
mod tests {
extern crate env_logger;
use super::*;
#[test]
fn should_take_each_item_in_pool() {
let p = InfoPool::of_vec(vec![0, 1, 2, 3]);
let mut t = p.replay();
assert_eq!(t.next_byte(), 0);
assert_eq!(t.next_byte(), 1);
assert_eq!(t.next_byte(), 2);
assert_eq!(t.next_byte(), 3);
assert_eq!(t.next_byte(), 0);
}
#[test]
fn should_generate_random_data() {
let trials = 1024usize;
let mut vals = 0;
let mut p = InfoPool::new();
let mut t = p.tap_with(::rand::XorShiftRng::new_unseeded());
let error = 4;
for _ in 0..trials {
vals += t.next_byte() as usize;
}
let mean = vals / trials;
let expected = 128;
assert!(
(expected - error) < mean && (expected + error) > mean,
"Expected {} trials to be ({}+/-{}); got {}",
trials,
expected,
error,
mean
)
}
#[test]
fn should_allow_restarting_read() {
let mut p = InfoPool::new();
let mut v0 = Vec::new();
{
let mut t = p.tap();
for _ in 0..4 {
v0.push(t.next_byte())
}
}
let mut t = p.replay();
let mut v1 = Vec::new();
for _ in 0..4 {
v1.push(t.next_byte())
}
assert_eq!(v0, v1)
}
#[test]
fn should_allow_borrowing_buffer() {
let p = InfoPool::of_vec(vec![1]);
assert_eq!(p.buffer(), &[1]);
}
#[test]
fn tap_can_act_as_iterator() {
let buf = vec![4, 3, 2, 1];
let p = InfoPool::of_vec(buf.clone());
let _: &Iterator<Item = u8> = &p.replay();
assert_eq!(p.replay().take(4).collect::<Vec<_>>(), buf)
}
#[test]
fn replay_can_act_as_iterator() {
let buf = vec![4, 3, 2, 1];
let p = InfoPool::of_vec(buf.clone());
let _: &Iterator<Item = u8> = &p.replay();
assert_eq!(p.replay().take(4).collect::<Vec<_>>(), buf)
}
#[test]
fn minimiser_should_minimise_to_empty() {
let p = InfoPool::of_vec(vec![1]);
let min = minimize(&p, &|_| true);
assert_eq!(min.as_ref().map(|p| p.buffer()), Some([].as_ref()))
}
#[test]
fn minimiser_should_minimise_to_minimum_given_size() {
env_logger::init().unwrap_or(());
let p = InfoPool::of_vec(vec![1; 4]);
let min = minimize(&p, &|t| t.take(16).filter(|&v| v > 0).count() > 1)
.expect("some smaller pool");
assert_eq!(min.buffer(), &[1, 1])
}
#[test]
fn minimiser_should_minimise_scalar_values() {
let p = InfoPool::of_vec(vec![255; 3]);
let min = minimize(&p, &|t| t.take(16).any(|v| v >= 3)).expect("some smaller pool");
assert_eq!(min.buffer(), &[3])
}
#[test]
fn minimiser_should_minimise_scalar_values_to_empty() {
let p = InfoPool::of_vec(vec![255; 3]);
let min = minimize(&p, &|t| t.take(16).any(|_| true)).expect("some smaller pool");
assert_eq!(min.buffer(), &[] as &[u8])
}
#[test]
fn minimiser_should_minimise_scalar_values_by_search() {
let p = InfoPool::of_vec(vec![255; 3]);
let min = minimize(&p, &|t| t.take(16).any(|v| v >= 13)).expect("some smaller pool");
assert_eq!(min.buffer(), &[13])
}
#[test]
fn minimiser_should_minimise_scalar_values_accounting_for_overflow() {
let p = InfoPool::of_vec(vec![255; 3]);
let min = minimize(&p, &|t| t.take(16).any(|v| v >= 251)).expect("some smaller pool");
assert_eq!(min.buffer(), &[251])
}
}