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
/// Data structure which uses a buffer to perform buffered updates to data.
///
/// Explicitly, this structure keeps two copies of some generic data type, which
/// can be accesssed by the methods `old()` and `new()`. The idea is that during
/// the course of an algorithm, you read from the `old` values and write to the
/// `new` values. Then after the step is finished, you call `swap_buffer` such
/// that `old` now points towards `new`.
///
/// Since you read from `old` and write to `new`, the methods `old()` and
/// `new()` return `&T` and `&mut T` respectively. This prevents any borrowing
/// conflicts.
///
/// This data structure provides three main benefits:
///   1. It makes the update step in an algorithm clean and explicit.
///   2. It allocates all memory used at initialization so the algorithm doesn't
///      have to allocate memory in each step.
///   3. It plays well with the rust borrow checker.
///
pub struct UpdateBuffer<T: Sized + Clone> {
  buffers: [T; 2],
  old_index: usize,
  new_index: usize,
}

impl<T: Sized + Clone> UpdateBuffer<T> {

  /// Creates a new update buffer with the given data written to the old and new buffers.
  pub fn from(initial: T) -> UpdateBuffer<T> {
    UpdateBuffer {
      buffers: [initial.clone(), initial],
      old_index: 0,
      new_index: 1,
    }
  }

  /// Get mutable access to the new values in the buffer.
  pub fn new(&mut self) -> &mut T {
    &mut self.buffers[self.new_index]
  }

  /// Get immutable access to the old values in the buffer.
  pub fn old(&self) -> &T {
    &self.buffers[self.old_index]
  }

  /// Swap the new and the old buffers (i.e. set the new updated data to be the old data).
  pub fn swap_buffers(&mut self) {
    if self.old_index == 1 {
      self.old_index = 0;
      self.new_index = 1;
    } else {
      self.old_index = 1;
      self.new_index = 0;
    }
  }
}