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; } } }