histbuf: Implement DoubleEndedIterator for OldestOrdered

Rewrite Iterator for OldestOrdered and add DoubleEndedIterator
implementation.
Update HistoryBuffer oldest_ordered() to reflect changes.

Signed-off-by: Witold Lipieta <witek103@gmail.com>
This commit is contained in:
Witold Lipieta 2024-01-25 23:52:43 +01:00 committed by Dario Nieuwenhuis
parent 39c379c339
commit a251cbf5a2
2 changed files with 60 additions and 24 deletions

View File

@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- Added `serde::Serialize` and `serde::Deserialize` implementations to `HistoryBuffer`.
- Added `Vec::drain`.
- Added `String::drain`.
- Implemented `DoubleEndedIterator` for `OldestOrdered`.
### Changed

View File

@ -283,7 +283,8 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
}
}
/// Returns an iterator for iterating over the buffer from oldest to newest.
/// Returns double ended iterator for iterating over the buffer from
/// the oldest to the newest and back.
///
/// # Examples
///
@ -298,19 +299,19 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
/// }
/// ```
pub fn oldest_ordered(&self) -> OldestOrdered<'_, T, N> {
if self.filled {
OldestOrdered {
match (self.oldest_index(), self.recent_index()) {
(Some(oldest_index), Some(recent_index)) => OldestOrdered {
buf: self,
cur: self.write_at,
wrapped: false,
}
} else {
// special case: act like we wrapped already to handle empty buffer.
OldestOrdered {
next: oldest_index,
back: recent_index,
done: false,
},
_ => OldestOrdered {
buf: self,
cur: 0,
wrapped: true,
}
next: 0,
back: 0,
done: true,
},
}
}
}
@ -403,30 +404,50 @@ where
}
}
/// An iterator on the underlying buffer ordered from oldest data to newest
/// Double ended iterator on the underlying buffer ordered from the oldest data
/// to the newest
#[derive(Clone)]
pub struct OldestOrdered<'a, T, const N: usize> {
buf: &'a HistoryBuffer<T, N>,
cur: usize,
wrapped: bool,
next: usize,
back: usize,
done: bool,
}
impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
if self.cur == self.buf.len() && self.buf.filled {
// roll-over
self.cur = 0;
self.wrapped = true;
}
if self.cur == self.buf.write_at && self.wrapped {
if self.done {
return None;
}
let item = &self.buf[self.cur];
self.cur += 1;
if self.next == self.back {
self.done = true;
}
let item = &self.buf[self.next];
self.next = if self.next == N - 1 { 0 } else { self.next + 1 };
Some(item)
}
}
impl<'a, T, const N: usize> DoubleEndedIterator for OldestOrdered<'a, T, N> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
if self.next == self.back {
self.done = true;
}
let item = &self.buf[self.back];
self.back = if self.back == 0 { N - 1 } else { self.back - 1 };
Some(item)
}
}
@ -609,18 +630,28 @@ mod tests {
let mut iter = buffer.oldest_ordered();
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next_back(), None);
// test on a un-filled buffer
let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
buffer.extend([1, 2, 3]);
assert_eq!(buffer.len(), 3);
assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3]);
assert_eq_iter(buffer.oldest_ordered().rev(), &[3, 2, 1]);
let mut iter = buffer.oldest_ordered();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next_back(), Some(&3));
assert_eq!(iter.next_back(), Some(&2));
assert_eq!(iter.next_back(), None);
assert_eq!(iter.next(), None);
// test on a filled buffer
let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]);
assert_eq!(buffer.len(), 6);
assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]);
assert_eq_iter(buffer.oldest_ordered().rev(), &[6, 5, 4, 3, 2, 1]);
// comprehensive test all cases
for n in 0..50 {
@ -631,6 +662,10 @@ mod tests {
buffer.oldest_ordered().copied(),
n.saturating_sub(N as u8)..n,
);
assert_eq_iter(
buffer.oldest_ordered().rev().copied(),
(n.saturating_sub(N as u8)..n).rev(),
);
}
}