Skip to content

Commit 7546c64

Browse files
authored
Merge pull request #266 from finnbear/feature/historybuf_ordered_iterator
Feature/historybuf `OldestOrdered` iterator
2 parents e33646c + 4d0a160 commit 7546c64

File tree

3 files changed

+121
-1
lines changed

3 files changed

+121
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added `OldestOrdered` iterator for `HistoryBuffer`
13+
1014
## [v0.7.9] - 2021-12-16
1115

1216
### Fixed

src/histbuf.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,38 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
181181
pub fn as_slice(&self) -> &[T] {
182182
unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.len()) }
183183
}
184+
185+
/// Returns an iterator for iterating over the buffer from oldest to newest.
186+
///
187+
/// # Examples
188+
///
189+
/// ```
190+
/// use heapless::HistoryBuffer;
191+
///
192+
/// let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
193+
/// buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]);
194+
/// let expected = [1, 2, 3, 4, 5, 6];
195+
/// for (x, y) in buffer.oldest_ordered().zip(expected.iter()) {
196+
/// assert_eq!(x, y)
197+
/// }
198+
///
199+
/// ```
200+
pub fn oldest_ordered<'a>(&'a self) -> OldestOrdered<'a, T, N> {
201+
if self.filled {
202+
OldestOrdered {
203+
buf: self,
204+
cur: self.write_at,
205+
wrapped: false,
206+
}
207+
} else {
208+
// special case: act like we wrapped already to handle empty buffer.
209+
OldestOrdered {
210+
buf: self,
211+
cur: 0,
212+
wrapped: true,
213+
}
214+
}
215+
}
184216
}
185217

186218
impl<T, const N: usize> Extend<T> for HistoryBuffer<T, N> {
@@ -247,9 +279,38 @@ impl<T, const N: usize> Default for HistoryBuffer<T, N> {
247279
}
248280
}
249281

282+
/// An iterator on the underlying buffer ordered from oldest data to newest
283+
#[derive(Clone)]
284+
pub struct OldestOrdered<'a, T, const N: usize> {
285+
buf: &'a HistoryBuffer<T, N>,
286+
cur: usize,
287+
wrapped: bool,
288+
}
289+
290+
impl<'a, T, const N: usize> Iterator for OldestOrdered<'a, T, N> {
291+
type Item = &'a T;
292+
293+
fn next(&mut self) -> Option<&'a T> {
294+
if self.cur == self.buf.len() && self.buf.filled {
295+
// roll-over
296+
self.cur = 0;
297+
self.wrapped = true;
298+
}
299+
300+
if self.cur == self.buf.write_at && self.wrapped {
301+
return None;
302+
}
303+
304+
let item = &self.buf[self.cur];
305+
self.cur += 1;
306+
Some(item)
307+
}
308+
}
309+
250310
#[cfg(test)]
251311
mod tests {
252312
use crate::HistoryBuffer;
313+
use core::fmt::Debug;
253314

254315
#[test]
255316
fn new() {
@@ -314,4 +375,59 @@ mod tests {
314375

315376
assert_eq!(x.as_slice(), [5, 2, 3, 4]);
316377
}
378+
379+
#[test]
380+
fn ordered() {
381+
// test on an empty buffer
382+
let buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
383+
let mut iter = buffer.oldest_ordered();
384+
assert_eq!(iter.next(), None);
385+
assert_eq!(iter.next(), None);
386+
387+
// test on a un-filled buffer
388+
let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
389+
buffer.extend([1, 2, 3]);
390+
assert_eq!(buffer.len(), 3);
391+
assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3]);
392+
393+
// test on a filled buffer
394+
let mut buffer: HistoryBuffer<u8, 6> = HistoryBuffer::new();
395+
buffer.extend([0, 0, 0, 1, 2, 3, 4, 5, 6]);
396+
assert_eq!(buffer.len(), 6);
397+
assert_eq_iter(buffer.oldest_ordered(), &[1, 2, 3, 4, 5, 6]);
398+
399+
// comprehensive test all cases
400+
for n in 0..50 {
401+
const N: usize = 7;
402+
let mut buffer: HistoryBuffer<u8, N> = HistoryBuffer::new();
403+
buffer.extend(0..n);
404+
assert_eq_iter(
405+
buffer.oldest_ordered().copied(),
406+
n.saturating_sub(N as u8)..n,
407+
);
408+
}
409+
}
410+
411+
/// Compares two iterators item by item, making sure they stop at the same time.
412+
fn assert_eq_iter<I: Eq + Debug>(
413+
a: impl IntoIterator<Item = I>,
414+
b: impl IntoIterator<Item = I>,
415+
) {
416+
let mut a = a.into_iter();
417+
let mut b = b.into_iter();
418+
419+
let mut i = 0;
420+
loop {
421+
let a_item = a.next();
422+
let b_item = b.next();
423+
424+
assert_eq!(a_item, b_item, "{}", i);
425+
426+
i += 1;
427+
428+
if b_item.is_none() {
429+
break;
430+
}
431+
}
432+
}
317433
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676

7777
pub use binary_heap::BinaryHeap;
7878
pub use deque::Deque;
79-
pub use histbuf::HistoryBuffer;
79+
pub use histbuf::{HistoryBuffer, OldestOrdered};
8080
pub use indexmap::{Bucket, FnvIndexMap, IndexMap, Pos};
8181
pub use indexset::{FnvIndexSet, IndexSet};
8282
pub use linear_map::LinearMap;

0 commit comments

Comments
 (0)