66 lines
2.6 KiB
Rust
66 lines
2.6 KiB
Rust
//! No field of a source sizes an allocation: a WAV `data` chunk that declares 4 GiB, and
|
||
//! a Y4M header that declares a 4096 × 4096 frame, each followed by a few bytes, are
|
||
//! refused having allocated about what they held. Its own binary: it installs a
|
||
//! counting global allocator.
|
||
|
||
use std::alloc::{GlobalAlloc, Layout, System};
|
||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||
|
||
struct Peak;
|
||
static LIVE: AtomicUsize = AtomicUsize::new(0);
|
||
static PEAK: AtomicUsize = AtomicUsize::new(0);
|
||
|
||
unsafe impl GlobalAlloc for Peak {
|
||
unsafe fn alloc(&self, l: Layout) -> *mut u8 {
|
||
let p = unsafe { System.alloc(l) };
|
||
if !p.is_null() {
|
||
let now = LIVE.fetch_add(l.size(), Ordering::SeqCst) + l.size();
|
||
PEAK.fetch_max(now, Ordering::SeqCst);
|
||
}
|
||
p
|
||
}
|
||
unsafe fn dealloc(&self, p: *mut u8, l: Layout) {
|
||
unsafe { System.dealloc(p, l) };
|
||
LIVE.fetch_sub(l.size(), Ordering::SeqCst);
|
||
}
|
||
}
|
||
|
||
#[global_allocator]
|
||
static A: Peak = Peak;
|
||
|
||
/// The most a reader may hold beyond what it was handed, for files of a few bytes.
|
||
const BOUND: usize = 1 << 20;
|
||
|
||
fn peak_of(f: impl FnOnce() -> &'static str) -> (&'static str, usize) {
|
||
let base = LIVE.load(Ordering::SeqCst);
|
||
PEAK.store(base, Ordering::SeqCst);
|
||
let v = f();
|
||
(v, PEAK.load(Ordering::SeqCst) - base)
|
||
}
|
||
|
||
#[test]
|
||
fn a_declared_size_never_sizes_a_buffer() {
|
||
let mut wav = b"RIFF\0\0\0\0WAVEfmt \x10\0\0\0".to_vec();
|
||
wav.extend_from_slice(&[1, 0, 1, 0]); // PCM, one channel
|
||
wav.extend_from_slice(&8000u32.to_le_bytes());
|
||
wav.extend_from_slice(&16000u32.to_le_bytes());
|
||
wav.extend_from_slice(&[2, 0, 16, 0]); // align 2, 16 bits
|
||
wav.extend_from_slice(b"data");
|
||
wav.extend_from_slice(&0xffff_fffeu32.to_le_bytes());
|
||
wav.extend_from_slice(&[0; 4]);
|
||
let (v, peak) = peak_of(|| wai_pack::conformance::source_verdict("x.wav", &wav));
|
||
assert_eq!(v, "wav-truncated");
|
||
assert!(peak < BOUND, "a 4 GiB data claim allocated {peak} bytes");
|
||
// The reader's own bound, whatever block a caller asks for.
|
||
let (v, peak) = peak_of(|| {
|
||
let mut r = wai_pack::source::wav::WavReader::new(&wav[..]).unwrap();
|
||
r.next_block(u64::MAX).map(|_| "ok").unwrap_or_else(|e| e.code())
|
||
});
|
||
assert_eq!(v, "wav-truncated");
|
||
assert!(peak < BOUND, "next_block(u64::MAX) on a 4 GiB data claim allocated {peak} bytes");
|
||
|
||
let y4m = [&b"YUV4MPEG2 W4096 H4096 F25:1 C444\nFRAME\n"[..], &[16; 6]].concat();
|
||
let (v, peak) = peak_of(|| wai_pack::conformance::source_verdict("x.y4m", &y4m));
|
||
assert_eq!(v, "y4m-truncated");
|
||
assert!(peak < BOUND, "a 4096 × 4096 frame header allocated {peak} bytes");
|
||
}
|