#![allow(clippy::module_name_repetitions)]
use crate::sample::Sample;
use std::collections::VecDeque;
#[derive(Debug)]
struct PendingChunk {
sequence: u64,
samples: Vec<Sample>,
}
#[derive(Debug)]
pub(crate) struct SequenceReorderBuffer {
pending: VecDeque<PendingChunk>,
}
impl SequenceReorderBuffer {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self {
pending: VecDeque::with_capacity(capacity.max(1)),
}
}
pub(crate) fn len(&self) -> usize {
self.pending.len()
}
pub(crate) fn is_empty(&self) -> bool {
self.pending.is_empty()
}
pub(crate) fn first_sequence(&self) -> Option<u64> {
self.pending.front().map(|chunk| chunk.sequence)
}
pub(crate) fn has_sequence(&self, sequence: u64) -> bool {
self.pending
.front()
.is_some_and(|chunk| chunk.sequence == sequence)
}
pub(crate) fn pop_next(&mut self, sequence: u64) -> Option<Vec<Sample>> {
if self.has_sequence(sequence) {
return self.pending.pop_front().map(|chunk| chunk.samples);
}
None
}
pub(crate) fn insert(&mut self, sequence: u64, samples: Vec<Sample>) {
let slot = self.pending.make_contiguous();
match slot.binary_search_by_key(&sequence, |chunk| chunk.sequence) {
Ok(index) => {
slot[index].samples = samples;
}
Err(index) => {
self.pending
.insert(index, PendingChunk { sequence, samples });
}
}
}
pub(crate) fn into_ordered_chunks(self) -> impl Iterator<Item = Vec<Sample>> {
self.pending.into_iter().map(|chunk| chunk.samples)
}
}
#[cfg(test)]
mod tests {
use super::SequenceReorderBuffer;
use crate::sample::Sample;
#[test]
fn ring_buffer_emits_out_of_order_arrivals_in_sequence_order() {
let mut pending = SequenceReorderBuffer::with_capacity(4);
pending.insert(2, vec![Sample::new().with_metadata("test", 2)]);
pending.insert(0, vec![Sample::new().with_metadata("test", 0)]);
pending.insert(1, vec![Sample::new().with_metadata("test", 1)]);
let first = pending.pop_next(0).expect("sequence 0 should be buffered");
let second = pending.pop_next(1).expect("sequence 1 should be buffered");
let third = pending.pop_next(2).expect("sequence 2 should be buffered");
assert_eq!(first[0].metadata().expect("missing metadata").index, 0);
assert_eq!(second[0].metadata().expect("missing metadata").index, 1);
assert_eq!(third[0].metadata().expect("missing metadata").index, 2);
assert!(
pending.is_empty(),
"all buffered sequences should be drained"
);
}
}