From 785eccc004f4a39c271c8ecf8e3dd7f67ed1f901 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Wed, 28 Jun 2023 12:11:54 +0100 Subject: [PATCH] Add capacity to RingBuffer --- crates/matrix-sdk-common/src/ring_buffer.rs | 74 +++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/crates/matrix-sdk-common/src/ring_buffer.rs b/crates/matrix-sdk-common/src/ring_buffer.rs index 3e5a12049..49bdee7da 100644 --- a/crates/matrix-sdk-common/src/ring_buffer.rs +++ b/crates/matrix-sdk-common/src/ring_buffer.rs @@ -17,6 +17,9 @@ use std::collections::{vec_deque::Iter, VecDeque}; use serde::{self, Deserialize, Serialize}; /// A simple fixed-size ring buffer implementation. +/// +/// A size is provided on creation, and the ring buffer reserves that much +/// space, and never reallocates. #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] #[serde(transparent)] pub struct RingBuffer { @@ -24,22 +27,34 @@ pub struct RingBuffer { } impl RingBuffer { + /// Create a ring buffer with the supplied capacity, reserving it so we + /// never need to reallocate. pub fn new(size: usize) -> Self { Self { inner: VecDeque::with_capacity(size) } } + /// Returns the number of items that are stored in this ring buffer. + /// + /// This is the dynamic size indicating how many items are held in the + /// buffer, not the fixed capacity. pub fn len(&self) -> usize { self.inner.len() } + /// Returns true if the ring buffer contains no elements. pub fn is_empty(&self) -> bool { self.inner.is_empty() } + /// Provides a reference to the element at the given index. + /// + /// Element at index zero is the "front" i.e. one that will be returned if + /// we call pop(). pub fn get(&self, index: usize) -> Option<&T> { self.inner.get(index) } + /// Appends an element to the back of the ring buffer pub fn push(&mut self, value: T) { if self.inner.len() == self.inner.capacity() { self.inner.pop_front(); @@ -48,17 +63,28 @@ impl RingBuffer { self.inner.push_back(value); } + /// Removes the first element and returns it, or None if the ring buffer is + /// empty. pub fn pop(&mut self) -> Option { self.inner.pop_front() } + /// Returns an iterator that provides elements in front-to-back order, i.e. + /// the same order you would get if you repeatedly called pop(). pub fn iter(&self) -> Iter<'_, T> { self.inner.iter() } + /// Clears the ring buffer, removing all values. This does not affect the + /// capacity. pub fn clear(&mut self) { self.inner.clear(); } + + /// Returns the total number of elements the vector can hold + pub fn capacity(&self) -> usize { + self.inner.capacity() + } } impl Extend for RingBuffer { @@ -174,6 +200,54 @@ mod tests { assert_eq!(ring_buffer.pop(), None); } + #[test] + fn clear_does_not_affect_capacity() { + // Given a RingBuffer that has been used + let mut ring_buffer = RingBuffer::new(3); + ring_buffer.push(4); + ring_buffer.push(5); + ring_buffer.push(6); + ring_buffer.pop(); + // Sanity: capacity is 3 + assert_eq!(ring_buffer.capacity(), 3); + + // When I clear it + ring_buffer.clear(); + + // Then its capacity is still 3 + assert_eq!(ring_buffer.capacity(), 3); + } + + #[test] + fn capacity_is_what_we_passed_to_new() { + // Given a RingBuffer + let ring_buffer = RingBuffer::::new(13); + // When I ask for its capacity I get what I provided at the start + assert_eq!(ring_buffer.capacity(), 13); + } + + #[test] + fn capacity_is_not_affected_by_overflowing() { + // Given a RingBuffer that has been used + let mut ring_buffer = RingBuffer::new(3); + ring_buffer.push(4); + ring_buffer.push(5); + ring_buffer.push(6); + ring_buffer.push(7); + ring_buffer.pop(); + ring_buffer.push(8); + ring_buffer.push(9); + + // When I ask for its capacity, it gives me what I gave it initially + assert_eq!(ring_buffer.capacity(), 3); + + // And even if I extend it + ring_buffer.extend(vec![10, 11, 12, 13, 14, 15]); + + // Then its capacity is still 3 + assert_eq!(ring_buffer.capacity(), 3); + } + #[test] fn roundtrip_serialization() { // Given a RingBuffer