block_grid/lib.rs
1//! A quick, cache-conscious, blocked 2D array.
2//!
3//! `block-grid` gives you a fixed-size 2D array with a blocked / tiled memory representation.
4//! This has the sweet benefit of being much more cache-friendly if you're often accessing nearby
5//! coordinates. It also offers a bunch of utility methods and block access. If you don't care
6//! about tiled memory and just want any 2D-grid, see the
7//! ["Using without Blocks"](#using-without-blocks-good-ol-row-major) section below.
8//!
9//! # Example
10//!
11//! The following example offers a tour of basic usage and some features.
12//!
13//! ```
14//! use block_grid::{BlockGrid, CoordsIterator, U2};
15//!
16//! let data: Vec<_> = (0..(4 * 6)).collect();
17//!
18//! // Construct from row-major ordered data
19//! let grid = BlockGrid::<usize, U2>::from_row_major(4, 6, &data)?;
20//!
21//! // The 2D grid looks like:
22//! // +-----------------------+
23//! // | 0 1 | 2 3 | 4 5 |
24//! // | 6 7 | 8 9 | 10 11 |
25//! // |-------+-------+-------|
26//! // | 12 13 | 14 15 | 16 17 |
27//! // | 18 19 | 20 21 | 22 23 |
28//! // +-----------------------+
29//!
30//! // Indexing
31//! assert_eq!(grid[(1, 3)], 9);
32//!
33//! // Access raw array
34//! let first_five = &grid.raw()[..5];
35//! assert_eq!(first_five, &[0, 1, 6, 7, 2]);
36//!
37//! // Iterate over blocks, and access the last
38//! let block = grid.block_iter().last().unwrap();
39//! assert_eq!(block[(0, 1)], 17);
40//!
41//! // Iterate in row-major order
42//! for (i, &x) in grid.row_major_iter().enumerate() {
43//! assert_eq!(x, i);
44//! }
45//!
46//! // Iterate in memory order, with coordinates
47//! for ((row, col), &x) in grid.each_iter().coords() {
48//! assert_eq!(row * 6 + col, x);
49//! }
50//!
51//! # Ok::<(), ()>(())
52//! ```
53//!
54//! # Usage
55//!
56//! ## Types
57//!
58//! The primary type is [`BlockGrid<T, B>`], where `T` is the stored type and `B` is a generic
59//! parameter that controls the block size (all the `U*` types below). A view of a 2D block,
60//! which is stored as a contiguous piece of memory, is a [`Block`] or [`BlockMut`].
61//!
62//! ## Indexing
63//!
64//! Indexing is by a pair of 2D coordinates, [`Coords`], which is simply a tuple `(row, column`).
65//! You can use `[(i, j)]` or one of the many functions. When indexing elements in a specific
66//! [`Block`] or [`BlockMut`], the coordinates are relative, meaning it's the row and column
67//! *within* that block.
68//!
69//! ### Element Coordinates vs. Block Coordinates
70//!
71//! Coordinates typically refer to the locations of specific elements, but they can *also* be used
72//! to index entire blocks. When using [`BlockGrid::block_iter`] chained by a
73//! [`.coords()`][coords] call, or the [`Block::coords`] method, the returned *block coordinates*
74//! instead refer to the entire block. This means that `(i, j`) would refer to the `i`-th *row of
75//! blocks* and then the `j`-th block in that row. If you want the coordinates of the first
76//! (top-left) element in a block, use the [`Block::starts_at`] method instead.
77//!
78//! [coords]: CoordsIterator::coords
79//!
80//! ## Iterating
81//!
82//! There are multiple ways of iterating over a 2D array. If you simply want to visit each
83//! element, use [`BlockGrid::each_iter`]. You can alternatively iterate in row-major order using
84//! [`BlockGrid::row_major_iter`]. Instead of iterating over elements, you can also iterate over
85//! entire blocks with [`BlockGrid::block_iter`]. For any of these, if you also need coordinates
86//! while iterating, you can chain a [`.coords()`][coords] call. If you only need a 1D iteration
87//! count, then there's always [`Iterator::enumerate`].
88//!
89//! [coords]: CoordsIterator::coords
90//!
91//! ## Using without Blocks (Good Ol' Row-Major)
92//!
93//! If you wanna test performance against an non-blocked memory representation, you need both, or
94//! you just like the rest of the interface but don't care about the blocks, we got you. Just use
95//! the [`U1`] block size or the handy [`Grid<T>`] alias. All the block related methods still exist
96//! and are correct, but are just functionally useless.
97//!
98//! # Optional Features
99//!
100//! ## Serde
101//!
102//! To use the [`serde`][serde] framework, enable the optional `serde` [feature] in your
103//! `Cargo.toml`. There is an important subtlety to its usage. Because the block size is generic
104//! and compile-time, you have to know `B` when deserializing. You *could* write it decide based
105//! on the input data, but I think it would lead to a bunch of extra code-gen, so I've left it
106//! out. It does, however, verify that `B` is the same value as the one originally used to
107//! serialize. If you always know the `B` value, then this shouldn't matter at all.
108//!
109//! [serde]: https://crates.io/crates/serde
110//! [feature]: https://doc.rust-lang.org/cargo/reference/features.html
111
112#![warn(missing_docs)]
113#![warn(missing_debug_implementations)]
114#![warn(rust_2018_idioms)]
115#![no_std]
116
117extern crate alloc;
118
119#[cfg(test)]
120#[macro_use]
121extern crate std;
122
123mod block_grid;
124mod block_width;
125pub mod iters;
126
127#[cfg(test)]
128mod tests;
129
130pub use crate::block_grid::*;
131pub use crate::block_width::*;
132pub use crate::iters::CoordsIterator;
133
134/// Type alias for a 2-tuple of indices, representing 2D coordinates.
135pub type Coords = (usize, usize);
136
137/// Type alias for a typical 2D grid with standard row-major memory.
138pub type Grid<T> = BlockGrid<T, U1>;