Merge pull request #1 from becheran/macro

Macro init
This commit is contained in:
Armin Becher 2020-04-04 18:45:52 +02:00 committed by GitHub
commit 4732783d4a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 327 additions and 97 deletions

View file

@ -4,10 +4,9 @@ version = "0.1.0"
authors = ["Armin Becher <armin.becher@gmai.com>"] authors = ["Armin Becher <armin.becher@gmai.com>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dev-dependencies] [dev-dependencies]
criterion = "0.3.1" criterion = "0.3.1"
rand="0.7.3"
[[bench]] [[bench]]
name = "benches" name = "benches"

View file

@ -1,3 +1,3 @@
# Grid # Grid
Data structure grid for rust. Provide a two dimensional data storage that is easy to use and fast. Data structure grid for rust. Provide a two dimensional data storage that is easy to use and fast.

View file

@ -1,85 +1,113 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{criterion_group, criterion_main, Criterion};
use grid::grid;
use grid::Grid; use grid::Grid;
use rand::Rng;
fn init_vec_vec(size: usize) -> Vec<Vec<u32>> { const SIZE: usize = 1000;
vec![vec![0; size]; size]
fn init_vec_vec() -> Vec<Vec<u32>> {
vec![vec![0; SIZE]; SIZE]
} }
fn init_vec_flat(size: usize) -> Vec<u32> { fn init_vec_flat() -> Vec<u32> {
vec![0; size * size] vec![0; SIZE * SIZE]
} }
fn init_grid(size: usize) -> Grid<u32> { fn init_grid() -> Grid<u32> {
Grid::new(size, size, 0) Grid::init(SIZE, SIZE, 0)
}
fn get_vec_vec(x: usize, y: usize) -> u32 {
let mat = init_vec_vec(10);
mat[x][y]
}
fn get_vec_flat(x: usize, y: usize) -> u32 {
let mat = init_vec_flat(10);
mat[x * 10 + y % 10]
}
fn get_grid(x: usize, y: usize) -> u32 {
let mat = init_grid(10);
mat[x][y]
}
fn get_grid_fn(x: usize, y: usize) {
let mat = init_grid(10);
mat.get(x, y);
}
fn set_vec_vec(x: usize, y: usize) {
let mut mat = init_vec_vec(10);
mat[x][y] = 10;
}
fn set_vec_flat(x: usize, y: usize) {
let mut mat = init_vec_flat(10);
mat[x * 10 + y % 10] = 10;
}
fn set_grid(x: usize, y: usize) {
let mut mat = init_grid(10);
mat[x][y] = 10;
} }
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
// Init let mut rng = rand::thread_rng();
c.bench_function("Init vec vec 10x10", |b| { let mut rand = || rng.gen_range(0, SIZE);
b.iter(|| init_vec_vec(black_box(10)))
let mut rng_u32 = rand::thread_rng();
let mut rand_u32 = || rng_u32.gen::<u32>();
// Init macro
c.bench_function("Macro init vec vec", |b| {
b.iter(|| {
vec![
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
]
})
}); });
c.bench_function("Init vec flat 10x10", |b| { c.bench_function("Macro init vec flat", |b| {
b.iter(|| init_vec_flat(black_box(10))) b.iter(|| {
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
]
})
}); });
c.bench_function("Init grid 10x10", |b| b.iter(|| init_grid(black_box(10)))); c.bench_function("Macro init grid from_vec", |b| {
b.iter(|| {
let vec = vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
];
Grid::from_vec(vec, 10)
})
});
c.bench_function("Macro init grid", |b| {
b.iter(|| {
grid![[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]
[0,1,2,3,4,5,6,7,8,9]]
})
});
// New
c.bench_function("Init vec vec", |b| b.iter(|| init_vec_vec()));
c.bench_function("Init vec flat", |b| b.iter(|| init_vec_flat()));
c.bench_function("Init grid", |b| b.iter(|| init_grid()));
// Get // Get
c.bench_function("Get vec vec 10x10", |b| { c.bench_function("Idx vec vec", |b| {
b.iter(|| get_vec_vec(black_box(2), black_box(3))) let vec_vec = init_vec_vec();
b.iter(|| vec_vec[rand()][rand()])
}); });
c.bench_function("Get vec flat 10x10", |b| { c.bench_function("Idx grid", |b| {
b.iter(|| get_vec_flat(black_box(2), black_box(3))) let grid = init_grid();
b.iter(|| grid[rand()][rand()])
}); });
c.bench_function("Get grid 10x10", |b| { c.bench_function("Get_fn vec vec", |b| {
b.iter(|| get_grid(black_box(2), black_box(3))) let vec_vec = init_vec_vec();
b.iter(|| vec_vec.get(rand()).unwrap().get(rand()))
}); });
c.bench_function("Get grid fn 10x10", |b| { c.bench_function("Get_fn grid", |b| {
b.iter(|| get_grid_fn(black_box(2), black_box(3))) let grid = init_grid();
b.iter(|| grid.get(rand(), rand()))
}); });
//Set //Set
c.bench_function("Set vec vec 10x10", |b| { c.bench_function("Set vec vec", |b| {
b.iter(|| set_vec_vec(black_box(2), black_box(3))) let mut vec_vec = init_vec_vec();
b.iter(|| vec_vec[rand()][rand()] = rand_u32())
}); });
c.bench_function("Set vec flat 10x10", |b| { c.bench_function("Set gird", |b| {
b.iter(|| set_vec_flat(black_box(2), black_box(3))) let mut gird = init_grid();
}); b.iter(|| gird[rand()][rand()] = rand_u32())
c.bench_function("Set grid 10x10", |b| {
b.iter(|| set_grid(black_box(2), black_box(3)))
}); });
} }

View file

@ -1,30 +1,164 @@
use std::ops::Index; use std::ops::Index;
use std::ops::IndexMut; use std::ops::IndexMut;
/// Stores elements of a certain type in a 2D grid structure.
pub struct Grid<T> { pub struct Grid<T> {
data: Vec<T>, data: Vec<T>,
columns: usize, cols: usize,
rows: usize, rows: usize,
} }
#[doc(hidden)]
#[macro_export]
macro_rules! count {
() => (0usize);
( $x:tt $($xs:tt)* ) => (1usize + $crate::count!($($xs)*));
}
/// Init a grid with values.
/// ```
/// use grid::grid;
/// let grid = grid![[1, 2, 3]
/// [4, 5, 6]
/// [7, 8, 9]];
/// assert_eq!(grid.size(), (3, 3))
/// ```
#[macro_export]
macro_rules! grid {
() => {
Grid {
rows: 0,
cols: 0,
data: vec![],
}
};
( [$( $x:expr ),* ]) => { {
let vec = vec![$($x),*];
let len = vec.len();
$crate::Grid::from_vec(vec, len)
} };
( [$( $x0:expr ),*] $([$( $x:expr ),*])* ) => {
{
let mut _assert_width0 = [(); $crate::count!($($x0)*)];
let cols = $crate::count!($($x0)*);
let rows = 1usize;
$(
let _assert_width = [(); $crate::count!($($x)*)];
_assert_width0 = _assert_width;
let rows = rows + 1usize;
)*
let mut vec = Vec::with_capacity(rows * cols);
$( vec.push($x0); )*
$( $( vec.push($x); )* )*
$crate::Grid::from_vec(vec, cols)
}
};
}
impl<T: Clone> Grid<T> { impl<T: Clone> Grid<T> {
pub fn new(rows: usize, columns: usize, data: T) -> Grid<T> { /// Init a grid of size rows x columns with default values of the given type.
if rows < 1 || columns < 1 { /// For example this will generate a 2x3 grid of zeros:
/// ```
/// use grid::Grid;
/// let grid : Grid<u8> = Grid::new(2,2);
/// assert_eq!(grid[0][0], 0);
/// ```
pub fn new(rows: usize, cols: usize) -> Grid<T>
where
T: Default,
{
if rows < 1 || cols < 1 {
panic!("Grid size of rows and columns must be greater than zero."); panic!("Grid size of rows and columns must be greater than zero.");
} }
Grid { Grid {
data: vec![data; rows * columns], data: vec![T::default(); rows * cols],
columns: columns, cols: cols,
rows: rows, rows: rows,
} }
} }
pub fn get(&self, row: usize, column: usize) -> Option<&T> { /// Init a grid of size rows x columns with the given data element.
self.data.get(row * self.columns + column % self.columns) pub fn init(rows: usize, cols: usize, data: T) -> Grid<T> {
if rows < 1 || cols < 1 {
panic!("Grid size of rows and columns must be greater than zero.");
}
Grid {
data: vec![data; rows * cols],
cols: cols,
rows: rows,
}
} }
/// Returns a grid from a vector with a given column length.
/// The length of `vec` must be a multiple of `cols`.
///
/// For example:
///
/// ```
/// use grid::Grid;
/// let grid = Grid::from_vec(&vec![1,2,3,4,5,6], 3);
/// assert_eq!(grid.size(), (2, 3));
/// ```
///
/// will create a grid with the following layout:
/// [1,2,3]
/// [4,5,6]
///
/// This example will fail, because `vec.len()` is not a multiple of `cols`:
///
/// ``` should_panic
/// use grid::Grid;
/// Grid::from_vec(&vec![1,2,3,4,5], 3);
/// ```
pub fn from_vec(vec: Vec<T>, cols: usize) -> Grid<T> {
if vec.len() == 0 {
if cols == 0 {
return grid![];
} else {
panic!("Vector length is zero, but cols is {:?}", cols);
}
}
if vec.len() % cols != 0 {
panic!("Vector length must be a multiple of cols.");
}
let rows = vec.len();
Grid {
data: vec,
rows: rows / cols,
cols: cols,
}
}
/// Access a certain element in the grid.
/// Returns None if an element beyond the grid bounds is tried to be accessed.
pub fn get(&self, row: usize, col: usize) -> Option<&T> {
if row < self.rows && col < self.cols() {
unsafe { Some(&self.data.get_unchecked(row * self.cols() + col)) }
//Some(&self.data.get_unchecked(row * self.cols() + col))
} else {
None
}
}
/// Returns the size of the gird as a two element tuple.
/// First element are the number of rows and the second the columns.
pub fn size(&self) -> (usize, usize) { pub fn size(&self) -> (usize, usize) {
(self.rows, self.columns) (self.rows, self.cols)
}
/// Returns the number of rows of the grid.
pub fn rows(&self) -> usize {
self.rows
}
/// Returns the number of columns of the grid.
pub fn cols(&self) -> usize {
self.cols
} }
} }
@ -32,25 +166,20 @@ impl<T: Clone> Index<usize> for Grid<T> {
type Output = [T]; type Output = [T];
fn index(&self, idx: usize) -> &Self::Output { fn index(&self, idx: usize) -> &Self::Output {
if idx >= self.rows { if idx < self.rows {
&self.data[(idx * &self.cols)..]
} else {
panic!( panic!(
"index out of bounds: grid has {:?} but the index is {:?}", "index {:?} out of bounds. Grid has {:?} rows.",
self.rows, idx self.rows, idx
); );
} }
&self.data[(idx * &self.columns)..]
} }
} }
impl<T: Clone> IndexMut<usize> for Grid<T> { impl<T: Clone> IndexMut<usize> for Grid<T> {
fn index_mut(&mut self, idx: usize) -> &mut Self::Output { fn index_mut(&mut self, idx: usize) -> &mut Self::Output {
if idx >= self.rows { &mut self.data[(idx * &self.cols)..]
panic!(
"index out of bounds: grid has {:?} but the index is {:?}",
self.rows, idx
);
}
&mut self.data[(idx * &self.columns)..]
} }
} }
@ -59,64 +188,138 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn ctr() { fn macro_init() {
Grid::new(1, 2, 3); let grid = grid![[1, 2, 3][4, 5, 6]];
Grid::new(1, 2, 1.2); assert_eq!(grid[0][0], 1);
Grid::new(1, 2, 'a'); assert_eq!(grid[0][1], 2);
assert_eq!(grid[0][2], 3);
assert_eq!(grid[1][0], 4);
assert_eq!(grid[1][1], 5);
assert_eq!(grid[1][2], 6);
} }
#[test]
fn macro_init_2() {
let grid = grid![[1, 2, 3]
[4, 5, 6]
[7, 8, 9]];
assert_eq!(grid.size(), (3, 3))
}
#[test]
fn macro_init_char() {
let grid = grid![['a', 'b', 'c']
['a', 'b', 'c']
['a', 'b', 'c']];
assert_eq!(grid.size(), (3, 3));
assert_eq!(grid[1][1], 'b');
}
#[test]
fn macro_one_row() {
let grid: Grid<usize> = grid![[1, 2, 3, 4]];
assert_eq!(grid.size(), (1, 4));
assert_eq!(grid[0][0], 1);
assert_eq!(grid[0][1], 2);
assert_eq!(grid[0][2], 3);
}
#[test]
fn macro_init_empty() {
let grid: Grid<usize> = grid![];
assert_eq!(grid.size(), (0, 0));
}
#[test]
fn from_vec_zero() {
let grid: Grid<u8> = Grid::from_vec(vec![], 0);
assert_eq!(grid.size(), (0, 0));
}
#[test] #[test]
#[should_panic] #[should_panic]
fn ctr_panics() { fn from_vec_panics_1() {
Grid::new(0, 2, 3); let _: Grid<u8> = Grid::from_vec(vec![1, 2, 3], 0);
}
#[test]
#[should_panic]
fn from_vec_panics_2() {
let _: Grid<u8> = Grid::from_vec(vec![1, 2, 3], 2);
}
#[test]
#[should_panic]
fn from_vec_panics_3() {
let _: Grid<u8> = Grid::from_vec(vec![], 1);
}
#[test]
fn init() {
Grid::init(1, 2, 3);
Grid::init(1, 2, 1.2);
Grid::init(1, 2, 'a');
}
#[test]
fn new() {
let grid: Grid<u8> = Grid::new(1, 2);
assert_eq!(grid[0][0], 0);
}
#[test]
#[should_panic]
fn init_panics() {
Grid::init(0, 2, 3);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn ctr_panics_2() { fn ctr_panics_2() {
Grid::new(1, 0, 3); Grid::init(1, 0, 3);
} }
#[test] #[test]
fn get() { fn get() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
assert_eq!(grid.get(0, 0), Some(&3)); assert_eq!(grid.get(0, 0), Some(&3));
} }
#[test] #[test]
fn get_none() { fn get_none() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
assert_eq!(grid.get(1, 0), None); assert_eq!(grid.get(1, 0), None);
} }
#[test] #[test]
fn idx() { fn idx() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
assert_eq!(grid[0][0], 3); assert_eq!(grid[0][0], 3);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn idx_panic_1() { fn idx_panic_1() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
grid[20][0]; grid[20][0];
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn idx_panic_2() { fn idx_panic_2() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
grid[0][20]; grid[0][20];
} }
#[test] #[test]
fn idx_set() { fn idx_set() {
let mut grid = Grid::new(1, 2, 3); let mut grid = Grid::init(1, 2, 3);
grid[0][0] = 4; grid[0][0] = 4;
assert_eq!(grid[0][0], 4); assert_eq!(grid[0][0], 4);
} }
#[test] #[test]
fn size() { fn size() {
let grid = Grid::new(1, 2, 3); let grid = Grid::init(1, 2, 3);
assert_eq!(grid.size(), (1, 2)); assert_eq!(grid.size(), (1, 2));
} }
} }