embedded_charts/
memory.rs1use crate::data::DataPoint;
4use crate::error::{DataError, DataResult};
5use heapless::Vec;
6
7pub struct FixedCapacityCollections;
9
10impl FixedCapacityCollections {
11 pub fn data_vec<T: DataPoint, const N: usize>() -> Vec<T, N> {
13 Vec::new()
14 }
15
16 pub fn string_vec<const N: usize, const S: usize>() -> Vec<heapless::String<S>, N> {
18 Vec::new()
19 }
20
21 pub fn color_vec<C: Copy, const N: usize>() -> Vec<C, N> {
23 Vec::new()
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct MemoryStats {
30 pub total_allocated: usize,
32 pub used: usize,
34 pub available: usize,
36 pub peak_usage: usize,
38}
39
40impl MemoryStats {
41 pub const fn new(total: usize) -> Self {
43 Self {
44 total_allocated: total,
45 used: 0,
46 available: total,
47 peak_usage: 0,
48 }
49 }
50
51 pub fn update_usage(&mut self, used: usize) {
53 self.used = used;
54 self.available = self.total_allocated.saturating_sub(used);
55 if used > self.peak_usage {
56 self.peak_usage = used;
57 }
58 }
59
60 pub fn utilization_percent(&self) -> f32 {
62 if self.total_allocated == 0 {
63 0.0
64 } else {
65 (self.used as f32 / self.total_allocated as f32) * 100.0
66 }
67 }
68
69 pub fn is_above_threshold(&self, threshold_percent: f32) -> bool {
71 self.utilization_percent() > threshold_percent
72 }
73}
74
75pub struct ChartMemoryManager<const POOL_SIZE: usize> {
77 stats: MemoryStats,
78 high_water_mark: usize,
79}
80
81impl<const POOL_SIZE: usize> ChartMemoryManager<POOL_SIZE> {
82 pub fn new() -> Self {
84 Self {
85 stats: MemoryStats::new(POOL_SIZE),
86 high_water_mark: 0,
87 }
88 }
89}
90
91impl<const POOL_SIZE: usize> Default for ChartMemoryManager<POOL_SIZE> {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97impl<const POOL_SIZE: usize> ChartMemoryManager<POOL_SIZE> {
98 pub fn stats(&self) -> &MemoryStats {
100 &self.stats
101 }
102
103 pub fn update_usage(&mut self, used: usize) {
105 self.stats.update_usage(used);
106 if used > self.high_water_mark {
107 self.high_water_mark = used;
108 }
109 }
110
111 pub fn high_water_mark(&self) -> usize {
113 self.high_water_mark
114 }
115
116 pub fn reset_stats(&mut self) {
118 self.stats = MemoryStats::new(POOL_SIZE);
119 self.high_water_mark = 0;
120 }
121
122 pub fn is_memory_critical(&self, threshold: f32) -> bool {
124 self.stats.is_above_threshold(threshold)
125 }
126}
127
128#[derive(Debug, Clone)]
130pub struct ManagedSlidingWindow<T: Copy, const N: usize> {
131 buffer: [Option<T>; N],
132 head: usize,
133 count: usize,
134 full: bool,
135 memory_stats: MemoryStats,
136}
137
138impl<T: Copy, const N: usize> ManagedSlidingWindow<T, N> {
139 pub fn new() -> Self {
141 Self {
142 buffer: [None; N],
143 head: 0,
144 count: 0,
145 full: false,
146 memory_stats: MemoryStats::new(N * core::mem::size_of::<T>()),
147 }
148 }
149
150 pub fn push(&mut self, item: T) {
152 self.buffer[self.head] = Some(item);
153 self.head = (self.head + 1) % N;
154
155 if self.full {
156 } else {
158 self.count += 1;
159 if self.count == N {
160 self.full = true;
161 }
162 self.update_memory_stats();
163 }
164 }
165
166 pub fn len(&self) -> usize {
168 self.count
169 }
170
171 pub fn is_empty(&self) -> bool {
173 self.count == 0
174 }
175
176 pub fn is_full(&self) -> bool {
178 self.full
179 }
180
181 pub fn memory_stats(&self) -> &MemoryStats {
183 &self.memory_stats
184 }
185
186 pub fn clear(&mut self) {
188 self.buffer = [None; N];
189 self.head = 0;
190 self.count = 0;
191 self.full = false;
192 self.update_memory_stats();
193 }
194
195 pub fn iter(&self) -> impl Iterator<Item = T> + '_ {
197 let start_idx = if self.full { self.head } else { 0 };
198 let len = if self.full { N } else { self.count };
199
200 (0..len).filter_map(move |i| {
201 let idx = (start_idx + i) % N;
202 self.buffer[idx]
203 })
204 }
205
206 fn update_memory_stats(&mut self) {
208 let used = self.count * core::mem::size_of::<T>();
209 self.memory_stats.update_usage(used);
210 }
211}
212
213impl<T: Copy, const N: usize> Default for ManagedSlidingWindow<T, N> {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219#[derive(Debug, Clone)]
221pub struct LabelStorage<const MAX_LABELS: usize, const MAX_LENGTH: usize> {
222 labels: Vec<heapless::String<MAX_LENGTH>, MAX_LABELS>,
223 memory_stats: MemoryStats,
224}
225
226impl<const MAX_LABELS: usize, const MAX_LENGTH: usize> LabelStorage<MAX_LABELS, MAX_LENGTH> {
227 pub fn new() -> Self {
229 Self {
230 labels: Vec::new(),
231 memory_stats: MemoryStats::new(MAX_LABELS * MAX_LENGTH),
232 }
233 }
234
235 pub fn add_label(&mut self, label: &str) -> DataResult<usize> {
237 let mut string = heapless::String::new();
238 if string.push_str(label).is_err() {
239 return Err(DataError::INVALID_DATA_POINT);
240 }
241 let index = self.labels.len();
242 self.labels
243 .push(string)
244 .map_err(|_| DataError::BUFFER_FULL)?;
245 self.update_memory_stats();
246 Ok(index)
247 }
248
249 pub fn get_label(&self, index: usize) -> Option<&str> {
251 self.labels.get(index).map(|s| s.as_str())
252 }
253
254 pub fn len(&self) -> usize {
256 self.labels.len()
257 }
258
259 pub fn is_empty(&self) -> bool {
261 self.labels.is_empty()
262 }
263
264 pub fn clear(&mut self) {
266 self.labels.clear();
267 self.update_memory_stats();
268 }
269
270 pub fn memory_stats(&self) -> &MemoryStats {
272 &self.memory_stats
273 }
274
275 fn update_memory_stats(&mut self) {
277 let used = self.labels.iter().map(|s| s.len()).sum::<usize>();
278 self.memory_stats.update_usage(used);
279 }
280}
281
282impl<const MAX_LABELS: usize, const MAX_LENGTH: usize> Default
283 for LabelStorage<MAX_LABELS, MAX_LENGTH>
284{
285 fn default() -> Self {
286 Self::new()
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_memory_stats() {
296 let mut stats = MemoryStats::new(1000);
297 assert_eq!(stats.total_allocated, 1000);
298 assert_eq!(stats.used, 0);
299 assert_eq!(stats.available, 1000);
300
301 stats.update_usage(300);
302 assert_eq!(stats.used, 300);
303 assert_eq!(stats.available, 700);
304 assert!((stats.utilization_percent() - 30.0).abs() < 0.001);
305 }
306
307 #[test]
308 fn test_managed_sliding_window() {
309 let mut window: ManagedSlidingWindow<i32, 3> = ManagedSlidingWindow::new();
310 assert!(window.is_empty());
311 assert!(!window.is_full());
312
313 window.push(1);
314 window.push(2);
315 window.push(3);
316
317 assert_eq!(window.len(), 3);
318 assert!(window.is_full());
319
320 window.push(4);
322 assert_eq!(window.len(), 3);
323
324 let values: Vec<i32, 3> = window.iter().collect();
325 assert_eq!(values.as_slice(), &[2, 3, 4]);
326 }
327
328 #[test]
329 fn test_label_storage() {
330 let mut storage: LabelStorage<5, 20> = LabelStorage::new();
331 assert!(storage.is_empty());
332
333 let index1 = storage.add_label("Label 1").unwrap();
334 let index2 = storage.add_label("Label 2").unwrap();
335
336 assert_eq!(storage.len(), 2);
337 assert_eq!(storage.get_label(index1), Some("Label 1"));
338 assert_eq!(storage.get_label(index2), Some("Label 2"));
339 }
340
341 #[test]
342 fn test_memory_manager() {
343 let mut manager: ChartMemoryManager<1000> = ChartMemoryManager::new();
344 assert_eq!(manager.stats().total_allocated, 1000);
345
346 manager.update_usage(500);
347 assert_eq!(manager.high_water_mark(), 500);
348 assert!(!manager.is_memory_critical(60.0));
349 assert!(manager.is_memory_critical(40.0));
350 }
351}