use std::mem;
#[repr(transparent)]
#[derive(Copy, Clone, Default, Debug)]
pub(super) struct ObservationLength(usize);
impl ObservationLength {
pub fn eq(&self, other: usize) -> bool {
self.0 == other
}
pub fn assert_eq(&self, other: usize) {
assert_eq!(self.0, other, "Expected observation lengths to be the same");
}
pub const fn new(obs_len: usize) -> Self {
Self(obs_len)
}
}
#[repr(transparent)]
pub(super) struct TrimmedObservation {
data: *mut i64,
}
unsafe impl Send for TrimmedObservation {}
impl TrimmedObservation {
pub unsafe fn as_mut_slice(&mut self, len: ObservationLength) -> &mut [i64] {
unsafe { std::slice::from_raw_parts_mut(self.data, len.0) }
}
pub unsafe fn consume(self, len: ObservationLength) {
drop(self.into_boxed_slice(len));
}
pub fn new(v: &[i64], len: ObservationLength) -> Self {
len.assert_eq(v.len());
let b: Box<[i64]> = Box::from(v);
let p = Box::into_raw(b);
let data = p as *mut i64;
Self { data }
}
unsafe fn into_boxed_slice(mut self, len: ObservationLength) -> Box<[i64]> {
unsafe {
let s: &mut [i64] = std::slice::from_raw_parts_mut(
mem::replace(&mut self.data, std::ptr::null_mut()),
len.0,
);
Box::from_raw(s)
}
}
pub(super) unsafe fn into_vec(mut self, len: ObservationLength) -> Vec<i64> {
unsafe {
Vec::from_raw_parts(
mem::replace(&mut self.data, std::ptr::null_mut()),
len.0,
len.0,
)
}
}
}
impl Drop for TrimmedObservation {
fn drop(&mut self) {
assert_eq!(
self.data,
std::ptr::null_mut(),
"Dropped TrimmedObservation that still owned data."
);
}
}
#[cfg(test)]
mod tests {
use super::*;
use bolero::TypeGenerator;
#[test]
fn as_mut_test() {
let v = &[1, 2];
let o = ObservationLength::new(2);
let mut t = TrimmedObservation::new(v, o);
unsafe {
assert_eq!(t.as_mut_slice(o), &vec![1, 2]);
t.as_mut_slice(o).iter_mut().for_each(|v| *v *= 2);
assert_eq!(t.as_mut_slice(o), &vec![2, 4]);
t.consume(o);
}
}
#[test]
fn drop_after_emptying_test() {
let v = &[1, 2];
let o = ObservationLength::new(2);
let t = TrimmedObservation::new(v, o);
unsafe {
t.consume(o);
}
}
#[test]
#[should_panic]
#[cfg_attr(miri, ignore)]
fn drop_owned_data_panics_test() {
let v = &[1, 2];
let o = ObservationLength::new(2);
let _t = TrimmedObservation::new(v, o);
}
#[test]
fn into_boxed_slice_test() {
let v = &[1, 2];
let o = ObservationLength::new(2);
let mut t = TrimmedObservation::new(v, o);
unsafe {
assert_eq!(t.as_mut_slice(o), &vec![1, 2]);
let b = t.into_boxed_slice(o);
assert_eq!(*b, vec![1, 2]);
}
}
#[test]
fn into_vec_test() {
let v = &[1, 2];
let o = ObservationLength::new(2);
let mut t = TrimmedObservation::new(v, o);
unsafe {
assert_eq!(t.as_mut_slice(o), &vec![1, 2]);
let b = t.into_vec(o);
assert_eq!(*b, vec![1, 2]);
}
}
#[test]
fn fuzz_trimmed_observation() {
bolero::check!().with_type::<Vec<i64>>().for_each(|v| {
let o = ObservationLength::new(v.len());
{
let t = TrimmedObservation::new(v, o);
unsafe {
t.consume(o);
}
}
{
let t = TrimmedObservation::new(v, o);
unsafe {
assert_eq!(t.into_boxed_slice(o).as_ref(), v.as_slice());
}
}
{
let t = TrimmedObservation::new(v, o);
unsafe {
assert_eq!(&t.into_vec(o), v);
}
}
})
}
#[derive(Debug, TypeGenerator)]
enum Operation {
Add(i64),
Sub(i64),
Mul(i64),
Div(i64),
}
#[test]
fn fuzz_mutations() {
bolero::check!()
.with_type::<(Vec<i64>, Vec<Operation>)>()
.for_each(|(v, ops)| {
let o = ObservationLength::new(v.len());
let mut t = TrimmedObservation::new(v, o);
let v = &mut v.clone();
for op in ops {
let slice = unsafe { t.as_mut_slice(o) };
slice.iter_mut().zip(v.iter_mut()).for_each(|(a, b)| {
let func = match op {
Operation::Add(x) => Box::new(|y: i64| y.checked_add(*x))
as Box<dyn Fn(_) -> Option<i64>>,
Operation::Sub(x) => Box::new(|y: i64| y.checked_sub(*x)),
Operation::Mul(x) => Box::new(|y: i64| y.checked_mul(*x)),
Operation::Div(x) => Box::new(|y: i64| y.checked_div(*x)),
};
match (func(*a), func(*b)) {
(Some(c), Some(d)) => {
*a = c;
*b = d;
assert_eq!(a, b);
}
(None, None) => {}
_ => {
panic!("a: {a}, b: {b}");
}
}
});
}
unsafe {
t.consume(o);
}
})
}
}