use flac_io::{decode, FlacError};
fn crc8(data: &[u8]) -> u8 {
let mut c: u8 = 0;
for &b in data {
c ^= b;
for _ in 0..8 {
c = if c & 0x80 != 0 {
(c << 1) ^ 0x07
} else {
c << 1
};
}
}
c
}
fn crc16(data: &[u8]) -> u16 {
let mut c: u16 = 0;
for &b in data {
c ^= (b as u16) << 8;
for _ in 0..8 {
c = if c & 0x8000 != 0 {
(c << 1) ^ 0x8005
} else {
c << 1
};
}
}
c
}
struct Bits {
out: Vec<u8>,
cur: u8,
used: u8,
}
impl Bits {
fn new() -> Self {
Bits {
out: Vec::new(),
cur: 0,
used: 0,
}
}
fn put(&mut self, v: u64, n: u32) {
let mut rem = n;
while rem > 0 {
let free = 8 - self.used;
let take = rem.min(free as u32) as u8;
let shift = rem - take as u32;
let chunk = ((v >> shift) & ((1u64 << take) - 1)) as u8;
self.cur |= chunk << (free - take);
self.used += take;
if self.used == 8 {
self.out.push(self.cur);
self.cur = 0;
self.used = 0;
}
rem -= take as u32;
}
}
fn align(&mut self) {
if self.used != 0 {
self.out.push(self.cur);
self.cur = 0;
self.used = 0;
}
}
fn finish(mut self) -> Vec<u8> {
self.align();
self.out
}
}
fn streaminfo(channels_minus_1: u64, bps_minus_1: u64, total_samples: u64, block: u64) -> Vec<u8> {
let mut s = Bits::new();
s.put(block, 16); s.put(block, 16); s.put(0, 24); s.put(0, 24); s.put(44100, 20); s.put(channels_minus_1, 3);
s.put(bps_minus_1, 5);
s.put(total_samples, 36);
let body = s.finish();
let mut v = b"fLaC".to_vec();
v.push(0x80); v.extend_from_slice(&[0x00, 0x00, 0x22]); v.extend_from_slice(&body);
v.extend_from_slice(&[0u8; 16]); v
}
fn constant_frame(frame_number: u64, block_size: u64) -> Vec<u8> {
let mut h = Bits::new();
h.put(0x3FFE, 14); h.put(0, 1); h.put(0, 1); h.put(7, 4); h.put(0, 4); h.put(0, 4); h.put(0, 3); h.put(0, 1); h.put(frame_number & 0x7F, 8); h.put(block_size - 1, 16); h.align();
let header = h.out.clone();
let mut f = Bits::new();
for &b in &header {
f.put(b as u64, 8);
}
f.put(crc8(&header) as u64, 8);
f.put(0, 1);
f.put(0, 6);
f.put(0, 1);
f.put(0, 16);
f.align();
let frame = f.out.clone();
let c16 = crc16(&frame);
f.put(c16 as u64, 16);
f.finish()
}
#[test]
fn constant_subframe_bomb_is_capped_not_oom() {
let mut stream = streaminfo(0, 15, 0, 65535);
for n in 0..40_000u64 {
stream.extend_from_slice(&constant_frame(n, 65535));
}
match decode(&stream) {
Err(FlacError::LimitExceeded(_)) => {}
other => panic!("expected LimitExceeded, got {other:?}"),
}
}
#[test]
fn declared_huge_total_is_rejected_before_allocation() {
let stream = streaminfo(1, 15, (1u64 << 36) - 1, 4096);
match decode(&stream) {
Err(FlacError::LimitExceeded(_)) => {}
other => panic!("expected LimitExceeded, got {other:?}"),
}
}
#[test]
fn declared_total_just_over_cap_is_rejected() {
let over = (1u64 << 30) / 2 + 1;
let stream = streaminfo(1, 15, over, 4096);
assert!(matches!(decode(&stream), Err(FlacError::LimitExceeded(_))));
}
#[test]
fn thirtytwo_bit_side_channel_wasted_bits_never_panics() {
let mut v = b"fLaC".to_vec();
let mut s = Bits::new();
s.put(192, 16);
s.put(192, 16);
s.put(0, 24);
s.put(0, 24);
s.put(44100, 20);
s.put(1, 3); s.put(31, 5); s.put(0, 36); let body = s.finish();
v.push(0x80);
v.extend_from_slice(&[0x00, 0x00, 0x22]);
v.extend_from_slice(&body);
v.extend_from_slice(&[0u8; 16]);
let mut h = Bits::new();
h.put(0x3FFE, 14);
h.put(0, 1);
h.put(0, 1);
h.put(1, 4); h.put(0, 4); h.put(10, 4); h.put(7, 3); h.put(0, 1);
h.put(0, 8); h.align();
let header = h.out.clone();
let mut f = Bits::new();
for &b in &header {
f.put(b as u64, 8);
}
f.put(crc8(&header) as u64, 8);
f.put(0, 1);
f.put(0, 6);
f.put(0, 1);
f.put(0, 32);
f.put(0, 1);
f.put(0, 6);
f.put(1, 1);
for _ in 0..31 {
f.put(0, 1);
}
f.put(1, 1);
f.put(0, 1); f.align();
let frame = f.out.clone();
let c16 = crc16(&frame);
f.put(c16 as u64, 16);
v.extend_from_slice(&f.finish());
let _ = decode(&v);
}