use bumpalo::Bump; use quickcheck::{quickcheck, Arbitrary, Gen}; use std::mem; #[derive(Clone, Debug, PartialEq)] struct BigValue { data: [u64; 32], } impl BigValue { fn new(x: u64) -> BigValue { BigValue { data: [ x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, ], } } } impl Arbitrary for BigValue { fn arbitrary(g: &mut G) -> BigValue { BigValue::new(u64::arbitrary(g)) } } #[derive(Clone, Debug)] enum Elems { OneT(T), TwoT(T, T), FourT(T, T, T, T), OneU(U), TwoU(U, U), FourU(U, U, U, U), } impl Arbitrary for Elems where T: Arbitrary + Clone, U: Arbitrary + Clone, { fn arbitrary(g: &mut G) -> Elems { let x: u8 = u8::arbitrary(g); match x % 6 { 0 => Elems::OneT(T::arbitrary(g)), 1 => Elems::TwoT(T::arbitrary(g), T::arbitrary(g)), 2 => Elems::FourT( T::arbitrary(g), T::arbitrary(g), T::arbitrary(g), T::arbitrary(g), ), 3 => Elems::OneU(U::arbitrary(g)), 4 => Elems::TwoU(U::arbitrary(g), U::arbitrary(g)), 5 => Elems::FourU( U::arbitrary(g), U::arbitrary(g), U::arbitrary(g), U::arbitrary(g), ), _ => unreachable!(), } } fn shrink(&self) -> Box> { match self { Elems::OneT(_) => Box::new(vec![].into_iter()), Elems::TwoT(a, b) => { Box::new(vec![Elems::OneT(a.clone()), Elems::OneT(b.clone())].into_iter()) } Elems::FourT(a, b, c, d) => Box::new( vec![ Elems::TwoT(a.clone(), b.clone()), Elems::TwoT(a.clone(), c.clone()), Elems::TwoT(a.clone(), d.clone()), Elems::TwoT(b.clone(), c.clone()), Elems::TwoT(b.clone(), d.clone()), Elems::TwoT(c.clone(), d.clone()), ] .into_iter(), ), Elems::OneU(_) => Box::new(vec![].into_iter()), Elems::TwoU(a, b) => { Box::new(vec![Elems::OneU(a.clone()), Elems::OneU(b.clone())].into_iter()) } Elems::FourU(a, b, c, d) => Box::new( vec![ Elems::TwoU(a.clone(), b.clone()), Elems::TwoU(a.clone(), c.clone()), Elems::TwoU(a.clone(), d.clone()), Elems::TwoU(b.clone(), c.clone()), Elems::TwoU(b.clone(), d.clone()), Elems::TwoU(c.clone(), d.clone()), ] .into_iter(), ), } } } fn overlap((a1, a2): (usize, usize), (b1, b2): (usize, usize)) -> bool { assert!(a1 < a2); assert!(b1 < b2); a1 < b2 && b1 < a2 } fn range(t: &T) -> (usize, usize) { let start = t as *const _ as usize; let end = start + mem::size_of::(); (start, end) } quickcheck! { fn can_allocate_big_values(values: Vec) -> () { let bump = Bump::new(); let mut alloced = vec![]; for vals in values.iter().cloned() { alloced.push(bump.alloc(vals)); } for (vals, alloc) in values.iter().zip(alloced.into_iter()) { assert_eq!(vals, alloc); } } fn big_allocations_never_overlap(values: Vec) -> () { let bump = Bump::new(); let mut alloced = vec![]; for v in values { let a = bump.alloc(v); let start = a as *const _ as usize; let end = unsafe { (a as *const BigValue).offset(1) as usize }; let range = (start, end); for r in &alloced { assert!(!overlap(*r, range)); } alloced.push(range); } } fn can_allocate_heterogeneous_things_and_they_dont_overlap(things: Vec>) -> () { let bump = Bump::new(); let mut ranges = vec![]; for t in things { let r = match t { Elems::OneT(a) => { range(bump.alloc(a)) }, Elems::TwoT(a, b) => { range(bump.alloc([a, b])) }, Elems::FourT(a, b, c, d) => { range(bump.alloc([a, b, c, d])) }, Elems::OneU(a) => { range(bump.alloc(a)) }, Elems::TwoU(a, b) => { range(bump.alloc([a, b])) }, Elems::FourU(a, b, c, d) => { range(bump.alloc([a, b, c, d])) }, }; for s in &ranges { assert!(!overlap(r, *s)); } ranges.push(r); } } fn test_alignment_chunks(sizes: Vec) -> () { const SUPPORTED_ALIGNMENTS: &[usize] = &[1, 2, 4, 8, 16]; for &alignment in SUPPORTED_ALIGNMENTS { let mut b = Bump::with_capacity(513); let mut sizes = sizes.iter().map(|&size| (size % 10) * alignment).collect::>(); for &size in &sizes { let layout = std::alloc::Layout::from_size_align(size, alignment).unwrap(); let ptr = b.alloc_layout(layout).as_ptr() as *const u8 as usize; assert_eq!(ptr % alignment, 0); } for chunk in b.iter_allocated_chunks() { let mut remaining = chunk.len(); while remaining > 0 { let size = sizes.pop().expect("too many bytes in the chunk output"); assert!(remaining >= size, "returned chunk contained padding"); remaining -= size; } } assert_eq!(sizes.into_iter().sum::(), 0); } } fn alloc_slices(allocs: Vec<(u8, usize)>) -> () { let b = Bump::new(); let mut allocated: Vec<(usize, usize)> = vec![]; for (val, len) in allocs { let len = len % 100; let s = b.alloc_slice_fill_copy(len, val); assert_eq!(s.len(), len); assert!(s.iter().all(|v| v == &val)); let range = (s.as_ptr() as usize, unsafe { s.as_ptr().add(s.len()) } as usize); for r in &allocated { let no_overlap = range.1 <= r.0 || r.1 <= range.0; assert!(no_overlap); } allocated.push(range); } } fn alloc_strs(allocs: Vec) -> () { let b = Bump::new(); let allocated: Vec<&str> = allocs.iter().map(|s| b.alloc_str(s) as &_).collect(); for (val, alloc) in allocs.into_iter().zip(allocated) { assert_eq!(val, alloc); } } }