#![cfg(feature = "std")]
use std::alloc::{GlobalAlloc, Layout, System};
use std::hint::black_box;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use quiver::prelude::*;
struct CountingAllocator;
static ALLOCS: AtomicUsize = AtomicUsize::new(0);
static COUNTING: AtomicBool = AtomicBool::new(false);
unsafe impl GlobalAlloc for CountingAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOCS.fetch_add(1, Ordering::Relaxed);
}
System.alloc(layout)
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
System.dealloc(ptr, layout);
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if COUNTING.load(Ordering::Relaxed) {
ALLOCS.fetch_add(1, Ordering::Relaxed);
}
System.realloc(ptr, layout, new_size)
}
}
#[global_allocator]
static GLOBAL: CountingAllocator = CountingAllocator;
fn count_allocs<F: FnOnce()>(f: F) -> usize {
ALLOCS.store(0, Ordering::Relaxed);
COUNTING.store(true, Ordering::Relaxed);
f();
COUNTING.store(false, Ordering::Relaxed);
ALLOCS.load(Ordering::Relaxed)
}
fn build_patch() -> Patch {
let sr = 44_100.0;
let mut patch = Patch::new(sr);
let vco = patch.add("vco", Vco::new(sr));
let lfo = patch.add("lfo", Lfo::new(sr));
let svf = patch.add("svf", Svf::new(sr));
let vca = patch.add("vca", Vca::new());
let out = patch.add("out", StereoOutput::new());
patch.connect(vco.out("saw"), svf.in_("in")).unwrap();
patch.connect(lfo.out("sin"), svf.in_("cutoff")).unwrap();
patch.connect(svf.out("lp"), vca.in_("in")).unwrap();
patch.connect(vca.out("out"), out.in_("left")).unwrap();
patch.set_output(out.id());
patch.compile().unwrap();
patch
}
#[test]
fn graph_tick_paths_are_allocation_free() {
let mut patch = build_patch();
for _ in 0..256 {
black_box(patch.tick());
}
let per_sample = count_allocs(|| {
for _ in 0..1000 {
black_box(patch.tick());
}
});
assert_eq!(
per_sample, 0,
"tick() allocated {} time(s) across 1000 samples",
per_sample
);
let mut left = [0.0_f64; 512];
let mut right = [0.0_f64; 512];
patch.tick_block(&mut left, &mut right); let block = count_allocs(|| {
patch.tick_block(&mut left, &mut right);
black_box((&left, &right));
});
assert_eq!(block, 0, "tick_block() allocated {} time(s)", block);
}