1
+ use core:: mem:: MaybeUninit ;
2
+ use core:: ptr;
3
+ use core:: slice;
4
+
1
5
/// A "history buffer", similar to a write-only ring buffer of fixed length.
2
6
///
3
7
/// This buffer keeps a fixed number of elements. On write, the oldest element
4
8
/// is overwritten. Thus, the buffer is useful to keep a history of values with
5
9
/// some desired depth, and for example calculate a rolling average.
6
10
///
7
- /// The buffer is always fully initialized; depending on the constructor, the
8
- /// initial value is either the default value for the element type or a supplied
9
- /// initial value. This simplifies the API and is mostly irrelevant for the
10
- /// intended use case.
11
- ///
12
11
/// # Examples
13
12
/// ```
14
13
/// use heapless::HistoryBuffer;
15
14
///
16
15
/// // Initialize a new buffer with 8 elements, all initially zero.
17
16
/// let mut buf = HistoryBuffer::<_, 8>::new();
18
17
///
18
+ /// // Starts with no data
19
+ /// assert_eq!(buf.recent(), None);
20
+ ///
19
21
/// buf.write(3);
20
22
/// buf.write(5);
21
23
/// buf.extend(&[4, 4]);
22
24
///
23
25
/// // The most recent written element is a four.
24
- /// assert_eq!(buf.recent(), &4 );
26
+ /// assert_eq!(buf.recent(), Some(&4) );
25
27
///
26
28
/// // To access all elements in an unspecified order, use `as_slice()`.
27
29
/// for el in buf.as_slice() { println!("{:?}", el); }
28
30
///
29
- /// // Now we can prepare an average of all values, which comes out to 2 .
31
+ /// // Now we can prepare an average of all values, which comes out to 4 .
30
32
/// let avg = buf.as_slice().iter().sum::<usize>() / buf.len();
31
- /// assert_eq!(avg, 2 );
33
+ /// assert_eq!(avg, 4 );
32
34
/// ```
33
- #[ derive( Clone ) ]
34
35
pub struct HistoryBuffer < T , const N : usize > {
35
- data : [ T ; N ] ,
36
+ data : [ MaybeUninit < T > ; N ] ,
36
37
write_at : usize ,
38
+ filled : bool ,
37
39
}
38
40
39
- impl < T , const N : usize > HistoryBuffer < T , N >
40
- where
41
- T : Default + Copy ,
42
- {
43
- /// Constructs a new history buffer, where every element is filled with the
44
- /// default value of the type `T`.
41
+ impl < T , const N : usize > HistoryBuffer < T , N > {
42
+ const INIT : MaybeUninit < T > = MaybeUninit :: uninit ( ) ;
43
+
44
+ /// Constructs a new history buffer.
45
45
///
46
- /// `HistoryBuffer` currently cannot be constructed in `const` context .
46
+ /// The construction of a `HistoryBuffer` works in `const` contexts .
47
47
///
48
48
/// # Examples
49
49
///
50
50
/// ```
51
51
/// use heapless::HistoryBuffer;
52
52
///
53
53
/// // Allocate a 16-element buffer on the stack
54
- /// let mut x: HistoryBuffer<u8, 16> = HistoryBuffer::new();
55
- /// // All elements are zero
56
- /// assert_eq!(x.as_slice(), [0; 16]);
54
+ /// let x: HistoryBuffer<u8, 16> = HistoryBuffer::new();
55
+ /// assert_eq!(x.len(), 0);
57
56
/// ```
58
- pub fn new ( ) -> Self {
57
+ #[ inline]
58
+ pub const fn new ( ) -> Self {
59
59
Self {
60
- // seems not yet implemented
61
- // data: Default::default(),
62
- data : [ T :: default ( ) ; N ] ,
60
+ data : [ Self :: INIT ; N ] ,
63
61
write_at : 0 ,
62
+ filled : false ,
64
63
}
65
64
}
66
65
@@ -87,10 +86,12 @@ where
87
86
/// // All elements are four
88
87
/// assert_eq!(x.as_slice(), [4; 16]);
89
88
/// ```
89
+ #[ inline]
90
90
pub fn new_with ( t : T ) -> Self {
91
91
Self {
92
- data : [ t ; N ] ,
92
+ data : [ MaybeUninit :: new ( t ) ; N ] ,
93
93
write_at : 0 ,
94
+ filled : true ,
94
95
}
95
96
}
96
97
@@ -101,18 +102,35 @@ where
101
102
}
102
103
103
104
impl < T , const N : usize > HistoryBuffer < T , N > {
105
+ /// Returns the current fill level of the buffer.
106
+ #[ inline]
107
+ pub fn len ( & self ) -> usize {
108
+ if self . filled {
109
+ N
110
+ } else {
111
+ self . write_at
112
+ }
113
+ }
114
+
104
115
/// Returns the capacity of the buffer, which is the length of the
105
116
/// underlying backing array.
106
- pub fn len ( & self ) -> usize {
107
- self . data . len ( )
117
+ #[ inline]
118
+ pub fn capacity ( & self ) -> usize {
119
+ N
108
120
}
109
121
110
122
/// Writes an element to the buffer, overwriting the oldest value.
111
123
pub fn write ( & mut self , t : T ) {
112
- self . data [ self . write_at ] = t;
124
+ if self . filled {
125
+ // Drop the old before we overwrite it.
126
+ unsafe { ptr:: drop_in_place ( self . data [ self . write_at ] . as_mut_ptr ( ) ) }
127
+ }
128
+ self . data [ self . write_at ] = MaybeUninit :: new ( t) ;
129
+
113
130
self . write_at += 1 ;
114
- if self . write_at == self . len ( ) {
131
+ if self . write_at == self . capacity ( ) {
115
132
self . write_at = 0 ;
133
+ self . filled = true ;
116
134
}
117
135
}
118
136
@@ -139,20 +157,28 @@ impl<T, const N: usize> HistoryBuffer<T, N> {
139
157
/// let mut x: HistoryBuffer<u8, 16> = HistoryBuffer::new();
140
158
/// x.write(4);
141
159
/// x.write(10);
142
- /// assert_eq!(x.recent(), &10);
160
+ /// assert_eq!(x.recent(), Some( &10) );
143
161
/// ```
144
- pub fn recent ( & self ) -> & T {
162
+ pub fn recent ( & self ) -> Option < & T > {
145
163
if self . write_at == 0 {
146
- & self . data [ self . len ( ) - 1 ]
164
+ if self . filled {
165
+ Some ( unsafe { & * self . data [ self . capacity ( ) - 1 ] . as_ptr ( ) } )
166
+ } else {
167
+ None
168
+ }
147
169
} else {
148
- & self . data [ self . write_at - 1 ]
170
+ Some ( unsafe { & * self . data [ self . write_at - 1 ] . as_ptr ( ) } )
149
171
}
150
172
}
151
173
152
174
/// Returns the array slice backing the buffer, without keeping track
153
175
/// of the write position. Therefore, the element order is unspecified.
154
176
pub fn as_slice ( & self ) -> & [ T ] {
155
- & self . data
177
+ if self . filled {
178
+ unsafe { slice:: from_raw_parts ( self . data . as_ptr ( ) as * const _ , self . capacity ( ) ) }
179
+ } else {
180
+ unsafe { slice:: from_raw_parts ( self . data . as_ptr ( ) as * const _ , self . write_at ) }
181
+ }
156
182
}
157
183
}
158
184
@@ -179,6 +205,17 @@ where
179
205
}
180
206
}
181
207
208
+ impl < T , const N : usize > Drop for HistoryBuffer < T , N > {
209
+ fn drop ( & mut self ) {
210
+ unsafe {
211
+ ptr:: drop_in_place ( ptr:: slice_from_raw_parts_mut (
212
+ self . data . as_mut_ptr ( ) as * mut T ,
213
+ self . len ( ) ,
214
+ ) )
215
+ }
216
+ }
217
+ }
218
+
182
219
#[ cfg( test) ]
183
220
mod tests {
184
221
use crate :: HistoryBuffer ;
@@ -190,15 +227,15 @@ mod tests {
190
227
assert_eq ! ( x. as_slice( ) , [ 1 ; 4 ] ) ;
191
228
192
229
let x: HistoryBuffer < u8 , 4 > = HistoryBuffer :: new ( ) ;
193
- assert_eq ! ( x. as_slice( ) , [ 0 ; 4 ] ) ;
230
+ assert_eq ! ( x. as_slice( ) , [ ] ) ;
194
231
}
195
232
196
233
#[ test]
197
234
fn write ( ) {
198
235
let mut x: HistoryBuffer < u8 , 4 > = HistoryBuffer :: new ( ) ;
199
236
x. write ( 1 ) ;
200
237
x. write ( 4 ) ;
201
- assert_eq ! ( x. as_slice( ) , [ 1 , 4 , 0 , 0 ] ) ;
238
+ assert_eq ! ( x. as_slice( ) , [ 1 , 4 ] ) ;
202
239
203
240
x. write ( 5 ) ;
204
241
x. write ( 6 ) ;
@@ -213,7 +250,7 @@ mod tests {
213
250
fn clear ( ) {
214
251
let mut x: HistoryBuffer < u8 , 4 > = HistoryBuffer :: new_with ( 1 ) ;
215
252
x. clear ( ) ;
216
- assert_eq ! ( x. as_slice( ) , [ 0 ; 4 ] ) ;
253
+ assert_eq ! ( x. as_slice( ) , [ ] ) ;
217
254
218
255
let mut x: HistoryBuffer < u8 , 4 > = HistoryBuffer :: new ( ) ;
219
256
x. clear_with ( 1 ) ;
@@ -223,16 +260,16 @@ mod tests {
223
260
#[ test]
224
261
fn recent ( ) {
225
262
let mut x: HistoryBuffer < u8 , 4 > = HistoryBuffer :: new ( ) ;
226
- assert_eq ! ( x. recent( ) , & 0 ) ;
263
+ assert_eq ! ( x. recent( ) , None ) ;
227
264
228
265
x. write ( 1 ) ;
229
266
x. write ( 4 ) ;
230
- assert_eq ! ( x. recent( ) , & 4 ) ;
267
+ assert_eq ! ( x. recent( ) , Some ( & 4 ) ) ;
231
268
232
269
x. write ( 5 ) ;
233
270
x. write ( 6 ) ;
234
271
x. write ( 10 ) ;
235
- assert_eq ! ( x. recent( ) , & 10 ) ;
272
+ assert_eq ! ( x. recent( ) , Some ( & 10 ) ) ;
236
273
}
237
274
238
275
#[ test]
0 commit comments