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}