1
+ //! A "history buffer", similar to a write-only ring buffer of fixed length.
2
+ //!
3
+ //! This buffer keeps a fixed number of elements. On write, the oldest element
4
+ //! is overwritten. Thus, the buffer is useful to keep a history of values with
5
+ //! some desired depth, and for example calculate a rolling average.
6
+ //!
7
+ //! # Examples
8
+ //! ```
9
+ //! use heapless::HistoryBuffer;
10
+ //!
11
+ //! // Initialize a new buffer with 8 elements.
12
+ //! let mut buf = HistoryBuffer::<_, 8>::new();
13
+ //!
14
+ //! // Starts with no data
15
+ //! assert_eq!(buf.recent(), None);
16
+ //!
17
+ //! buf.write(3);
18
+ //! buf.write(5);
19
+ //! buf.extend(&[4, 4]);
20
+ //!
21
+ //! // The most recent written element is a four.
22
+ //! assert_eq!(buf.recent(), Some(&4));
23
+ //!
24
+ //! // To access all elements in an unspecified order, use `as_slice()`.
25
+ //! for el in buf.as_slice() {
26
+ //! println!("{:?}", el);
27
+ //! }
28
+ //!
29
+ //! // Now we can prepare an average of all values, which comes out to 4.
30
+ //! let avg = buf.as_slice().iter().sum::<usize>() / buf.len();
31
+ //! assert_eq!(avg, 4);
32
+ //! ```
33
+
34
+ use core:: borrow:: Borrow ;
35
+ use core:: borrow:: BorrowMut ;
1
36
use core:: fmt;
37
+ use core:: marker:: PhantomData ;
2
38
use core:: mem:: MaybeUninit ;
3
39
use core:: ops:: Deref ;
4
40
use core:: ptr;
5
41
use core:: slice;
6
42
43
+ use crate :: storage:: OwnedStorage ;
44
+ use crate :: storage:: Storage ;
45
+ use crate :: storage:: ViewStorage ;
46
+
47
+ /// Base struct for [`HistoryBuffer`] and [`HistoryBufferView`], generic over the [`Storage`].
48
+ ///
49
+ /// In most cases you should use [`HistoryBuffer`] or [`HistoryBufferView`] directly. Only use this
50
+ /// struct if you want to write code that's generic over both.
51
+ pub struct HistoryBufferInner < T , S : Storage > {
52
+ write_at : usize ,
53
+ filled : bool ,
54
+ data : S :: Buffer < MaybeUninit < T > > ,
55
+ }
56
+
7
57
/// A "history buffer", similar to a write-only ring buffer of fixed length.
8
58
///
9
59
/// This buffer keeps a fixed number of elements. On write, the oldest element
@@ -36,11 +86,40 @@ use core::slice;
36
86
/// let avg = buf.as_slice().iter().sum::<usize>() / buf.len();
37
87
/// assert_eq!(avg, 4);
38
88
/// ```
39
- pub struct HistoryBuffer < T , const N : usize > {
40
- data : [ MaybeUninit < T > ; N ] ,
41
- write_at : usize ,
42
- filled : bool ,
43
- }
89
+ pub type HistoryBuffer < T , const N : usize > = HistoryBufferInner < T , OwnedStorage < N > > ;
90
+
91
+ /// A "view" into a [`HistoryBuffer`]
92
+ ///
93
+ /// Unlike [`HistoryBuffer`], it doesn't have the `const N: usize` in its type signature.
94
+ ///
95
+ /// # Examples
96
+ /// ```
97
+ /// use heapless::HistoryBuffer;
98
+ ///
99
+ /// // Initialize a new buffer with 8 elements.
100
+ /// let mut owned_buf = HistoryBuffer::<_, 8>::new();
101
+ /// let buf: &mut HistoryBufferView<_> = &mut owned_buf;
102
+ ///
103
+ /// // Starts with no data
104
+ /// assert_eq!(buf.recent(), None);
105
+ ///
106
+ /// buf.write(3);
107
+ /// buf.write(5);
108
+ /// buf.extend(&[4, 4]);
109
+ ///
110
+ /// // The most recent written element is a four.
111
+ /// assert_eq!(buf.recent(), Some(&4));
112
+ ///
113
+ /// // To access all elements in an unspecified order, use `as_slice()`.
114
+ /// for el in buf.as_slice() {
115
+ /// println!("{:?}", el);
116
+ /// }
117
+ ///
118
+ /// // Now we can prepare an average of all values, which comes out to 4.
119
+ /// let avg = buf.as_slice().iter().sum::<usize>() / buf.len();
120
+ /// assert_eq!(avg, 4);
121
+ /// ```
122
+ pub type HistoryBufferView < T > = HistoryBufferInner < T , ViewStorage > ;
44
123
45
124
impl < T , const N : usize > HistoryBuffer < T , N > {
46
125
const INIT : MaybeUninit < T > = MaybeUninit :: uninit ( ) ;
@@ -69,12 +148,6 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
69
148
filled : false ,
70
149
}
71
150
}
72
-
73
- /// Clears the buffer, replacing every element with the default value of
74
- /// type `T`.
75
- pub fn clear ( & mut self ) {
76
- * self = Self :: new ( ) ;
77
- }
78
151
}
79
152
80
153
impl < T , const N : usize > HistoryBuffer < T , N >
@@ -101,19 +174,46 @@ where
101
174
filled : true ,
102
175
}
103
176
}
104
-
177
+ }
178
+ impl < T : Copy , S : Storage > HistoryBufferInner < T , S > {
105
179
/// Clears the buffer, replacing every element with the given value.
106
180
pub fn clear_with ( & mut self , t : T ) {
107
- * self = Self :: new_with ( t) ;
181
+ // SAFETY: we reset the values just after
182
+ unsafe { self . drop_contents ( ) } ;
183
+ self . write_at = 0 ;
184
+ self . filled = true ;
185
+
186
+ for d in self . data . borrow_mut ( ) {
187
+ * d = MaybeUninit :: new ( t) ;
188
+ }
108
189
}
109
190
}
110
191
111
- impl < T , const N : usize > HistoryBuffer < T , N > {
192
+ impl < T , S : Storage > HistoryBufferInner < T , S > {
193
+ /// Clears the buffer
194
+ pub fn clear ( & mut self ) {
195
+ // SAFETY: we reset the values just after
196
+ unsafe { self . drop_contents ( ) } ;
197
+ self . write_at = 0 ;
198
+ self . filled = false ;
199
+ }
200
+ }
201
+
202
+ impl < T , S : Storage > HistoryBufferInner < T , S > {
203
+ unsafe fn drop_contents ( & mut self ) {
204
+ unsafe {
205
+ ptr:: drop_in_place ( ptr:: slice_from_raw_parts_mut (
206
+ self . data . borrow_mut ( ) . as_mut_ptr ( ) as * mut T ,
207
+ self . len ( ) ,
208
+ ) )
209
+ }
210
+ }
211
+
112
212
/// Returns the current fill level of the buffer.
113
213
#[ inline]
114
214
pub fn len ( & self ) -> usize {
115
215
if self . filled {
116
- N
216
+ self . capacity ( )
117
217
} else {
118
218
self . write_at
119
219
}
@@ -138,7 +238,7 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
138
238
/// underlying backing array.
139
239
#[ inline]
140
240
pub fn capacity ( & self ) -> usize {
141
- N
241
+ self . data . borrow ( ) . len ( )
142
242
}
143
243
144
244
/// Returns whether the buffer is full
@@ -151,9 +251,9 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
151
251
pub fn write ( & mut self , t : T ) {
152
252
if self . filled {
153
253
// Drop the old before we overwrite it.
154
- unsafe { ptr:: drop_in_place ( self . data [ self . write_at ] . as_mut_ptr ( ) ) }
254
+ unsafe { ptr:: drop_in_place ( self . data . borrow_mut ( ) [ self . write_at ] . as_mut_ptr ( ) ) }
155
255
}
156
- self . data [ self . write_at ] = MaybeUninit :: new ( t) ;
256
+ self . data . borrow_mut ( ) [ self . write_at ] = MaybeUninit :: new ( t) ;
157
257
158
258
self . write_at += 1 ;
159
259
if self . write_at == self . capacity ( ) {
@@ -189,7 +289,7 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
189
289
/// ```
190
290
pub fn recent ( & self ) -> Option < & T > {
191
291
self . recent_index ( )
192
- . map ( |i| unsafe { & * self . data [ i] . as_ptr ( ) } )
292
+ . map ( |i| unsafe { & * self . data . borrow ( ) [ i] . as_ptr ( ) } )
193
293
}
194
294
195
295
/// Returns index of the most recently written value in the underlying slice.
@@ -230,7 +330,7 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
230
330
/// ```
231
331
pub fn oldest ( & self ) -> Option < & T > {
232
332
self . oldest_index ( )
233
- . map ( |i| unsafe { & * self . data [ i] . as_ptr ( ) } )
333
+ . map ( |i| unsafe { & * self . data . borrow ( ) [ i] . as_ptr ( ) } )
234
334
}
235
335
236
336
/// Returns index of the oldest value in the underlying slice.
@@ -258,7 +358,7 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
258
358
/// Returns the array slice backing the buffer, without keeping track
259
359
/// of the write position. Therefore, the element order is unspecified.
260
360
pub fn as_slice ( & self ) -> & [ T ] {
261
- unsafe { slice:: from_raw_parts ( self . data . as_ptr ( ) as * const _ , self . len ( ) ) }
361
+ unsafe { slice:: from_raw_parts ( self . data . borrow ( ) . as_ptr ( ) as * const _ , self . len ( ) ) }
262
362
}
263
363
264
364
/// Returns a pair of slices which contain, in order, the contents of the buffer.
@@ -298,20 +398,11 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
298
398
/// assert_eq!(x, y)
299
399
/// }
300
400
/// ```
301
- pub fn oldest_ordered ( & self ) -> OldestOrdered < ' _ , T , N > {
302
- match ( self . oldest_index ( ) , self . recent_index ( ) ) {
303
- ( Some ( oldest_index) , Some ( recent_index) ) => OldestOrdered {
304
- buf : self ,
305
- next : oldest_index,
306
- back : recent_index,
307
- done : false ,
308
- } ,
309
- _ => OldestOrdered {
310
- buf : self ,
311
- next : 0 ,
312
- back : 0 ,
313
- done : true ,
314
- } ,
401
+ pub fn oldest_ordered ( & self ) -> OldestOrderedInner < ' _ , T , S > {
402
+ let ( old, new) = self . as_slices ( ) ;
403
+ OldestOrderedInner {
404
+ phantom : PhantomData ,
405
+ inner : old. iter ( ) . chain ( new) ,
315
406
}
316
407
}
317
408
}
@@ -354,14 +445,9 @@ where
354
445
}
355
446
}
356
447
357
- impl < T , const N : usize > Drop for HistoryBuffer < T , N > {
448
+ impl < T , S : Storage > Drop for HistoryBufferInner < T , S > {
358
449
fn drop ( & mut self ) {
359
- unsafe {
360
- ptr:: drop_in_place ( ptr:: slice_from_raw_parts_mut (
361
- self . data . as_mut_ptr ( ) as * mut T ,
362
- self . len ( ) ,
363
- ) )
364
- }
450
+ unsafe { self . drop_contents ( ) }
365
451
}
366
452
}
367
453
@@ -404,51 +490,74 @@ where
404
490
}
405
491
}
406
492
493
+ /// Base struct for [`OldestOrdered`] and [`OldestOrderedView`], generic over the [`Storage`].
494
+ ///
495
+ /// In most cases you should use [`OldestOrdered`] or [`OldestOrderedView`] directly. Only use this
496
+ /// struct if you want to write code that's generic over both.
497
+ pub struct OldestOrderedInner < ' a , T , S : Storage > {
498
+ phantom : PhantomData < S > ,
499
+ inner : core:: iter:: Chain < core:: slice:: Iter < ' a , T > , core:: slice:: Iter < ' a , T > > ,
500
+ }
501
+
407
502
/// Double ended iterator on the underlying buffer ordered from the oldest data
408
503
/// to the newest
409
- #[ derive( Clone ) ]
410
- pub struct OldestOrdered < ' a , T , const N : usize > {
411
- buf : & ' a HistoryBuffer < T , N > ,
412
- next : usize ,
413
- back : usize ,
414
- done : bool ,
415
- }
504
+ /// This type exists for backwards compatibility. It is always better to convert it to an [`OldestOrderedView`] with [`into_view`](OldestOrdered::into_view)
505
+ pub type OldestOrdered < ' a , T , const N : usize > = OldestOrderedInner < ' a , T , OwnedStorage < N > > ;
416
506
417
- impl < ' a , T , const N : usize > Iterator for OldestOrdered < ' a , T , N > {
418
- type Item = & ' a T ;
507
+ /// Double ended iterator on the underlying buffer ordered from the oldest data
508
+ /// to the newest
509
+ pub type OldestOrderedView < ' a , T > = OldestOrderedInner < ' a , T , ViewStorage > ;
419
510
420
- fn next ( & mut self ) -> Option < & ' a T > {
421
- if self . done {
422
- return None ;
511
+ impl < ' a , T , const N : usize > OldestOrdered < ' a , T , N > {
512
+ /// Remove the `N` const-generic parameter from the iterator
513
+ ///
514
+ /// For the opposite operation, see [`into_legacy_iter`](OldestOrderedView::into_legacy_iter)
515
+ pub fn into_view ( self ) -> OldestOrderedView < ' a , T > {
516
+ OldestOrderedView {
517
+ phantom : PhantomData ,
518
+ inner : self . inner ,
423
519
}
520
+ }
521
+ }
424
522
425
- if self . next == self . back {
426
- self . done = true ;
523
+ impl < ' a , T > OldestOrderedView < ' a , T > {
524
+ /// Add back the `N` const-generic parameter to use it with APIs expecting the legacy type
525
+ ///
526
+ /// You probably do not need this
527
+ ///
528
+ /// For the opposite operation, see [`into_view`](OldestOrdered::into_view)
529
+ pub fn into_legacy_iter < const N : usize > ( self ) -> OldestOrdered < ' a , T , N > {
530
+ OldestOrdered {
531
+ phantom : PhantomData ,
532
+ inner : self . inner ,
427
533
}
428
-
429
- let item = & self . buf [ self . next ] ;
430
-
431
- self . next = if self . next == N - 1 { 0 } else { self . next + 1 } ;
432
-
433
- Some ( item)
434
534
}
435
535
}
436
536
437
- impl < ' a , T , const N : usize > DoubleEndedIterator for OldestOrdered < ' a , T , N > {
438
- fn next_back ( & mut self ) -> Option < Self :: Item > {
439
- if self . done {
440
- return None ;
537
+ impl < ' a , T , S : Storage > Clone for OldestOrderedInner < ' a , T , S > {
538
+ fn clone ( & self ) -> Self {
539
+ Self {
540
+ phantom : PhantomData ,
541
+ inner : self . inner . clone ( ) ,
441
542
}
543
+ }
544
+ }
442
545
443
- if self . next == self . back {
444
- self . done = true ;
445
- }
546
+ impl < ' a , T , S : Storage > Iterator for OldestOrderedInner < ' a , T , S > {
547
+ type Item = & ' a T ;
446
548
447
- let item = & self . buf [ self . back ] ;
549
+ fn next ( & mut self ) -> Option < & ' a T > {
550
+ self . inner . next ( )
551
+ }
448
552
449
- self . back = if self . back == 0 { N - 1 } else { self . back - 1 } ;
553
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
554
+ self . inner . size_hint ( )
555
+ }
556
+ }
450
557
451
- Some ( item)
558
+ impl < ' a , T , const N : usize > DoubleEndedIterator for OldestOrdered < ' a , T , N > {
559
+ fn next_back ( & mut self ) -> Option < Self :: Item > {
560
+ self . inner . next_back ( )
452
561
}
453
562
}
454
563
0 commit comments