diff --git a/src/grid/mod.rs b/src/grid/mod.rs index e790dd41..8e30915c 100644 --- a/src/grid/mod.rs +++ b/src/grid/mod.rs @@ -21,7 +21,6 @@ //! ranges is currently supported. use std::cmp::Ordering; -use std::collections::{VecDeque, vec_deque}; use std::iter::IntoIterator; use std::ops::{Deref, Range, RangeTo, RangeFrom, RangeFull, Index, IndexMut}; @@ -33,6 +32,9 @@ pub use self::row::Row; #[cfg(test)] mod tests; +mod storage; +use self::storage::Storage; + /// Convert a type to a linear index range. pub trait ToRange { fn to_range(&self) -> RangeInclusive; @@ -64,7 +66,7 @@ impl Deref for Indexed { pub struct Grid { /// Lines in the grid. Each row holds a list of cells corresponding to the /// columns in that row. - raw: VecDeque>, + raw: Storage>, /// Number of columns cols: index::Column, @@ -82,6 +84,9 @@ pub struct Grid { /// Template cell for populating template_row template: T, + + /// Temporary row storage for scrolling with a region + temp: Vec>, } pub struct GridIterator<'a, T: 'a> { @@ -91,10 +96,10 @@ pub struct GridIterator<'a, T: 'a> { impl Grid { pub fn new(lines: index::Line, cols: index::Column, template: T) -> Grid { - let mut raw = VecDeque::with_capacity(*lines); + let mut raw = Storage::with_capacity(*lines); let template_row = Row::new(cols, &template); for _ in IndexRange(index::Line(0)..lines) { - raw.push_back(template_row.clone()); + raw.push(template_row.clone()); } Grid { @@ -103,6 +108,7 @@ impl Grid { lines, template_row, template, + temp: Vec::new(), } } @@ -127,7 +133,7 @@ impl Grid { fn grow_lines(&mut self, lines: index::Line) { for _ in IndexRange(self.num_lines()..lines) { - self.raw.push_back(self.template_row.clone()); + self.raw.push(self.template_row.clone()); } self.lines = lines; @@ -147,12 +153,25 @@ impl Grid { #[inline] pub fn scroll_down(&mut self, region: &Range, positions: index::Line) { - if region.start == Line(0) && region.end == self.num_lines() { - // Full rotation - for _ in 0..positions.0 { - let mut item = self.raw.pop_back().unwrap(); - item.reset(&self.template_row); - self.raw.push_front(item); + // Whether or not there is a scrolling region active, as long as it + // starts at the top, we can do a full rotation which just involves + // changing the start index. + // + // To accomodate scroll regions, rows are reordered at the end. + if region.start == Line(0) { + // Rotate the entire line buffer. If there's a scrolling region + // active, the bottom lines are restored in the next step. + self.raw.rotate(-(*positions as isize)); + + // Now, restore any scroll region lines + for i in IndexRange(region.end .. self.num_lines()) { + // First do the swap + self.raw.swap(*i, *i + *positions); + } + + // Finally, reset recycled lines + for i in 0..*positions { + self.raw[i].reset(&self.template_row); } } else { // Subregion rotation @@ -160,23 +179,33 @@ impl Grid { self.swap_lines(line, line - positions); } - let template = &self.template_row; for i in IndexRange(Line(0)..positions) { - self.raw - .get_mut(*(region.start + i)) - .map(|row| row.reset(template)); + self.raw[*(region.start - i - 1)].reset(&self.template_row); } } } #[inline] pub fn scroll_up(&mut self, region: &Range, positions: index::Line) { - if region.start == Line(0) && region.end == self.num_lines() { - // Full rotation - for _ in 0..positions.0 { - let mut item = self.raw.pop_front().unwrap(); - item.reset(&self.template_row); - self.raw.push_back(item); + if region.start == Line(0) { + // Rotate the entire line buffer. If there's a scrolling region + // active, the bottom lines are restored in the next step. + self.raw.rotate(*positions as isize); + + // Now, restore any lines outside the scroll region + let mut i = 0; + for _ in IndexRange(region.end .. self.num_lines()) { + let idx = *self.num_lines() - i - 1; + // First do the swap + self.raw.swap(idx, idx - *positions); + i += 1; + } + + // Finally, reset recycled lines + // + // Recycled lines are just above the end of the scrolling region. + for i in 0..*positions { + self.raw[*region.end - i - 1].reset(&self.template_row); } } else { // Subregion rotation @@ -185,11 +214,8 @@ impl Grid { } // Clear reused lines - let template = &self.template_row; for i in IndexRange(Line(0)..positions) { - self.raw - .get_mut(*(region.end - i - 1)) - .map(|row| row.reset(template)); + self.raw[*(region.start - i - 1)].reset(&self.template_row); } } } @@ -229,7 +255,7 @@ impl Grid { fn shrink_lines(&mut self, lines: index::Line) { while index::Line(self.raw.len()) != lines { - self.raw.pop_back(); + self.raw.pop(); } self.lines = lines; @@ -322,10 +348,10 @@ impl<'point, T> IndexMut<&'point Point> for Grid { impl<'a, T> IntoIterator for &'a Grid { type Item = &'a Row; - type IntoIter = vec_deque::Iter<'a, Row>; + type IntoIter = self::storage::Iter<'a, Row>; #[inline] - fn into_iter(self) -> vec_deque::Iter<'a, Row> { + fn into_iter(self) -> self::storage::Iter<'a, Row> { self.raw.iter() } } @@ -340,7 +366,7 @@ impl<'a, T> IntoIterator for &'a Grid { pub struct Region<'a, T: 'a> { start: Line, end: Line, - raw: &'a VecDeque>, + raw: &'a Storage>, } /// A mutable subset of lines in the grid @@ -349,7 +375,7 @@ pub struct Region<'a, T: 'a> { pub struct RegionMut<'a, T: 'a> { start: Line, end: Line, - raw: &'a mut VecDeque>, + raw: &'a mut Storage>, } impl<'a, T> RegionMut<'a, T> { @@ -453,13 +479,13 @@ impl IndexRegion for Grid { pub struct RegionIter<'a, T: 'a> { end: Line, cur: Line, - raw: &'a VecDeque>, + raw: &'a Storage>, } pub struct RegionIterMut<'a, T: 'a> { end: Line, cur: Line, - raw: &'a mut VecDeque>, + raw: &'a mut Storage>, } impl<'a, T> IntoIterator for Region<'a, T> { diff --git a/src/grid/storage.rs b/src/grid/storage.rs new file mode 100644 index 00000000..f5c2d87e --- /dev/null +++ b/src/grid/storage.rs @@ -0,0 +1,129 @@ +/// Wrapper around Vec which supports fast indexing and rotation +/// +/// The rotation implemented by grid::Storage is a simple integer addition. +/// Compare with standard library rotation which requires rearranging items in +/// memory. +/// +/// As a consequence, the indexing operators need to be reimplemented for this +/// type to account for the 0th element not always being at the start of the +/// allocation. +/// +/// Because certain Vec operations are no longer valid on this type, no Deref +/// implementation is provided. Anything from Vec that should be exposed must be +/// done so manually. +use std::ops::{Index, IndexMut}; + +#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)] +pub struct Storage { + inner: Vec, + zero: usize, +} + +impl Storage { + #[inline] + pub fn with_capacity(cap: usize) -> Storage { + Storage { + inner: Vec::with_capacity(cap), + zero: 0 + } + } + + #[inline] + pub fn push(&mut self, item: T) { + self.inner.push(item) + } + + #[inline] + pub fn pop(&mut self) -> Option { + self.inner.pop() + } + + #[inline] + pub fn len(&self) -> usize { + self.inner.len() + } + + /// Compute actual index in underlying storage given the requested index. + #[inline] + fn compute_index(&self, requested: usize) -> usize { + (requested + self.zero) % self.len() + } + + pub fn swap(&mut self, a: usize, b: usize) { + let a = self.compute_index(a); + let b = self.compute_index(b); + self.inner.swap(a, b); + } + + pub fn iter(&self) -> Iter { + Iter { storage: self, index: 0 } + } + + pub fn iter_mut(&mut self) -> IterMut { + IterMut { storage: self, index: 0 } + } + + pub fn rotate(&mut self, count: isize) { + let len = self.len(); + assert!(count.abs() as usize <= len); + self.zero += (count + len as isize) as usize % len; + } +} + +impl Index for Storage { + type Output = T; + #[inline] + fn index(&self, index: usize) -> &T { + let index = self.compute_index(index); // borrowck + &self.inner[index] + } +} + +impl IndexMut for Storage { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut T { + let index = self.compute_index(index); // borrowck + &mut self.inner[index] + } +} + +pub struct Iter<'a, T: 'a> { + storage: &'a Storage, + index: usize, +} + +pub struct IterMut<'a, T: 'a> { + storage: &'a mut Storage, + index: usize, +} + +impl<'a, T: 'a> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option { + if self.index == self.storage.len() { + None + } else { + let res = Some(&self.storage[self.index]); + self.index += 1; + res + } + } +} + +impl<'a, T: 'a> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + fn next(&mut self) -> Option { + if self.index == self.storage.len() { + None + } else { + let index = self.index; + self.index += 1; + + unsafe { + Some(&mut *(&mut self.storage[index] as *mut _)) + } + } + } +}