Commit c61a912f authored by Joe Wilm's avatar Joe Wilm

Optimize Row::reset

Now, only cells that have been used are cleared. This is achieved by
using a "occupied" memo on the Row itself. The value, `occ`, is updated
wherever the Row is accessed mutably, and it's cleared to zero in
Row::reset.

The tests for grid scroll_up and scroll_down were updated to include a
test on the value `occ` and slightly refactored, but are otherwise
equivalent to the previous implementation of those tests.

Because of the change to the `Row` struct, the ref tests were updated so
Deserialization keeps working as expected.
parent 4b1a3b1e
......@@ -3,18 +3,31 @@
require 'json'
Dir.glob('./tests/ref/**/grid.json').each do |path|
puts "Migrating #{path}"
# Read contents
s = File.open(path) { |f| f.read }
# Parse
grid = JSON.parse(s)
# Check if it's already migrated / make this migration idempotent
next if grid['raw'][0][0].is_a? Array
# Normalize Storage serialization
if grid['raw'].is_a? Array
grid['raw'] = {
'inner' => grid['raw'][0],
'zero' => grid['raw'][1],
'visible_lines' => grid['raw'][2]
}
end
# Transform
grid['raw'].reverse!
grid['raw'] = [grid['raw'], 0, grid['lines'] - 1]
# Migrate Row serialization
grid['raw']['inner'].map! do |row|
if row.is_a? Hash
row
else
{ inner: row, occ: row.length }
end
end
# Write updated grid
File.open(path, 'w') { |f| f << JSON.generate(grid) }
......
......@@ -14,43 +14,78 @@
//! Defines the Row type which makes up lines in the grid
use std::ops::{Deref, DerefMut, Index, IndexMut};
use std::ops::{Index, IndexMut};
use std::ops::{Range, RangeTo, RangeFrom, RangeFull};
use std::cmp::{max, min};
use std::slice;
use index::Column;
/// A row in the grid
#[derive(Default, Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Row<T>(Vec<T>);
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct Row<T> {
inner: Vec<T>,
/// occupied entries
///
/// Semantically, this value can be understood as the **end** of an
/// Exclusive Range. Thus,
///
/// - Zero means there are no occupied entries
/// - 1 means there is a value at index zero, but nowhere else
/// - `occ == inner.len` means every value is occupied
pub(crate) occ: usize,
}
impl<T: PartialEq> PartialEq for Row<T> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T: Copy + Clone> Row<T> {
pub fn new(columns: Column, template: &T) -> Row<T> {
Row(vec![*template; *columns])
Row {
inner: vec![*template; *columns],
occ: 0,
}
}
pub fn grow(&mut self, cols: Column, template: &T) {
assert!(self.len() < * cols);
while self.len() != *cols {
self.0.push(*template);
self.inner.push(*template);
}
}
/// Resets contents to the contents of `other`
#[inline]
#[inline(never)]
pub fn reset(&mut self, other: &T) {
for item in &mut self.0 {
let occ = self.occ;
for item in &mut self.inner[..occ] {
*item = *other;
}
self.occ = 0;
}
}
impl<T> Row<T> {
pub fn shrink(&mut self, cols: Column) {
while self.len() != *cols {
self.pop();
self.inner.pop();
}
self.occ = min(self.occ, *cols);
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn iter<'a>(&'a self) -> slice::Iter<'a, T> {
self.inner.iter()
}
}
......@@ -71,24 +106,8 @@ impl<'a, T> IntoIterator for &'a mut Row<T> {
#[inline]
fn into_iter(self) -> slice::IterMut<'a, T> {
self.iter_mut()
}
}
impl<T> Deref for Row<T> {
type Target = Vec<T>;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Row<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
self.occ = self.len();
self.inner.iter_mut()
}
}
......@@ -97,14 +116,15 @@ impl<T> Index<Column> for Row<T> {
#[inline]
fn index(&self, index: Column) -> &T {
&self.0[index.0]
&self.inner[index.0]
}
}
impl<T> IndexMut<Column> for Row<T> {
#[inline]
fn index_mut(&mut self, index: Column) -> &mut T {
&mut self.0[index.0]
self.occ = max(self.occ, *index + 1);
&mut self.inner[index.0]
}
}
......@@ -117,14 +137,15 @@ impl<T> Index<Range<Column>> for Row<T> {
#[inline]
fn index(&self, index: Range<Column>) -> &[T] {
&self.0[(index.start.0)..(index.end.0)]
&self.inner[(index.start.0)..(index.end.0)]
}
}
impl<T> IndexMut<Range<Column>> for Row<T> {
#[inline]
fn index_mut(&mut self, index: Range<Column>) -> &mut [T] {
&mut self.0[(index.start.0)..(index.end.0)]
self.occ = max(self.occ, *index.end);
&mut self.inner[(index.start.0)..(index.end.0)]
}
}
......@@ -133,14 +154,15 @@ impl<T> Index<RangeTo<Column>> for Row<T> {
#[inline]
fn index(&self, index: RangeTo<Column>) -> &[T] {
&self.0[..(index.end.0)]
&self.inner[..(index.end.0)]
}
}
impl<T> IndexMut<RangeTo<Column>> for Row<T> {
#[inline]
fn index_mut(&mut self, index: RangeTo<Column>) -> &mut [T] {
&mut self.0[..(index.end.0)]
self.occ = max(self.occ, *index.end);
&mut self.inner[..(index.end.0)]
}
}
......@@ -149,14 +171,15 @@ impl<T> Index<RangeFrom<Column>> for Row<T> {
#[inline]
fn index(&self, index: RangeFrom<Column>) -> &[T] {
&self.0[(index.start.0)..]
&self.inner[(index.start.0)..]
}
}
impl<T> IndexMut<RangeFrom<Column>> for Row<T> {
#[inline]
fn index_mut(&mut self, index: RangeFrom<Column>) -> &mut [T] {
&mut self.0[(index.start.0)..]
self.occ = self.len();
&mut self.inner[(index.start.0)..]
}
}
......@@ -165,13 +188,14 @@ impl<T> Index<RangeFull> for Row<T> {
#[inline]
fn index(&self, _: RangeFull) -> &[T] {
&self.0[..]
&self.inner[..]
}
}
impl<T> IndexMut<RangeFull> for Row<T> {
#[inline]
fn index_mut(&mut self, _: RangeFull) -> &mut [T] {
&mut self.0[..]
self.occ = self.len();
&mut self.inner[..]
}
}
......@@ -20,73 +20,77 @@ use index::{Point, Line, Column};
// Scroll up moves lines upwards
#[test]
fn scroll_up() {
info!("");
println!("");
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
for i in 0..10 {
grid[Line(i)][Column(0)] = i;
}
info!("grid: {:?}", grid);
println!("grid: {:?}", grid);
grid.scroll_up(&(Line(0)..Line(10)), Line(2), &0);
info!("grid: {:?}", grid);
let mut other = Grid::new(Line(10), Column(1), 0, 9);
other[Line(0)][Column(0)] = 2;
other[Line(1)][Column(0)] = 3;
other[Line(2)][Column(0)] = 4;
other[Line(3)][Column(0)] = 5;
other[Line(4)][Column(0)] = 6;
other[Line(5)][Column(0)] = 7;
other[Line(6)][Column(0)] = 8;
other[Line(7)][Column(0)] = 9;
other[Line(8)][Column(0)] = 0; // should be cleared on scroll; was 0
other[Line(9)][Column(0)] = 0; // should be cleared on scroll; was 1
for i in 0..10 {
assert_eq!(grid[Line(i)][Column(0)], other[Line(i)][Column(0)],
"index={}; actual: {:?}, expected: {:?}",
Line(i), grid[Line(i)][Column(0)], other[Line(i)][Column(0)]);
}
println!("grid: {:?}", grid);
assert_eq!(grid[Line(0)][Column(0)], 2);
assert_eq!(grid[Line(0)].occ, 1);
assert_eq!(grid[Line(1)][Column(0)], 3);
assert_eq!(grid[Line(1)].occ, 1);
assert_eq!(grid[Line(2)][Column(0)], 4);
assert_eq!(grid[Line(2)].occ, 1);
assert_eq!(grid[Line(3)][Column(0)], 5);
assert_eq!(grid[Line(3)].occ, 1);
assert_eq!(grid[Line(4)][Column(0)], 6);
assert_eq!(grid[Line(4)].occ, 1);
assert_eq!(grid[Line(5)][Column(0)], 7);
assert_eq!(grid[Line(5)].occ, 1);
assert_eq!(grid[Line(6)][Column(0)], 8);
assert_eq!(grid[Line(6)].occ, 1);
assert_eq!(grid[Line(7)][Column(0)], 9);
assert_eq!(grid[Line(7)].occ, 1);
assert_eq!(grid[Line(8)][Column(0)], 0); // was 0
assert_eq!(grid[Line(8)].occ, 0);
assert_eq!(grid[Line(9)][Column(0)], 0); // was 1
assert_eq!(grid[Line(9)].occ, 0);
}
// Scroll down moves lines downwards
#[test]
fn scroll_down() {
info!("");
println!("");
let mut grid = Grid::new(Line(10), Column(1), 0, 0);
for i in 0..10 {
grid[Line(i)][Column(0)] = i;
}
info!("grid: {:?}", grid);
println!("grid: {:?}", grid);
grid.scroll_down(&(Line(0)..Line(10)), Line(2), &0);
info!("grid: {:?}", grid);
let mut other = Grid::new(Line(10), Column(1), 0, 9);
other[Line(0)][Column(0)] = 0; // Should be cleared upon recycle; was 8
other[Line(1)][Column(0)] = 0; // Should be cleared upon recycle; was 9
other[Line(2)][Column(0)] = 0;
other[Line(3)][Column(0)] = 1;
other[Line(4)][Column(0)] = 2;
other[Line(5)][Column(0)] = 3;
other[Line(6)][Column(0)] = 4;
other[Line(7)][Column(0)] = 5;
other[Line(8)][Column(0)] = 6;
other[Line(9)][Column(0)] = 7;
for i in 0..10 {
assert_eq!(grid[Line(i)][Column(0)], other[Line(i)][Column(0)],
"index={}; actual: {:?}, expected: {:?}",
Line(i), grid[Line(i)][Column(0)], other[Line(i)][Column(0)]);
}
println!("grid: {:?}", grid);
assert_eq!(grid[Line(0)][Column(0)], 0); // was 8
assert_eq!(grid[Line(0)].occ, 0);
assert_eq!(grid[Line(1)][Column(0)], 0); // was 9
assert_eq!(grid[Line(1)].occ, 0);
assert_eq!(grid[Line(2)][Column(0)], 0);
assert_eq!(grid[Line(2)].occ, 1);
assert_eq!(grid[Line(3)][Column(0)], 1);
assert_eq!(grid[Line(3)].occ, 1);
assert_eq!(grid[Line(4)][Column(0)], 2);
assert_eq!(grid[Line(4)].occ, 1);
assert_eq!(grid[Line(5)][Column(0)], 3);
assert_eq!(grid[Line(5)].occ, 1);
assert_eq!(grid[Line(6)][Column(0)], 4);
assert_eq!(grid[Line(6)].occ, 1);
assert_eq!(grid[Line(7)][Column(0)], 5);
assert_eq!(grid[Line(7)].occ, 1);
assert_eq!(grid[Line(8)][Column(0)], 6);
assert_eq!(grid[Line(8)].occ, 1);
assert_eq!(grid[Line(9)][Column(0)], 7);
assert_eq!(grid[Line(9)].occ, 1);
}
// Test that GridIterator works
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment