block_grid/block_grid.rs
1// FIXME: Fix and remove eventally
2#![allow(clippy::result_unit_err)]
3
4use alloc::{vec, vec::Vec};
5use core::marker::PhantomData;
6use core::ops::{Index, IndexMut};
7
8#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10
11use crate::iters::{BlockIter, BlockIterMut, EachIter, EachIterMut, RowMajorIter, RowMajorIterMut};
12use crate::{BlockDim, Coords};
13
14/// A fixed-size 2D array with a blocked memory representation.
15///
16/// See [crate-level documentation][crate] for general usage info.
17///
18/// If your dimensions are not a multiple of the block size, you can use the helper function
19/// [`BlockDim::round_up_to_valid`] to generate larger, valid dimensions.
20#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
21#[cfg_attr(feature = "serde", serde(bound(serialize = "T: Clone + Serialize")))]
22#[cfg_attr(feature = "serde", serde(try_from = "serde_hack::ShadowBlockGrid<T>"))]
23#[cfg_attr(feature = "serde", serde(into = "serde_hack::ShadowBlockGrid<T>"))]
24#[derive(Clone, Debug, Eq, Hash, PartialEq)]
25pub struct BlockGrid<T, B: BlockDim> {
26 rows: usize,
27 cols: usize,
28 col_blocks: usize,
29 buf: Vec<T>,
30 _phantom: PhantomData<B>,
31}
32
33/// A view of a 2D block contiguous in memory.
34///
35/// Can be obtained via [`BlockIter`], which is created by calling [`BlockGrid::block_iter`].
36#[derive(Clone, Copy, Debug)]
37pub struct Block<'a, T, B: BlockDim> {
38 block_coords: Coords,
39 arr: &'a [T],
40 _phantom: PhantomData<B>,
41}
42
43/// A mutable view of a 2D block contiguous in memory.
44///
45/// Can be obtained via [`BlockIterMut`], which is created by calling [`BlockGrid::block_iter_mut`].
46#[derive(Debug)]
47pub struct BlockMut<'a, T, B: BlockDim> {
48 block_coords: Coords,
49 arr: &'a mut [T],
50 _phantom: PhantomData<B>,
51}
52
53impl<T, B: BlockDim> BlockGrid<T, B> {
54 /// Constructs a `BlockGrid<T, B>` by consuming a [`Vec<T>`].
55 ///
56 /// The ordering of the memory is taken as is in the vector.
57 ///
58 /// # Errors
59 ///
60 /// If invalid dimensions, either because `rows` and `cols` do not divide evenly into the block
61 /// size `B` or the length of `elems` does not match `rows * cols`.
62 pub fn from_raw_vec(rows: usize, cols: usize, elems: Vec<T>) -> Result<Self, ()> {
63 if !Self::valid_size(rows, cols) || rows * cols != elems.len() {
64 return Err(());
65 }
66 Ok(Self {
67 rows,
68 cols,
69 col_blocks: cols / B::WIDTH,
70 buf: elems,
71 _phantom: PhantomData,
72 })
73 }
74
75 /// Converts a `BlockGrid<T, B>` to a [`Vec<T>`] in memory order.
76 #[inline]
77 pub fn take_raw_vec(self) -> Vec<T> {
78 self.buf
79 }
80
81 /// Returns the nuumber of rows.
82 #[inline]
83 pub fn rows(&self) -> usize {
84 self.rows
85 }
86
87 /// Returns the number of columns.
88 #[inline]
89 pub fn cols(&self) -> usize {
90 self.cols
91 }
92
93 /// Returns the number of elements.
94 #[inline]
95 pub fn size(&self) -> usize {
96 self.rows() * self.cols()
97 }
98
99 /// Returns the number of blocks in the vertical direction.
100 #[inline]
101 pub fn row_blocks(&self) -> usize {
102 self.rows / B::WIDTH
103 }
104
105 /// Returns the number of blocks in the horizontal direction.
106 #[inline]
107 pub fn col_blocks(&self) -> usize {
108 self.col_blocks
109 }
110
111 /// Returns the total number of blocks.
112 #[inline]
113 pub fn blocks(&self) -> usize {
114 self.row_blocks() * self.col_blocks()
115 }
116
117 /// Returns `true` if the given coordinates are valid.
118 #[inline]
119 pub fn contains(&self, (row, col): Coords) -> bool {
120 row < self.rows && col < self.cols
121 }
122
123 /// Returns a reference to the element at the given coordinates, or [`None`] if they are
124 /// out-of-bounds.
125 #[inline]
126 pub fn get(&self, coords: Coords) -> Option<&T> {
127 if !self.contains(coords) {
128 return None;
129 }
130 // SAFETY: `coords` is a valid index
131 Some(unsafe { self.get_unchecked(coords) })
132 }
133
134 /// Returns a mutable reference to the element at the given coordinates, or [`None`] if they
135 /// are out-of-bounds.
136 #[inline]
137 pub fn get_mut(&mut self, coords: Coords) -> Option<&mut T> {
138 if !self.contains(coords) {
139 return None;
140 }
141 // SAFETY: `coords` is a valid index
142 Some(unsafe { self.get_unchecked_mut(coords) })
143 }
144
145 /// Returns a reference to the element at the given coordinates, without bounds checking.
146 ///
147 /// # Safety
148 ///
149 /// Calling this method with out-of-bounds coordinates is *undefined-behaviour*.
150 #[inline]
151 pub unsafe fn get_unchecked(&self, coords: Coords) -> &T {
152 debug_assert!(self.contains(coords));
153 let ind = self.calc_index(coords);
154 self.buf.get_unchecked(ind)
155 }
156
157 /// Returns a mutable reference to the element at the given coordinates, without bounds
158 /// checking.
159 ///
160 /// # Safety
161 ///
162 /// Calling this method with out-of-bounds coordinates is *undefined-behaviour*.
163 #[inline]
164 pub unsafe fn get_unchecked_mut(&mut self, coords: Coords) -> &mut T {
165 debug_assert!(self.contains(coords));
166 let ind = self.calc_index(coords);
167 self.buf.get_unchecked_mut(ind)
168 }
169
170 /// Returns all elements as a slice in memory order.
171 #[inline]
172 pub fn raw(&self) -> &[T] {
173 &self.buf
174 }
175
176 /// Returns all elements as a mutable slice in memory order.
177 #[inline]
178 pub fn raw_mut(&mut self) -> &mut [T] {
179 &mut self.buf
180 }
181
182 /// Returns an iterator over all the elements in memory order.
183 ///
184 /// If you wanna visit each element arbitrarily, this would be the best way. If you also need
185 /// coordinates while iterating, follow up with a chained [`.coords()`][coords] call.
186 ///
187 /// [coords]: crate::CoordsIterator::coords()
188 #[inline]
189 pub fn each_iter(&self) -> EachIter<'_, T, B> {
190 EachIter::new(self)
191 }
192
193 /// Returns a mutable iterator over all the elements in memory order.
194 ///
195 /// If you wanna mutably visit each element arbitrarily, this would be the best way. If you
196 /// also need coordinates while iterating, follow up with a chained [`.coords()`][coords] call.
197 ///
198 /// [coords]: crate::CoordsIterator::coords()
199 #[inline]
200 pub fn each_iter_mut(&mut self) -> EachIterMut<'_, T, B> {
201 EachIterMut::new(self)
202 }
203
204 /// Returns an iterator over all blocks in memory order, yielding [`Block`]s.
205 ///
206 /// If you need the block coordinates while iterating, follow up with a chained
207 /// [`.coords()`][coords] call. In this case, note that the 2D coordinates yielded are of the
208 /// actual entire block. If you instead need the coordinates of the first (top-left) element
209 /// in the block, see [`Block::starts_at`].
210 ///
211 /// [coords]: crate::CoordsIterator::coords()
212 #[inline]
213 pub fn block_iter(&self) -> BlockIter<'_, T, B> {
214 BlockIter::new(self)
215 }
216
217 /// Returns a mutable iterator over all blocks in memory order, yielding [`BlockMut`]s.
218 ///
219 /// If you need the block coordinates while iterating, follow up with a chained
220 /// [`.coords()`][coords] call. In this case, note that the 2D coordinates yielded are of the
221 /// actual entire block. If you instead need the coordinates of the first (top-left) element
222 /// in the block, see [`BlockMut::starts_at`].
223 ///
224 /// [coords]: crate::CoordsIterator::coords()
225 #[inline]
226 pub fn block_iter_mut(&mut self) -> BlockIterMut<'_, T, B> {
227 BlockIterMut::new(self)
228 }
229
230 /// Returns an iterator over all the elements in [row-major order][row_major].
231 ///
232 /// This ordering is what you're probably used to with usual 2D arrays. This method may be
233 /// useful for converting between array types or general IO. If you also need the coordinates
234 /// while iterating, follow up with a chained [`.coords()`][coords] call.
235 ///
236 /// [row_major]: https://en.wikipedia.org/wiki/Row-_and_column-major_order
237 /// [coords]: crate::CoordsIterator::coords()
238 #[inline]
239 pub fn row_major_iter(&self) -> RowMajorIter<'_, T, B> {
240 RowMajorIter::new(self)
241 }
242
243 /// Returns an mutable iterator over all the elements in [row-major order][row_major].
244 ///
245 /// If you also need the coordinates while iterating, follow up with a chained
246 /// [`.coords()`][coords] call.
247 ///
248 /// [row_major]: https://en.wikipedia.org/wiki/Row-_and_column-major_order
249 /// [coords]: crate::CoordsIterator::coords()
250 #[inline]
251 pub fn row_major_iter_mut(&mut self) -> RowMajorIterMut<'_, T, B> {
252 RowMajorIterMut::new(self)
253 }
254
255 /// Returns `true` if `rows` and `cols` form a valid sized `BlockGrid<T, B>`.
256 fn valid_size(rows: usize, cols: usize) -> bool {
257 rows > 0 && cols > 0 && rows % B::WIDTH == 0 && cols % B::WIDTH == 0
258 }
259
260 /// Returns the 1D memory index calculated from 2D coordinates.
261 fn calc_index(&self, (row, col): Coords) -> usize {
262 // Get block
263 let (b_row, b_col) = (row / B::WIDTH, col / B::WIDTH);
264 let block_ind = B::AREA * (self.col_blocks() * b_row + b_col);
265 // Offset within block
266 let (s_row, s_col) = (row % B::WIDTH, col % B::WIDTH);
267 let sub_ind = B::WIDTH * s_row + s_col;
268 block_ind + sub_ind
269 }
270}
271
272impl<T: Clone, B: BlockDim> BlockGrid<T, B> {
273 /// Constructs a `BlockGrid<T, B>` by filling with a single element.
274 ///
275 /// # Errors
276 ///
277 /// If `rows` and `cols` do not divide evenly into the block size `B`.
278 pub fn filled(rows: usize, cols: usize, elem: T) -> Result<Self, ()> {
279 if !Self::valid_size(rows, cols) {
280 return Err(());
281 }
282 Ok(Self {
283 rows,
284 cols,
285 col_blocks: cols / B::WIDTH,
286 buf: vec![elem; rows * cols],
287 _phantom: PhantomData,
288 })
289 }
290
291 /// Constructs a `BlockGrid<T, B>` from a slice in [row-major order][row_major].
292 ///
293 /// This method may be useful for converting from a typical 2D array.
294 ///
295 /// # Errors
296 ///
297 /// If invalid dimensions, either because `rows` and `cols` do not divide evenly into the block
298 /// size `B` or the length of `elems` does not match `rows * cols`.
299 ///
300 /// [row_major]: https://en.wikipedia.org/wiki/Row-_and_column-major_order
301 pub fn from_row_major(rows: usize, cols: usize, elems: &[T]) -> Result<Self, ()> {
302 Self::from_array_index_helper(rows, cols, elems, |row, col| cols * row + col)
303 }
304
305 /// Constructs a `BlockGrid<T, B>` from a slice in [column-major order][col_major].
306 ///
307 /// 2D arrays are not usually stored like this, but occasionally they are.
308 ///
309 /// # Errors
310 ///
311 /// If invalid dimensions, either because `rows` and `cols` do not divide evenly into the block
312 /// size `B` or the length of `elems` does not match `rows * cols`.
313 ///
314 /// [col_major]: https://en.wikipedia.org/wiki/Row-_and_column-major_order
315 pub fn from_col_major(rows: usize, cols: usize, elems: &[T]) -> Result<Self, ()> {
316 Self::from_array_index_helper(rows, cols, elems, |row, col| rows * col + row)
317 }
318
319 /// Helper method to convert from a differently ordered array to a `BlockGrid<T, B>`.
320 fn from_array_index_helper(
321 rows: usize,
322 cols: usize,
323 elems: &[T],
324 calc_index: impl Fn(usize, usize) -> usize,
325 ) -> Result<Self, ()> {
326 if !Self::valid_size(rows, cols) || rows * cols != elems.len() {
327 return Err(());
328 }
329 let mut grid = Self {
330 rows,
331 cols,
332 col_blocks: cols / B::WIDTH,
333 buf: Vec::with_capacity(rows * cols),
334 _phantom: PhantomData,
335 };
336 // Iterate in memory order by index and pull values from `elems`
337 for bi in (0..grid.rows()).step_by(B::WIDTH) {
338 for bj in (0..grid.cols()).step_by(B::WIDTH) {
339 for si in 0..B::WIDTH {
340 for sj in 0..B::WIDTH {
341 let (row, col) = (bi + si, bj + sj);
342 let ind = calc_index(row, col);
343 // There's no 'simple' way to do this without `Clone`,
344 // because `elems` can't be easily drained out of order.
345 grid.buf.push(elems[ind].clone());
346 }
347 }
348 }
349 }
350 debug_assert_eq!(grid.buf.len(), grid.size());
351 Ok(grid)
352 }
353}
354
355impl<T: Clone + Default, B: BlockDim> BlockGrid<T, B> {
356 /// Constructs a `BlockGrid<T, B>` by filling with the default value of `T`.
357 ///
358 /// # Errors
359 ///
360 /// If `rows` and `cols` do not divide evenly into the block size `B`.
361 pub fn new(rows: usize, cols: usize) -> Result<Self, ()> {
362 Self::filled(rows, cols, T::default())
363 }
364}
365
366impl<T, B: BlockDim> Index<Coords> for BlockGrid<T, B> {
367 type Output = T;
368
369 #[inline]
370 fn index(&self, coords: Coords) -> &Self::Output {
371 self.get(coords).expect("Index out of bounds")
372 }
373}
374
375impl<T, B: BlockDim> IndexMut<Coords> for BlockGrid<T, B> {
376 #[inline]
377 fn index_mut(&mut self, coords: Coords) -> &mut Self::Output {
378 self.get_mut(coords).expect("Index out of bounds")
379 }
380}
381
382impl<'a, T, B: BlockDim> Block<'a, T, B> {
383 /// Constructs a `Block<'a, T, B>` from an array slice.
384 ///
385 /// # Safety
386 ///
387 /// `block_coords` *must* be valid and `arr` *must* be of length `B::AREA`.
388 pub(crate) unsafe fn new(block_coords: Coords, arr: &'a [T]) -> Self {
389 debug_assert_eq!(arr.len(), B::AREA);
390 Self {
391 block_coords,
392 arr,
393 _phantom: PhantomData,
394 }
395 }
396
397 /// Returns the coordinates of the entire block.
398 ///
399 /// Block coordinates mean that the `(i, j)` refers to the `i`-th *row of blocks* and the
400 /// `j`-th block in that row. If you need the coordinates of the first (top-left) element,
401 /// use [`starts_at`] instead.
402 ///
403 /// [`starts_at`]: Self::starts_at
404 #[inline]
405 pub fn coords(&self) -> Coords {
406 self.block_coords
407 }
408
409 /// Returns the coordinates of the first (top-left) element in the block.
410 #[inline]
411 pub fn starts_at(&self) -> Coords {
412 let (b_row, b_col) = self.block_coords;
413 (B::WIDTH * b_row, B::WIDTH * b_col)
414 }
415
416 /// Returns `true` if the given coordinates are valid.
417 #[inline]
418 pub fn contains(&self, (row, col): Coords) -> bool {
419 row < B::WIDTH && col < B::WIDTH
420 }
421
422 /// Returns a reference to the element at the given coordinates, or [`None`] if they are
423 /// out-of-bounds.
424 #[inline]
425 pub fn get(&self, coords: Coords) -> Option<&T> {
426 if !self.contains(coords) {
427 return None;
428 }
429 // SAFETY: `coords` is a valid index
430 Some(unsafe { self.get_unchecked(coords) })
431 }
432
433 /// Returns a reference to the element at the given coordinates, without bounds checking.
434 ///
435 /// # Safety
436 ///
437 /// Calling this method with out-of-bounds coordinates is *undefined-behaviour*.
438 #[inline]
439 pub unsafe fn get_unchecked(&self, coords: Coords) -> &T {
440 debug_assert!(self.contains(coords));
441 self.arr.get_unchecked(self.calc_index(coords))
442 }
443
444 /// Returns all elements in block as a slice in memory order.
445 #[inline]
446 pub fn raw(&self) -> &[T] {
447 self.arr
448 }
449
450 /// Returns the 1D memory index calculated from 2D coordinates.
451 fn calc_index(&self, (row, col): Coords) -> usize {
452 B::WIDTH * row + col
453 }
454}
455
456impl<'a, T, B: BlockDim> Index<Coords> for Block<'a, T, B> {
457 type Output = T;
458
459 #[inline]
460 fn index(&self, coords: Coords) -> &Self::Output {
461 self.get(coords).expect("Index out of bounds")
462 }
463}
464
465impl<'a, T, B: BlockDim> BlockMut<'a, T, B> {
466 /// Constructs a `BlockMut<'a, T, B>` from an array slice.
467 ///
468 /// # Safety
469 ///
470 /// `block_coords` *must* be valid and `arr` *must* be of length `B::AREA`.
471 pub(crate) unsafe fn new(block_coords: Coords, arr: &'a mut [T]) -> Self {
472 debug_assert_eq!(arr.len(), B::AREA);
473 Self {
474 block_coords,
475 arr,
476 _phantom: PhantomData,
477 }
478 }
479
480 /// Returns the coordinates of the entire block.
481 ///
482 /// Block coordinates mean that the `(i, j)` refers to the `i`-th *row of blocks* and the
483 /// `j`-th block in that row. If you need the coordinates of the first (top-left) element,
484 /// use [`starts_at`] instead.
485 ///
486 /// [`starts_at`]: Self::starts_at
487 #[inline]
488 pub fn coords(&self) -> Coords {
489 self.block_coords
490 }
491
492 /// Returns of the coordinates of the first (top-left) element in the block.
493 #[inline]
494 pub fn starts_at(&self) -> Coords {
495 let (b_row, b_col) = self.block_coords;
496 (B::WIDTH * b_row, B::WIDTH * b_col)
497 }
498
499 /// Returns `true` if the given coordinates are valid.
500 #[inline]
501 pub fn contains(&self, (row, col): Coords) -> bool {
502 row < B::WIDTH && col < B::WIDTH
503 }
504
505 /// Returns a reference to the element at the given coordinates, or [`None`] if they are
506 /// out-of-bounds.
507 #[inline]
508 pub fn get(&self, coords: Coords) -> Option<&T> {
509 if !self.contains(coords) {
510 return None;
511 }
512 // SAFETY: `coords` is a valid index
513 Some(unsafe { self.get_unchecked(coords) })
514 }
515
516 /// Returns a mutable reference to the element at the given coordinates, or [`None`] if they
517 /// are out-of-bounds.
518 #[inline]
519 pub fn get_mut(&mut self, coords: Coords) -> Option<&mut T> {
520 if !self.contains(coords) {
521 return None;
522 }
523 // SAFETY: `coords` is a valid index
524 Some(unsafe { self.get_unchecked_mut(coords) })
525 }
526
527 /// Returns a reference to the element at the given coordinates, without bounds checking.
528 ///
529 /// # Safety
530 ///
531 /// Calling this method with out-of-bounds coordinates is *undefined-behaviour*.
532 #[inline]
533 pub unsafe fn get_unchecked(&self, coords: Coords) -> &T {
534 debug_assert!(self.contains(coords));
535 self.arr.get_unchecked(self.calc_index(coords))
536 }
537
538 /// Returns a mutable reference to the element at the given coordinates, without bounds
539 /// checking.
540 ///
541 /// # Safety
542 ///
543 /// Calling this method with out-of-bounds coordinates is *undefined-behaviour*.
544 #[inline]
545 pub unsafe fn get_unchecked_mut(&mut self, coords: Coords) -> &mut T {
546 debug_assert!(self.contains(coords));
547 self.arr.get_unchecked_mut(self.calc_index(coords))
548 }
549
550 /// Returns all elements in block as a slice in memory order.
551 #[inline]
552 pub fn raw(&self) -> &[T] {
553 self.arr
554 }
555
556 /// Returns all elements in block as a mutable slice in memory order.
557 #[inline]
558 pub fn raw_mut(&mut self) -> &mut [T] {
559 self.arr
560 }
561
562 /// Returns the 1D memory index calculated from 2D coordinates.
563 fn calc_index(&self, (row, col): Coords) -> usize {
564 B::WIDTH * row + col
565 }
566}
567
568impl<'a, T, B: BlockDim> Index<Coords> for BlockMut<'a, T, B> {
569 type Output = T;
570
571 #[inline]
572 fn index(&self, coords: Coords) -> &Self::Output {
573 self.get(coords).expect("Coordinates out of bounds")
574 }
575}
576
577impl<'a, T, B: BlockDim> IndexMut<Coords> for BlockMut<'a, T, B> {
578 #[inline]
579 fn index_mut(&mut self, coords: Coords) -> &mut Self::Output {
580 self.get_mut(coords).expect("Coordinates out of bounds")
581 }
582}
583
584#[cfg(feature = "serde")]
585mod serde_hack {
586 use super::*;
587 use core::convert::{From, TryFrom};
588 use core::fmt;
589
590 /// Error if invalid dimensions are passed in or deserialized.
591 ///
592 /// Currently, only used for `serde` deserialization, but in the future, this should be used
593 /// for the [`BlockGrid<T, B>`] constructors as well.
594 #[derive(Debug)]
595 pub(super) struct InvalidSizeError;
596
597 impl fmt::Display for InvalidSizeError {
598 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599 write!(f, "Dimensions are invalid")
600 }
601 }
602
603 /// A "trick" to avoid writing (de)serialization code with validation.
604 ///
605 /// See PR for details.
606 #[derive(Deserialize, Serialize)]
607 pub(super) struct ShadowBlockGrid<T> {
608 rows: usize,
609 cols: usize,
610 #[serde(rename = "b")]
611 bwidth: usize,
612 buf: Vec<T>,
613 }
614
615 // Serialization
616 impl<T, B: BlockDim> From<BlockGrid<T, B>> for ShadowBlockGrid<T> {
617 fn from(bgrid: BlockGrid<T, B>) -> Self {
618 // Assumes `bgrid` is in valid state
619 Self {
620 rows: bgrid.rows(),
621 cols: bgrid.cols(),
622 bwidth: B::WIDTH,
623 buf: bgrid.take_raw_vec(),
624 }
625 }
626 }
627
628 // Deserialization
629 impl<T, B: BlockDim> TryFrom<ShadowBlockGrid<T>> for BlockGrid<T, B> {
630 type Error = InvalidSizeError;
631
632 fn try_from(shadow: ShadowBlockGrid<T>) -> Result<Self, Self::Error> {
633 let ShadowBlockGrid {
634 rows,
635 cols,
636 bwidth,
637 buf,
638 } = shadow;
639 // Check that deserialized data is a valid state
640 if bwidth != B::WIDTH {
641 return Err(InvalidSizeError);
642 }
643 Self::from_raw_vec(rows, cols, buf).map_err(|_| InvalidSizeError)
644 }
645 }
646}