Skip to content

Commit

Permalink
Merge pull request #449 from witek103/dev/double-ended-histbuf
Browse files Browse the repository at this point in the history
histbuf: Implement DoubleEndedIterator for OldestOrdered
  • Loading branch information
Dirbaio authored Jun 30, 2024
2 parents 39c379c + b00d388 commit d67ebda
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
86 changes: 64 additions & 22 deletions src/histbuf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
///
Expand All @@ -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,
},
}
}
}
Expand Down Expand Up @@ -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.done {
return None;
}

if self.next == self.back {
self.done = true;
}

if self.cur == self.buf.write_at && self.wrapped {
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;
}

let item = &self.buf[self.cur];
self.cur += 1;
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)
}
}
Expand Down Expand Up @@ -609,18 +630,35 @@ 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 an exactly filled buffer
let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
buffer.extend([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]);

// 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 {
Expand All @@ -631,6 +669,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(),
);
}
}

Expand Down

0 comments on commit d67ebda

Please sign in to comment.