1#![forbid(unsafe_code)]
2
3use std::collections::VecDeque;
27use std::ops::Range;
28
29#[derive(Debug, Clone)]
34pub struct LogRing<T> {
35 ring: VecDeque<T>,
37
38 capacity: usize,
40
41 total_count: usize,
43}
44
45impl<T> LogRing<T> {
46 #[must_use]
52 pub fn new(capacity: usize) -> Self {
53 assert!(capacity > 0, "LogRing capacity must be greater than 0");
54 Self {
55 ring: VecDeque::with_capacity(capacity),
56 capacity,
57 total_count: 0,
58 }
59 }
60
61 pub fn push(&mut self, item: T) {
65 self.total_count = self.total_count.saturating_add(1);
66
67 if self.ring.len() >= self.capacity {
68 self.ring.pop_front();
69 }
70
71 self.ring.push_back(item);
72 }
73
74 pub fn extend(&mut self, items: impl IntoIterator<Item = T>) {
76 for item in items {
77 self.push(item);
78 }
79 }
80
81 #[must_use = "use the returned item (if any)"]
85 pub fn get(&self, absolute_idx: usize) -> Option<&T> {
86 let ring_start = self.first_index();
87
88 if absolute_idx >= ring_start && absolute_idx < self.total_count {
89 self.ring.get(absolute_idx - ring_start)
90 } else {
91 None
92 }
93 }
94
95 #[must_use = "use the returned item (if any)"]
97 pub fn get_mut(&mut self, absolute_idx: usize) -> Option<&mut T> {
98 let ring_start = self.first_index();
99
100 if absolute_idx >= ring_start && absolute_idx < self.total_count {
101 self.ring.get_mut(absolute_idx - ring_start)
102 } else {
103 None
104 }
105 }
106
107 pub fn get_range(&self, range: Range<usize>) -> impl Iterator<Item = &T> {
112 let ring_start = self.first_index();
113 let ring_end = self.total_count;
114
115 let start = range.start.max(ring_start);
117 let end = range.end.min(ring_end);
118
119 (start..end).filter_map(move |i| self.get(i))
120 }
121
122 #[must_use]
124 pub const fn total_count(&self) -> usize {
125 self.total_count
126 }
127
128 #[inline]
130 #[must_use]
131 pub fn len(&self) -> usize {
132 self.ring.len()
133 }
134
135 #[inline]
137 #[must_use]
138 pub fn is_empty(&self) -> bool {
139 self.ring.is_empty()
140 }
141
142 #[must_use]
144 pub const fn capacity(&self) -> usize {
145 self.capacity
146 }
147
148 #[must_use]
150 pub fn first_index(&self) -> usize {
151 self.total_count.saturating_sub(self.ring.len())
152 }
153
154 #[must_use = "use the returned index (if any)"]
158 pub fn last_index(&self) -> Option<usize> {
159 if self.total_count > 0 {
160 Some(self.total_count - 1)
161 } else {
162 None
163 }
164 }
165
166 #[must_use]
168 pub fn is_in_memory(&self, absolute_idx: usize) -> bool {
169 absolute_idx >= self.first_index() && absolute_idx < self.total_count
170 }
171
172 #[must_use]
174 pub fn evicted_count(&self) -> usize {
175 self.first_index()
176 }
177
178 pub fn clear(&mut self) {
182 self.ring.clear();
183 }
184
185 pub fn reset(&mut self) {
187 self.ring.clear();
188 self.total_count = 0;
189 }
190
191 #[must_use = "use the returned item (if any)"]
193 pub fn back(&self) -> Option<&T> {
194 self.ring.back()
195 }
196
197 #[must_use = "use the returned item (if any)"]
199 pub fn front(&self) -> Option<&T> {
200 self.ring.front()
201 }
202
203 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &T> {
205 self.ring.iter()
206 }
207
208 pub fn iter_indexed(&self) -> impl DoubleEndedIterator<Item = (usize, &T)> {
210 let start = self.first_index();
211 self.ring
212 .iter()
213 .enumerate()
214 .map(move |(i, item)| (start + i, item))
215 }
216
217 pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
219 self.ring.drain(..)
220 }
221}
222
223impl<T> Default for LogRing<T> {
224 fn default() -> Self {
225 Self::new(1024) }
227}
228
229impl<T> Extend<T> for LogRing<T> {
230 fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
231 for item in iter {
232 self.push(item);
233 }
234 }
235}
236
237impl<T> FromIterator<T> for LogRing<T> {
238 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
239 let items: Vec<T> = iter.into_iter().collect();
240 let capacity = items.len().max(1);
241 let mut ring = Self::new(capacity);
242 ring.extend(items);
243 ring
244 }
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250
251 #[test]
252 fn new_creates_empty_ring() {
253 let ring: LogRing<i32> = LogRing::new(10);
254 assert!(ring.is_empty());
255 assert_eq!(ring.len(), 0);
256 assert_eq!(ring.total_count(), 0);
257 assert_eq!(ring.capacity(), 10);
258 }
259
260 #[test]
261 #[should_panic(expected = "capacity must be greater than 0")]
262 fn new_panics_on_zero_capacity() {
263 let _ring: LogRing<i32> = LogRing::new(0);
264 }
265
266 #[test]
267 fn push_adds_items() {
268 let mut ring = LogRing::new(5);
269 ring.push("a");
270 ring.push("b");
271 ring.push("c");
272
273 assert_eq!(ring.len(), 3);
274 assert_eq!(ring.total_count(), 3);
275 assert_eq!(ring.get(0), Some(&"a"));
276 assert_eq!(ring.get(1), Some(&"b"));
277 assert_eq!(ring.get(2), Some(&"c"));
278 }
279
280 #[test]
281 fn push_evicts_oldest_when_full() {
282 let mut ring = LogRing::new(3);
283 ring.push(1);
284 ring.push(2);
285 ring.push(3);
286 ring.push(4); ring.push(5); assert_eq!(ring.len(), 3);
290 assert_eq!(ring.total_count(), 5);
291 assert_eq!(ring.get(0), None); assert_eq!(ring.get(1), None); assert_eq!(ring.get(2), Some(&3));
294 assert_eq!(ring.get(3), Some(&4));
295 assert_eq!(ring.get(4), Some(&5));
296 }
297
298 #[test]
299 fn first_and_last_index() {
300 let mut ring = LogRing::new(3);
301 assert_eq!(ring.first_index(), 0);
302 assert_eq!(ring.last_index(), None);
303
304 ring.push("a");
305 ring.push("b");
306 assert_eq!(ring.first_index(), 0);
307 assert_eq!(ring.last_index(), Some(1));
308
309 ring.push("c");
310 ring.push("d"); assert_eq!(ring.first_index(), 1);
312 assert_eq!(ring.last_index(), Some(3));
313 }
314
315 #[test]
316 fn get_range_returns_available_items() {
317 let mut ring = LogRing::new(3);
318 ring.push("a");
319 ring.push("b");
320 ring.push("c");
321 ring.push("d"); let items: Vec<_> = ring.get_range(0..5).collect();
324 assert_eq!(items, vec![&"b", &"c", &"d"]);
325
326 let items: Vec<_> = ring.get_range(2..4).collect();
327 assert_eq!(items, vec![&"c", &"d"]);
328 }
329
330 #[test]
331 fn is_in_memory() {
332 let mut ring = LogRing::new(2);
333 ring.push(1);
334 ring.push(2);
335 ring.push(3); assert!(!ring.is_in_memory(0));
338 assert!(ring.is_in_memory(1));
339 assert!(ring.is_in_memory(2));
340 assert!(!ring.is_in_memory(3));
341 }
342
343 #[test]
344 fn evicted_count() {
345 let mut ring = LogRing::new(2);
346 assert_eq!(ring.evicted_count(), 0);
347
348 ring.push(1);
349 ring.push(2);
350 assert_eq!(ring.evicted_count(), 0);
351
352 ring.push(3); assert_eq!(ring.evicted_count(), 1);
354
355 ring.push(4); assert_eq!(ring.evicted_count(), 2);
357 }
358
359 #[test]
360 fn clear_preserves_total_count() {
361 let mut ring = LogRing::new(5);
362 ring.push(1);
363 ring.push(2);
364 ring.push(3);
365
366 ring.clear();
367 assert!(ring.is_empty());
368 assert_eq!(ring.total_count(), 3);
369 assert_eq!(ring.first_index(), 3);
370 }
371
372 #[test]
373 fn reset_clears_everything() {
374 let mut ring = LogRing::new(5);
375 ring.push(1);
376 ring.push(2);
377 ring.push(3);
378
379 ring.reset();
380 assert!(ring.is_empty());
381 assert_eq!(ring.total_count(), 0);
382 assert_eq!(ring.first_index(), 0);
383 }
384
385 #[test]
386 fn front_and_back() {
387 let mut ring = LogRing::new(3);
388 assert_eq!(ring.front(), None);
389 assert_eq!(ring.back(), None);
390
391 ring.push("first");
392 ring.push("middle");
393 ring.push("last");
394
395 assert_eq!(ring.front(), Some(&"first"));
396 assert_eq!(ring.back(), Some(&"last"));
397
398 ring.push("newest"); assert_eq!(ring.front(), Some(&"middle"));
400 assert_eq!(ring.back(), Some(&"newest"));
401 }
402
403 #[test]
404 fn iter_yields_oldest_to_newest() {
405 let mut ring = LogRing::new(3);
406 ring.push(1);
407 ring.push(2);
408 ring.push(3);
409
410 let items: Vec<_> = ring.iter().copied().collect();
411 assert_eq!(items, vec![1, 2, 3]);
412 }
413
414 #[test]
415 fn iter_indexed_includes_absolute_indices() {
416 let mut ring = LogRing::new(2);
417 ring.push("a");
418 ring.push("b");
419 ring.push("c"); let indexed: Vec<_> = ring.iter_indexed().collect();
422 assert_eq!(indexed, vec![(1, &"b"), (2, &"c")]);
423 }
424
425 #[test]
426 fn extend_adds_multiple_items() {
427 let mut ring = LogRing::new(5);
428 ring.extend(vec![1, 2, 3]);
429
430 assert_eq!(ring.len(), 3);
431 assert_eq!(ring.total_count(), 3);
432 }
433
434 #[test]
435 fn from_iter_creates_ring() {
436 let ring: LogRing<i32> = vec![1, 2, 3, 4, 5].into_iter().collect();
437 assert_eq!(ring.len(), 5);
438 assert_eq!(ring.capacity(), 5);
439 }
440
441 #[test]
442 fn default_has_reasonable_capacity() {
443 let ring: LogRing<i32> = LogRing::default();
444 assert_eq!(ring.capacity(), 1024);
445 }
446
447 #[test]
448 fn get_mut_allows_modification() {
449 let mut ring = LogRing::new(3);
450 ring.push(1);
451 ring.push(2);
452
453 if let Some(item) = ring.get_mut(0) {
454 *item = 10;
455 }
456
457 assert_eq!(ring.get(0), Some(&10));
458 }
459
460 #[test]
461 fn drain_removes_all_items() {
462 let mut ring = LogRing::new(5);
463 ring.push(1);
464 ring.push(2);
465 ring.push(3);
466
467 let drained: Vec<_> = ring.drain().collect();
468 assert_eq!(drained, vec![1, 2, 3]);
469 assert!(ring.is_empty());
470 assert_eq!(ring.total_count(), 3); }
472
473 #[test]
474 fn handles_large_total_count() {
475 let mut ring = LogRing::new(2);
476 for i in 0..1000 {
477 ring.push(i);
478 }
479
480 assert_eq!(ring.len(), 2);
481 assert_eq!(ring.total_count(), 1000);
482 assert_eq!(ring.first_index(), 998);
483 assert_eq!(ring.get(998), Some(&998));
484 assert_eq!(ring.get(999), Some(&999));
485 }
486
487 #[test]
490 fn capacity_one_ring() {
491 let mut ring = LogRing::new(1);
492 ring.push("a");
493 assert_eq!(ring.len(), 1);
494 assert_eq!(ring.get(0), Some(&"a"));
495
496 ring.push("b"); assert_eq!(ring.len(), 1);
498 assert_eq!(ring.total_count(), 2);
499 assert_eq!(ring.get(0), None);
500 assert_eq!(ring.get(1), Some(&"b"));
501 assert_eq!(ring.first_index(), 1);
502 }
503
504 #[test]
505 fn get_mut_evicted_returns_none() {
506 let mut ring = LogRing::new(2);
507 ring.push(1);
508 ring.push(2);
509 ring.push(3); assert!(ring.get_mut(0).is_none());
511 }
512
513 #[test]
514 fn get_mut_beyond_total_returns_none() {
515 let mut ring = LogRing::new(5);
516 ring.push(1);
517 assert!(ring.get_mut(1).is_none());
518 assert!(ring.get_mut(100).is_none());
519 }
520
521 #[test]
522 fn get_beyond_total_count() {
523 let mut ring = LogRing::new(5);
524 ring.push(1);
525 assert_eq!(ring.get(1), None); assert_eq!(ring.get(usize::MAX), None);
527 }
528
529 #[test]
530 fn get_range_empty_range() {
531 let mut ring = LogRing::new(5);
532 ring.push(1);
533 ring.push(2);
534 let items: Vec<_> = ring.get_range(1..1).collect();
535 assert!(items.is_empty());
536 }
537
538 #[test]
539 fn get_range_inverted_start_gt_end() {
540 let mut ring = LogRing::new(5);
541 ring.push(1);
542 ring.push(2);
543 let start = std::hint::black_box(5usize);
546 let end = std::hint::black_box(2usize);
547 let items: Vec<_> = ring.get_range(start..end).collect();
548 assert!(items.is_empty());
549 }
550
551 #[test]
552 fn get_range_fully_evicted() {
553 let mut ring = LogRing::new(2);
554 ring.push(1);
555 ring.push(2);
556 ring.push(3);
557 ring.push(4);
558 let items: Vec<_> = ring.get_range(0..2).collect();
560 assert!(items.is_empty());
561 }
562
563 #[test]
564 fn get_range_fully_future() {
565 let mut ring = LogRing::new(5);
566 ring.push(1);
567 let items: Vec<_> = ring.get_range(5..10).collect();
569 assert!(items.is_empty());
570 }
571
572 #[test]
573 fn get_range_partial_overlap() {
574 let mut ring = LogRing::new(3);
575 ring.push(10);
576 ring.push(20);
577 ring.push(30);
578 ring.push(40); let items: Vec<_> = ring.get_range(0..5).collect();
581 assert_eq!(items, vec![&20, &30, &40]);
582 }
583
584 #[test]
585 fn iter_empty_ring() {
586 let ring: LogRing<i32> = LogRing::new(5);
587 assert_eq!(ring.iter().count(), 0);
588 }
589
590 #[test]
591 fn iter_indexed_empty_ring() {
592 let ring: LogRing<i32> = LogRing::new(5);
593 assert_eq!(ring.iter_indexed().count(), 0);
594 }
595
596 #[test]
597 fn iter_reverse() {
598 let mut ring = LogRing::new(3);
599 ring.push(1);
600 ring.push(2);
601 ring.push(3);
602 let rev: Vec<_> = ring.iter().rev().copied().collect();
603 assert_eq!(rev, vec![3, 2, 1]);
604 }
605
606 #[test]
607 fn iter_indexed_reverse() {
608 let mut ring = LogRing::new(2);
609 ring.push("a");
610 ring.push("b");
611 ring.push("c"); let rev: Vec<_> = ring.iter_indexed().rev().collect();
613 assert_eq!(rev, vec![(2, &"c"), (1, &"b")]);
614 }
615
616 #[test]
617 fn extend_empty_iterator() {
618 let mut ring = LogRing::new(5);
619 ring.push(1);
620 ring.extend(std::iter::empty::<i32>());
621 assert_eq!(ring.len(), 1);
622 assert_eq!(ring.total_count(), 1);
623 }
624
625 #[test]
626 fn extend_trait_impl() {
627 let mut ring = LogRing::new(5);
628 Extend::extend(&mut ring, vec![1, 2, 3]);
629 assert_eq!(ring.len(), 3);
630 assert_eq!(ring.total_count(), 3);
631 }
632
633 #[test]
634 fn from_iter_empty() {
635 let ring: LogRing<i32> = std::iter::empty().collect();
636 assert_eq!(ring.capacity(), 1); assert!(ring.is_empty());
638 }
639
640 #[test]
641 fn from_iter_single() {
642 let ring: LogRing<i32> = std::iter::once(42).collect();
643 assert_eq!(ring.capacity(), 1);
644 assert_eq!(ring.len(), 1);
645 assert_eq!(ring.get(0), Some(&42));
646 }
647
648 #[test]
649 fn clone_independence() {
650 let mut ring = LogRing::new(5);
651 ring.push(1);
652 ring.push(2);
653 let mut cloned = ring.clone();
654 cloned.push(3);
655 assert_eq!(ring.len(), 2);
656 assert_eq!(cloned.len(), 3);
657 assert_eq!(ring.total_count(), 2);
658 assert_eq!(cloned.total_count(), 3);
659 }
660
661 #[test]
662 fn debug_format() {
663 let mut ring = LogRing::new(3);
664 ring.push(1);
665 let dbg = format!("{:?}", ring);
666 assert!(dbg.contains("LogRing"));
667 }
668
669 #[test]
670 fn drain_empty_ring() {
671 let mut ring: LogRing<i32> = LogRing::new(5);
672 let drained: Vec<_> = ring.drain().collect();
673 assert!(drained.is_empty());
674 assert_eq!(ring.total_count(), 0);
675 }
676
677 #[test]
678 fn clear_then_push_continues_absolute_index() {
679 let mut ring = LogRing::new(5);
680 ring.push("a");
681 ring.push("b");
682 ring.clear();
683 assert_eq!(ring.total_count(), 2);
684 assert_eq!(ring.first_index(), 2);
685
686 ring.push("c");
687 assert_eq!(ring.total_count(), 3);
688 assert_eq!(ring.first_index(), 2);
689 assert_eq!(ring.get(2), Some(&"c"));
690 assert_eq!(ring.get(0), None); assert_eq!(ring.get(1), None);
692 }
693
694 #[test]
695 fn reset_then_push_starts_fresh() {
696 let mut ring = LogRing::new(5);
697 ring.push("a");
698 ring.push("b");
699 ring.reset();
700 ring.push("c");
701 assert_eq!(ring.total_count(), 1);
702 assert_eq!(ring.first_index(), 0);
703 assert_eq!(ring.get(0), Some(&"c"));
704 }
705
706 #[test]
707 fn last_index_after_clear() {
708 let mut ring = LogRing::new(5);
709 ring.push(1);
710 ring.clear();
711 assert_eq!(ring.last_index(), Some(0));
712 }
714
715 #[test]
716 fn last_index_after_reset() {
717 let mut ring = LogRing::new(5);
718 ring.push(1);
719 ring.reset();
720 assert_eq!(ring.last_index(), None);
721 }
722
723 #[test]
724 fn front_back_after_clear() {
725 let mut ring = LogRing::new(5);
726 ring.push(1);
727 ring.clear();
728 assert_eq!(ring.front(), None);
729 assert_eq!(ring.back(), None);
730 }
731
732 #[test]
733 fn is_in_memory_at_exact_boundaries() {
734 let mut ring = LogRing::new(3);
735 ring.push(10);
736 ring.push(20);
737 ring.push(30);
738 assert!(ring.is_in_memory(0));
740 assert!(ring.is_in_memory(2));
741 assert!(!ring.is_in_memory(3)); }
743
744 #[test]
745 fn extend_causes_eviction() {
746 let mut ring = LogRing::new(3);
747 ring.extend(vec![1, 2, 3, 4, 5]);
748 assert_eq!(ring.len(), 3);
749 assert_eq!(ring.total_count(), 5);
750 assert_eq!(ring.get(2), Some(&3));
751 assert_eq!(ring.get(4), Some(&5));
752 assert_eq!(ring.get(0), None); }
754
755 #[test]
756 fn get_range_exact_memory_window() {
757 let mut ring = LogRing::new(3);
758 ring.push(1);
759 ring.push(2);
760 ring.push(3);
761 ring.push(4); let items: Vec<_> = ring.get_range(1..4).collect();
763 assert_eq!(items, vec![&2, &3, &4]);
764 }
765
766 #[test]
767 fn push_with_string_types() {
768 let mut ring = LogRing::new(2);
769 ring.push(String::from("hello"));
770 ring.push(String::from("world"));
771 ring.push(String::from("foo")); assert_eq!(ring.get(1), Some(&String::from("world")));
773 assert_eq!(ring.get(2), Some(&String::from("foo")));
774 }
775}