1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! A circular buffer.
//!
//! See the documentation on the [CircularBuffer] type.

use std::ops::IndexMut;

/// A circular buffer.
///
/// A circular buffer is an ordered buffer with a fixed capacity and the property that if it is full
/// and a new element is pushed to the front, the oldest element is deleted.
///
/// The buffer is created using the [new](CircularBuffer::new) method, elements are pushed to the
/// front using [push](CircularBuffer::push), and retrieved using
/// [std::ops::Index::index] trait method.
///
/// ```
/// # use texcraft_stdext::collections::circularbuffer::CircularBuffer;
/// # use std::ops::Index;
/// let mut buf = CircularBuffer::new(3);
/// buf.push(0);
/// buf.push(1);
/// assert_eq![buf.index(0), &1];
/// assert_eq![buf.index(1), &0];
/// buf.push(2);
/// buf.push(3);
/// assert_eq![buf.index(0), &3];
/// assert_eq![buf.index(1), &2];
/// assert_eq![buf.index(2), &1];
/// ```
pub struct CircularBuffer<T> {
    data: Vec<T>,
    head: usize,
}

impl<T> CircularBuffer<T> {
    /// Create a new circular buffer with the provided capacity.
    pub fn new(capacity: usize) -> CircularBuffer<T> {
        CircularBuffer {
            data: Vec::with_capacity(capacity),
            head: 0,
        }
    }

    /// Push a new element to the front of the buffer.
    pub fn push(&mut self, elem: T) {
        if self.data.len() < self.data.capacity() {
            self.data.push(elem);
            self.head = self.data.len() - 1;
            return;
        }
        self.head = (self.head + 1) % self.capacity();
        *self.data.index_mut(self.head) = elem;
    }

    /// Return the buffer's capacity.
    pub fn capacity(&self) -> usize {
        self.data.capacity()
    }

    fn internal_index(&self, i: usize) -> usize {
        (self.head + self.capacity() - i) % self.capacity()
    }
}

impl<T> std::ops::Index<usize> for CircularBuffer<T> {
    type Output = T;

    /// Get the element at the provided index.
    ///
    /// The first element in the buffer has index 0, the next element index 1, etc.
    ///
    /// This method may panic if the index is valid.
    fn index(&self, i: usize) -> &T {
        &self.data[self.internal_index(i)]
    }
}

impl<T: Clone> CircularBuffer<T> {
    /// Clone the element at the provided index to the front of the buffer.
    ///
    /// The buffer contains the following optimization: if the buffer is full and the
    /// referenced element is the tail element, this method is a no-op.
    /// If the method's clone function has side effects, these will not be seen, as in
    /// the following example.
    ///
    /// ```
    /// # use texcraft_stdext::collections::circularbuffer::CircularBuffer;
    /// # use std::rc::Rc;
    /// # use std::cell::RefCell;
    /// /// A struct whose clone function has a side effect; i.e., is not pure.
    /// /// The side effect is that a shared counter owned by all clones is incremented.
    /// /// The shared counter thus records the number of times a clone has occured.
    /// struct ImpureClone {
    ///     counter: Rc<RefCell<i64>>,
    /// }
    ///
    /// impl Clone for ImpureClone {
    ///     fn clone(&self) -> Self {
    ///         let old_count = *self.counter.borrow();
    ///         *self.counter.borrow_mut() = old_count + 1;
    ///         ImpureClone {
    ///             counter: self.counter.clone()
    ///         }
    ///     }
    /// }
    ///
    /// let mut buf = CircularBuffer::new(2);
    /// let counter = Rc::new(RefCell::new(0));
    /// buf.push(ImpureClone{counter: counter.clone()});
    /// assert_eq![*counter.borrow(), 0];
    ///
    /// buf.clone_to_front(0);
    /// assert_eq![*counter.borrow(), 1];
    ///
    /// // Clone from the tail - no clone occurs!
    /// buf.clone_to_front(1);
    /// assert_eq![*counter.borrow(), 1];
    /// ```
    pub fn clone_to_front(&mut self, i: usize) -> &mut T {
        let i = self.internal_index(i);
        if self.data.len() < self.data.capacity() {
            self.push(self.data[i].clone());
            return self.data.last_mut().unwrap();
        }
        let tail_i = (self.head + 1) % self.capacity();
        match i.cmp(&tail_i) {
            std::cmp::Ordering::Equal => {
                // If we're cloning the current tail, we just make it the head by incrementing
                // the head ptr. No need to clone in this case!
            }
            std::cmp::Ordering::Less => {
                let (front, back) = self.data.split_at_mut(tail_i);
                back[0].clone_from(&front[i]);
            }
            std::cmp::Ordering::Greater => {
                let (front, back) = self.data.split_at_mut(i);
                front[tail_i].clone_from(&back[0]);
            }
        }
        self.head = tail_i;
        self.data.index_mut(self.head)
    }
}