1use crate::{CanonicalWrite, StateHasher};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum GridError {
8 IncorrectCellCount,
10 TooLarge,
13}
14
15#[derive(Clone, Debug, PartialEq, Eq)]
17pub struct Grid2<T> {
18 width: u16,
19 height: u16,
20 cells: Vec<T>,
21}
22
23impl<T> Grid2<T> {
24 pub fn from_cells(width: u16, height: u16, cells: Vec<T>) -> Result<Self, GridError> {
26 let expected = Self::cell_count(width, height);
27 if cells.len() != expected {
28 return Err(GridError::IncorrectCellCount);
29 }
30 Ok(Self {
31 width,
32 height,
33 cells,
34 })
35 }
36
37 #[must_use]
39 pub const fn width(&self) -> u16 {
40 self.width
41 }
42
43 #[must_use]
45 pub const fn height(&self) -> u16 {
46 self.height
47 }
48
49 #[must_use]
51 pub fn get(&self, x: u16, y: u16) -> Option<&T> {
52 self.cells.get(self.index(x, y)?)
53 }
54
55 #[must_use]
57 pub fn get_mut(&mut self, x: u16, y: u16) -> Option<&mut T> {
58 let index = self.index(x, y)?;
59 self.cells.get_mut(index)
60 }
61
62 #[must_use]
64 pub fn row(&self, y: u16) -> Option<&[T]> {
65 let start = self.index(0, y)?;
66 self.cells
67 .get(start..start.checked_add(usize::from(self.width))?)
68 }
69
70 #[must_use]
72 pub fn cells(&self) -> &[T] {
73 &self.cells
74 }
75
76 fn cell_count(width: u16, height: u16) -> usize {
77 let count = u32::from(width)
78 .checked_mul(u32::from(height))
79 .expect("u16 grid dimensions have a representable product");
80 usize::try_from(count).expect("u16 grid dimensions fit usize")
81 }
82
83 fn index(&self, x: u16, y: u16) -> Option<usize> {
85 if y >= self.height || (x >= self.width && !(self.width == 0 && x == 0)) {
86 return None;
87 }
88 usize::try_from(u32::from(y) * u32::from(self.width) + u32::from(x)).ok()
89 }
90}
91
92impl<T: Clone> Grid2<T> {
93 pub fn filled(width: u16, height: u16, value: T) -> Result<Self, GridError> {
96 let count = Self::cell_count(width, height);
97 let mut cells = Vec::new();
98 cells
99 .try_reserve_exact(count)
100 .map_err(|_| GridError::TooLarge)?;
101 cells.resize(count, value);
102 Ok(Self {
103 width,
104 height,
105 cells,
106 })
107 }
108}
109
110impl<T: CanonicalWrite> CanonicalWrite for Grid2<T> {
111 fn canonical_write(&self, out: &mut StateHasher) {
112 out.u16(self.width);
113 out.u16(self.height);
114 out.sequence(&self.cells);
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use wasm_bindgen_test::wasm_bindgen_test;
122
123 #[wasm_bindgen_test(unsupported = test)]
125 fn dimensions_are_reported() {
126 let grid = Grid2::filled(3, 5, 0u8).expect("small grid");
127 assert_eq!((grid.width(), grid.height()), (3, 5));
128 assert_eq!(grid.cells().len(), 15);
129 }
130
131 #[cfg(target_pointer_width = "32")]
134 #[wasm_bindgen_test(unsupported = test)]
135 fn oversized_fill_returns_an_error() {
136 assert_eq!(
137 Grid2::filled(u16::MAX, u16::MAX, 0u8),
138 Err(GridError::TooLarge)
139 );
140 }
141
142 #[wasm_bindgen_test(unsupported = test)]
143 fn construction_and_access_preserve_row_major_invariants() {
144 let mut grid = Grid2::from_cells(3, 2, vec![0, 1, 2, 3, 4, 5]).expect("matching shape");
145 assert_eq!(grid.get(2, 1), Some(&5));
146 assert_eq!(grid.row(1), Some(&[3, 4, 5][..]));
147 *grid.get_mut(1, 0).expect("in bounds") = 9;
148 assert_eq!(grid.cells(), &[0, 9, 2, 3, 4, 5]);
149 assert_eq!(grid.get(3, 0), None);
150 assert_eq!(grid.get(0, 2), None);
151 assert_eq!(
152 Grid2::from_cells(2, 2, vec![1, 2, 3]),
153 Err(GridError::IncorrectCellCount)
154 );
155 }
156
157 #[wasm_bindgen_test(unsupported = test)]
158 fn dimensions_have_checked_widened_size_arithmetic() {
159 assert_eq!(Grid2::<()>::cell_count(u16::MAX, u16::MAX), 4_294_836_225);
160 }
161
162 #[wasm_bindgen_test(unsupported = test)]
163 fn zero_width_rows_are_valid_and_empty() {
164 let grid = Grid2::from_cells(0, 2, Vec::<u8>::new()).expect("zero-width shape");
165 assert_eq!(grid.row(0), Some(&[][..]));
166 assert_eq!(grid.row(1), Some(&[][..]));
167 assert_eq!(grid.row(2), None);
168 assert_eq!(grid.get(0, 0), None);
169 }
170}