pub const MAX_CAPACITY: usize = 100;
#[derive(Debug, Clone, Copy)]
pub struct Level {
pub average: f32,
pub max: f32,
}
impl PartialEq for Level {
fn eq(&self, other: &Self) -> bool {
const EPSILON: f32 = 1e-6;
(self.average - other.average).abs() < EPSILON && (self.max - other.max).abs() < EPSILON
}
}
#[derive(Debug)]
pub struct ClippingPredictorLevelBuffer {
tail: isize,
size: usize,
data: Vec<Level>,
}
impl ClippingPredictorLevelBuffer {
pub fn new(capacity: i32) -> Self {
let capacity = capacity.max(1) as usize;
if capacity > MAX_CAPACITY {
tracing::warn!(
"[agc]: ClippingPredictorLevelBuffer exceeds the maximum allowed capacity. Capacity: {}",
capacity
);
}
Self {
tail: -1,
size: 0,
data: vec![
Level {
average: 0.0,
max: 0.0
};
capacity
],
}
}
pub fn reset(&mut self) {
self.tail = -1;
self.size = 0;
}
pub fn size(&self) -> usize {
self.size
}
pub fn capacity(&self) -> usize {
self.data.len()
}
pub fn push(&mut self, level: Level) {
self.tail += 1;
if self.tail as usize == self.capacity() {
self.tail = 0;
}
if self.size < self.capacity() {
self.size += 1;
}
self.data[self.tail as usize] = level;
}
pub fn compute_partial_metrics(&self, delay: usize, num_items: usize) -> Option<Level> {
debug_assert!(delay < self.capacity());
debug_assert!(num_items > 0);
debug_assert!(num_items <= self.capacity());
debug_assert!(delay + num_items <= self.capacity());
if delay + num_items > self.size {
return None;
}
let mut sum = 0.0_f32;
let mut max = 0.0_f32;
for i in 0..num_items.min(self.size) {
let mut idx = self.tail - delay as isize - i as isize;
if idx < 0 {
idx += self.capacity() as isize;
}
sum += self.data[idx as usize].average;
max = max.max(self.data[idx as usize].max);
}
Some(Level {
average: sum / num_items as f32,
max,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_empty_buffer_size() {
for &capacity in &[-1, 0, 1, 123] {
let buffer = ClippingPredictorLevelBuffer::new(capacity);
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), 0);
}
}
#[test]
fn check_half_empty_buffer_size() {
for &capacity in &[-1, 0, 1, 123] {
let mut buffer = ClippingPredictorLevelBuffer::new(capacity);
for _ in 0..buffer.capacity() / 2 {
buffer.push(Level {
average: 2.0,
max: 4.0,
});
}
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), capacity.max(1) as usize / 2);
}
}
#[test]
fn check_full_buffer_size() {
for &capacity in &[-1, 0, 1, 123] {
let mut buffer = ClippingPredictorLevelBuffer::new(capacity);
for _ in 0..buffer.capacity() {
buffer.push(Level {
average: 2.0,
max: 4.0,
});
}
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), capacity.max(1) as usize);
}
}
#[test]
fn check_large_buffer_size() {
for &capacity in &[-1, 0, 1, 123] {
let mut buffer = ClippingPredictorLevelBuffer::new(capacity);
for _ in 0..2 * buffer.capacity() {
buffer.push(Level {
average: 2.0,
max: 4.0,
});
}
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), capacity.max(1) as usize);
}
}
#[test]
fn check_size_after_reset() {
for &capacity in &[-1, 0, 1, 123] {
let mut buffer = ClippingPredictorLevelBuffer::new(capacity);
buffer.push(Level {
average: 1.0,
max: 1.0,
});
buffer.push(Level {
average: 1.0,
max: 1.0,
});
buffer.reset();
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), 0);
buffer.push(Level {
average: 1.0,
max: 1.0,
});
assert_eq!(buffer.capacity(), capacity.max(1) as usize);
assert_eq!(buffer.size(), 1);
}
}
#[test]
fn check_metrics_after_full_buffer() {
let mut buffer = ClippingPredictorLevelBuffer::new(2);
buffer.push(Level {
average: 1.0,
max: 2.0,
});
buffer.push(Level {
average: 3.0,
max: 6.0,
});
let m = buffer.compute_partial_metrics(0, 1).unwrap();
assert_eq!(
m,
Level {
average: 3.0,
max: 6.0
}
);
let m = buffer.compute_partial_metrics(1, 1).unwrap();
assert_eq!(
m,
Level {
average: 1.0,
max: 2.0
}
);
let m = buffer.compute_partial_metrics(0, 2).unwrap();
assert_eq!(
m,
Level {
average: 2.0,
max: 6.0
}
);
}
#[test]
fn check_metrics_after_push_beyond_capacity() {
let mut buffer = ClippingPredictorLevelBuffer::new(2);
buffer.push(Level {
average: 1.0,
max: 1.0,
});
buffer.push(Level {
average: 3.0,
max: 6.0,
});
buffer.push(Level {
average: 5.0,
max: 10.0,
});
buffer.push(Level {
average: 7.0,
max: 14.0,
});
buffer.push(Level {
average: 6.0,
max: 12.0,
});
let m = buffer.compute_partial_metrics(0, 1).unwrap();
assert_eq!(
m,
Level {
average: 6.0,
max: 12.0
}
);
let m = buffer.compute_partial_metrics(1, 1).unwrap();
assert_eq!(
m,
Level {
average: 7.0,
max: 14.0
}
);
let m = buffer.compute_partial_metrics(0, 2).unwrap();
assert_eq!(
m,
Level {
average: 6.5,
max: 14.0
}
);
}
#[test]
fn check_metrics_after_too_few_items() {
let mut buffer = ClippingPredictorLevelBuffer::new(4);
buffer.push(Level {
average: 1.0,
max: 2.0,
});
buffer.push(Level {
average: 3.0,
max: 6.0,
});
assert!(buffer.compute_partial_metrics(0, 3).is_none());
assert!(buffer.compute_partial_metrics(2, 1).is_none());
}
#[test]
fn check_metrics_after_reset() {
let mut buffer = ClippingPredictorLevelBuffer::new(2);
buffer.push(Level {
average: 1.0,
max: 2.0,
});
buffer.reset();
buffer.push(Level {
average: 5.0,
max: 10.0,
});
buffer.push(Level {
average: 7.0,
max: 14.0,
});
let m = buffer.compute_partial_metrics(0, 1).unwrap();
assert_eq!(
m,
Level {
average: 7.0,
max: 14.0
}
);
let m = buffer.compute_partial_metrics(0, 2).unwrap();
assert_eq!(
m,
Level {
average: 6.0,
max: 14.0
}
);
let m = buffer.compute_partial_metrics(1, 1).unwrap();
assert_eq!(
m,
Level {
average: 5.0,
max: 10.0
}
);
}
}