toodee/
view.rs

1use core::fmt;
2use core::fmt::{Formatter, Debug};
3use core::ops::{Index, IndexMut, Range};
4use core::ptr;
5use core::mem;
6
7use crate::toodee::*;
8use crate::ops::*;
9use crate::iter::*;
10
11/// Checks the proposed view dimensions, and returns the correct cols, rows and slice data range
12/// for view construction.
13fn calculate_view_dimensions<T>(start: Coordinate, end: Coordinate, toodee: &impl TooDeeOps<T>, stride: usize) -> (usize, usize, Range<usize>) {
14    assert!(end.0 >= start.0);
15    assert!(end.1 >= start.1);
16    assert!(end.0 <= toodee.num_cols());
17    assert!(end.1 <= toodee.num_rows());
18    assert!(stride >= toodee.num_cols());
19    let mut num_cols = end.0 - start.0;
20    let mut num_rows = end.1 - start.1;
21    // zero out dimensions for empty arrays
22    if num_cols == 0 || num_rows == 0 {
23        num_cols = 0;
24        num_rows = 0;
25    }
26    let data_start = start.1 * stride + start.0;
27    let data_len = {
28        if num_rows == 0 {
29            0
30        } else {
31            (num_rows - 1) * stride + num_cols
32        }
33    };
34    (num_cols, num_rows, data_start..data_start + data_len)
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use alloc::vec;
41
42    #[test]
43    fn calc_dims_view() {
44        let v = vec![1u32; 32];
45        let view = TooDeeView::new(4, 4, &v);
46        let (num_cols, num_rows, range) = calculate_view_dimensions((0, 1), (2,3), &view, 4);
47        assert_eq!(num_cols, 2);
48        assert_eq!(num_rows, 2);
49        assert_eq!(range, 4..10);
50    }
51}
52
53/// *Internal only* functions for calculating vector ranges.
54trait TooDeeViewCommon<T>: TooDeeOps<T> {
55
56    fn data(&self) -> &[T];
57
58    fn stride(&self) -> usize;
59
60    fn get_col_params(&self, col: usize) -> (Range<usize>, usize){
61        assert!(col < self.num_cols());
62        let stride = self.stride();
63        let start = col;
64        let end = {
65            let num_rows = self.num_rows();
66            if num_rows == 0 {
67                start
68            } else {
69                start + (num_rows - 1) * stride + 1
70            }
71        };
72        (start..end, stride - 1)
73    }
74
75
76}
77
78impl<T> TooDeeViewCommon<T> for TooDeeView<'_, T> {
79    #[inline]
80    fn data(&self) -> &[T] { &self.data }
81    #[inline]
82    fn stride(&self) -> usize {
83        self.stride
84    }
85}
86
87impl<T> TooDeeViewCommon<T> for TooDeeViewMut<'_, T> {
88    #[inline]
89    fn data(&self) -> &[T] { &self.data }
90    #[inline]
91    fn stride(&self) -> usize {
92        self.stride
93    }
94}
95
96
97/// Provides a read-only view (or subset) of a `TooDee` array.
98#[derive(Copy, Clone, Hash, Eq, PartialEq)]
99pub struct TooDeeView<'a, T> {
100    data: &'a [T],
101    num_cols: usize,
102    num_rows: usize,
103    stride: usize,
104}
105
106impl<'a, T> TooDeeView<'a, T> {
107    /// Create a new `TooDeeViewMut` using the provided slice reference.
108    ///
109    /// # Panics
110    ///
111    /// Panics if one of the dimensions is zero but the other is non-zero. This
112    /// is to enforce the rule that empty arrays have no dimensions.
113    ///
114    /// Panics if the slice's length is not sufficient to represent
115    /// the desired array dimensions.
116    ///
117    /// Panics if `num_cols * num_rows` overflows.
118    ///
119    /// # Examples
120    ///
121    /// ```
122    /// use toodee::TooDeeView;
123    /// let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
124    /// let view = TooDeeView::new(4, 3, &data);
125    /// ```
126    pub fn new(num_cols: usize, num_rows: usize, data: &'a [T]) -> TooDeeView<'a, T> {
127        if num_cols == 0 || num_rows == 0 {
128            assert_eq!(num_rows, num_cols);
129        }
130        let size = num_cols.checked_mul(num_rows).unwrap();
131        assert!(size <= data.len());
132        TooDeeView {
133            data: &data[..size],
134            num_cols,
135            num_rows,
136            stride: num_cols,
137        }
138    }
139
140    /// Used internally by `TooDee` to create a `TooDeeView`.
141    pub(super) fn from_toodee(start: Coordinate, end: Coordinate, toodee: &'a TooDee<T>) -> TooDeeView<'a, T> {
142        let stride = toodee.num_cols();
143        let (num_cols, num_rows, data_range) = calculate_view_dimensions(start, end, toodee, stride);
144        unsafe {
145            TooDeeView {
146                data: toodee.data().get_unchecked(data_range),
147                num_cols,
148                num_rows,
149                stride,
150            }
151        }
152    }
153}
154
155impl<'a, T> TooDeeOps<T> for TooDeeView<'a, T>
156{
157    #[inline]
158    fn num_cols(&self) -> usize {
159        self.num_cols
160    }
161
162    #[inline]
163    fn num_rows(&self) -> usize {
164        self.num_rows
165    }
166
167    fn view(&self, start: Coordinate, end: Coordinate) -> TooDeeView<'_, T> {
168        let (num_cols, num_rows, data_range) = calculate_view_dimensions(start, end, self, self.stride);
169        unsafe {
170            TooDeeView {
171                data: self.data.get_unchecked(data_range),
172                num_cols,
173                num_rows,
174                stride: self.stride,
175            }
176        }
177    }
178
179    fn rows(&self) -> Rows<'_, T> {
180        Rows {
181            v: self.data,
182            cols: self.num_cols,
183            skip_cols: self.stride - self.num_cols,
184        }
185    }
186
187    fn col(&self, col: usize) -> Col<'_, T> {
188        let (data_range, skip) = self.get_col_params(col);
189        unsafe {
190            Col {
191                v: self.data.get_unchecked(data_range),
192                skip,
193            }
194        }
195    }
196
197    /// # Examples
198    ///
199    /// ```
200    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
201    /// unsafe {
202    ///     let toodee : TooDee<u32> = TooDee::new(10, 5);
203    ///     let view = toodee.view((0,0), (10,5));
204    ///     let row = view.get_unchecked_row(3);
205    ///     assert_eq!(row.len(), 10);
206    /// }
207    /// ```
208    unsafe fn get_unchecked_row(&self, row: usize) -> &[T] {
209        let start = row * self.stride;
210        self.data.get_unchecked(start..start + self.num_cols)
211    }
212
213    /// # Examples
214    ///
215    /// ```
216    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
217    /// let toodee : TooDee<u32> = TooDee::new(10, 5);
218    /// let view = toodee.view((0,0), (10,5));
219    /// unsafe {
220    ///     assert_eq!(*view.get_unchecked((1,3)), 0);
221    /// }
222    /// ```
223    unsafe fn get_unchecked(&self, coord: Coordinate) -> &T {
224        self.data.get_unchecked(coord.1 * self.stride + coord.0)
225    }
226}
227
228impl<'a, T> Index<usize> for TooDeeView<'a, T> {
229    type Output = [T];
230
231    fn index(&self, row: usize) -> &Self::Output {
232        assert!(row < self.num_rows);
233        let start = row * self.stride;
234        unsafe {
235            self.data.get_unchecked(start..start + self.num_cols)
236        }
237    }
238}
239
240impl<'a, T> Index<Coordinate> for TooDeeView<'a, T> {
241    type Output = T;
242
243    fn index(&self, coord: Coordinate) -> &Self::Output {
244        assert!(coord.1 < self.num_rows);
245        assert!(coord.0 < self.num_cols);
246        // can access the element unchecked because the above assertions hold
247        unsafe {
248            self.data.get_unchecked(coord.1 * self.stride + coord.0)
249        }
250    }
251}
252
253
254/// Provides a mutable view (or subset), of a `TooDee` array.
255#[derive(Hash, Eq, PartialEq)]
256pub struct TooDeeViewMut<'a, T> {
257    data: &'a mut [T],
258    num_cols: usize,
259    num_rows: usize,
260    stride: usize,
261}
262
263
264impl<'a, T> TooDeeViewMut<'a, T> {
265    /// Create a new `TooDeeViewMut` using the provided mutable slice reference.
266    ///
267    /// # Panics
268    ///
269    /// Panics if one of the dimensions is zero but the other is non-zero. This
270    /// is to enforce the rule that empty arrays have no dimensions.
271    ///
272    /// Panics if the slice's length is not sufficient to represent
273    /// the desired array dimensions.
274    ///
275    /// Panics if `num_cols * num_rows` overflows.
276    ///
277    /// # Examples
278    ///
279    /// ```
280    /// use toodee::TooDeeViewMut;
281    /// let mut data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
282    /// let view_mut = TooDeeViewMut::new(4, 3, &mut data);
283    /// ```
284    pub fn new(num_cols: usize, num_rows: usize, data: &'a mut [T]) -> TooDeeViewMut<'a, T> {
285        if num_cols == 0 || num_rows == 0 {
286            assert_eq!(num_rows, num_cols);
287        }
288        let size = num_cols.checked_mul(num_rows).unwrap();
289        assert!(size <= data.len());
290        unsafe {
291            TooDeeViewMut {
292                data: data.get_unchecked_mut(..size),
293                num_cols,
294                num_rows,
295                stride: num_cols,
296            }
297        }
298    }
299
300    /// Used internally by `TooDee` to create a `TooDeeViewMut`.
301    pub(super) fn from_toodee(start: Coordinate, end: Coordinate, toodee: &'a mut TooDee<T>) -> TooDeeViewMut<'a, T> {
302        let stride = toodee.num_cols();
303        let (num_cols, num_rows, data_range) = calculate_view_dimensions(start, end, toodee, stride);
304        unsafe {
305            TooDeeViewMut {
306                data: toodee.data_mut().get_unchecked_mut(data_range),
307                num_cols,
308                num_rows,
309                stride,
310            }
311        }
312    }
313}
314
315
316impl<'a, T> TooDeeOps<T> for TooDeeViewMut<'a, T> {
317    #[inline]
318    fn num_rows(&self) -> usize {
319        self.num_rows
320    }
321
322    #[inline]
323    fn num_cols(&self) -> usize {
324        self.num_cols
325    }
326
327    fn view(&self, start: Coordinate, end: Coordinate) -> TooDeeView<'_, T> {
328        let (num_cols, num_rows, data_range) = calculate_view_dimensions(start, end, self, self.stride);
329        TooDeeView {
330            data: &self.data[data_range],
331            num_cols,
332            num_rows,
333            stride: self.stride,
334        }
335    }
336
337    fn rows(&self) -> Rows<'_, T> {
338        Rows {
339            v: self.data,
340            cols: self.num_cols,
341            skip_cols: self.stride - self.num_cols,
342        }
343    }
344
345    fn col(&self, col: usize) -> Col<'_, T> {
346        let (data_range, skip) = self.get_col_params(col);
347        unsafe {
348            Col {
349                v: self.data.get_unchecked(data_range),
350                skip,
351            }
352        }
353    }
354
355    /// # Examples
356    ///
357    /// ```
358    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
359    /// unsafe {
360    ///     let mut toodee : TooDee<u32> = TooDee::new(10, 5);
361    ///     let mut view = toodee.view_mut((0,0), (10,5));
362    ///     let row = view.get_unchecked_row(3);
363    ///     assert_eq!(row.len(), 10);
364    /// }
365    /// ```
366    unsafe fn get_unchecked_row(&self, row: usize) -> &[T] {
367        let start = row * self.stride;
368        self.data.get_unchecked(start..start + self.num_cols)
369    }
370
371    /// # Examples
372    ///
373    /// ```
374    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
375    /// let mut toodee : TooDee<u32> = TooDee::new(10, 5);
376    /// let mut view = toodee.view_mut((0,0), (10,5));
377    /// unsafe {
378    ///     assert_eq!(*view.get_unchecked((1,3)), 0);
379    /// }
380    /// ```
381    unsafe fn get_unchecked(&self, coord: Coordinate) -> &T {
382        self.data.get_unchecked(coord.1 * self.stride + coord.0)
383    }
384}
385
386impl<'a, T> TooDeeOpsMut<T> for TooDeeViewMut<'a, T> {
387    fn view_mut(&mut self, start: Coordinate, end: Coordinate) -> TooDeeViewMut<'_, T> {
388        let (num_cols, num_rows, data_range) = calculate_view_dimensions(start, end, self, self.stride);
389        unsafe {
390            TooDeeViewMut {
391                data: self.data.get_unchecked_mut(data_range),
392                num_cols,
393                num_rows,
394                stride: self.stride,
395            }
396        }
397    }
398
399    fn rows_mut(&mut self) -> RowsMut<'_, T> {
400        RowsMut {
401            v: self.data,
402            cols: self.num_cols,
403            skip_cols: self.stride - self.num_cols,
404        }
405    }
406
407    fn col_mut(&mut self, col: usize) -> ColMut<'_, T> {
408        let (data_range, skip) = self.get_col_params(col);
409        unsafe {
410            ColMut {
411                v: self.data.get_unchecked_mut(data_range),
412                skip,
413            }
414        }
415    }
416
417    /// Swap/exchange the data between two rows.
418    ///
419    /// # Panics
420    ///
421    /// Panics if either row index is out of bounds.
422    ///
423    /// # Examples
424    ///
425    /// ```
426    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
427    /// let mut toodee : TooDee<u32> = TooDee::init(10, 5, 42u32);
428    /// toodee[0].iter_mut().for_each(|v| *v = 1);
429    /// assert_eq!(toodee[(0, 2)], 42);
430    /// toodee.swap_rows(0, 2);
431    /// assert_eq!(toodee[(0, 2)], 1);
432    /// ```
433    fn swap_rows(&mut self, mut r1: usize, mut r2: usize) {
434        if r1 == r2 {
435            return;
436        }
437        if r2 < r1 {
438            mem::swap(&mut r1, &mut r2);
439        }
440        assert!(r2 < self.num_rows);
441        let num_cols = self.num_cols;
442        unsafe {
443            let (first, rest) = self.data.get_unchecked_mut(r1 * self.stride..).split_at_mut(num_cols);
444            let snd_idx = (r2 - r1) * self.stride - num_cols;
445            let second = rest.get_unchecked_mut(snd_idx..snd_idx + num_cols);
446            // Both slices are guaranteed to have the same length
447            debug_assert_eq!(first.len(), num_cols);
448            debug_assert_eq!(second.len(), num_cols);
449            // We know that the two slices will not overlap because r1 != r2, and we used split_at_mut()
450            ptr::swap_nonoverlapping(first.as_mut_ptr(), second.as_mut_ptr(), num_cols);
451        }
452    }
453
454    /// # Examples
455    ///
456    /// ```
457    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
458    /// unsafe {
459    ///     let mut toodee : TooDee<u32> = TooDee::new(10, 5);
460    ///     let mut view = toodee.view_mut((0,0), (10,5));
461    ///     let row = view.get_unchecked_row_mut(3);
462    ///     assert_eq!(row.len(), 10);
463    /// }
464    /// ```
465    unsafe fn get_unchecked_row_mut(&mut self, row: usize) -> &mut [T] {
466        let start = row * self.stride;
467        self.data.get_unchecked_mut(start..start + self.num_cols)
468    }
469
470
471    /// # Examples
472    ///
473    /// ```
474    /// use toodee::{TooDee,TooDeeOps,TooDeeOpsMut};
475    /// let mut toodee : TooDee<u32> = TooDee::new(10, 5);
476    /// let mut view = toodee.view_mut((0,0), (10,5));
477    /// unsafe {
478    ///     assert_eq!(*view.get_unchecked_mut((1,3)), 0);
479    /// }
480    /// ```
481    unsafe fn get_unchecked_mut(&mut self, coord: Coordinate) -> &mut T {
482        self.data.get_unchecked_mut(coord.1 * self.stride + coord.0)
483    }
484}
485
486impl<'a, T> Index<usize> for TooDeeViewMut<'a, T> {
487    type Output = [T];
488    fn index(&self, row: usize) -> &Self::Output {
489        assert!(row < self.num_rows);
490        let start = row * self.stride;
491        unsafe {
492            self.data.get_unchecked(start..start + self.num_cols)
493        }
494    }
495}
496
497impl<'a, T> Index<Coordinate> for TooDeeViewMut<'a, T> {
498    type Output = T;
499    fn index(&self, coord: Coordinate) -> &Self::Output {
500        assert!(coord.1 < self.num_rows);
501        assert!(coord.0 < self.num_cols);
502        // can access the element unchecked because the above assertions hold
503        unsafe {
504            self.data.get_unchecked(coord.1 * self.stride + coord.0)
505        }
506    }
507}
508
509impl<'a, T> IndexMut<usize> for TooDeeViewMut<'a, T> {
510    fn index_mut(&mut self, row: usize) -> &mut Self::Output {
511        assert!(row < self.num_rows);
512        let start = row * self.stride;
513        unsafe {
514            self.data.get_unchecked_mut(start..start + self.num_cols)
515        }
516    }
517}
518
519impl<'a, T> IndexMut<Coordinate> for TooDeeViewMut<'a, T> {
520    fn index_mut(&mut self, coord: Coordinate) -> &mut Self::Output {
521        assert!(coord.1 < self.num_rows);
522        assert!(coord.0 < self.num_cols);
523        // can access the element unchecked because the above assertions hold
524        unsafe {
525            self.data.get_unchecked_mut(coord.1 * self.stride + coord.0)
526        }
527    }
528}
529
530impl<'a, T> From<TooDeeViewMut<'a, T>> for TooDeeView<'a, T> {
531    fn from(v: TooDeeViewMut<'a, T>) -> TooDeeView<'a, T> {
532        TooDeeView {
533            data: v.data,
534            num_cols: v.num_cols,
535            num_rows: v.num_rows,
536            stride: v.stride,
537        }
538    }
539}
540
541impl<'a, T> IntoIterator for &'a TooDeeView<'a, T> {
542    type Item = &'a T;
543    type IntoIter = Cells<'a, T>;
544    fn into_iter(self) -> Self::IntoIter {
545        self.cells()
546    }
547}
548
549impl<'a, T> IntoIterator for &'a TooDeeViewMut<'a, T> {
550    type Item = &'a T;
551    type IntoIter = Cells<'a, T>;
552    fn into_iter(self) -> Self::IntoIter {
553        self.cells()
554    }
555}
556
557impl<'a, T> IntoIterator for &'a mut TooDeeViewMut<'a, T> {
558    type Item = &'a mut T;
559    type IntoIter = CellsMut<'a, T>;
560    fn into_iter(self) -> Self::IntoIter {
561        self.cells_mut()
562    }
563}
564
565impl<T> Debug for TooDeeView<'_, T> where T: Debug {
566    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
567        f.debug_list().entries(self.rows()).finish()
568    }
569}
570
571impl<T> Debug for TooDeeViewMut<'_, T> where T: Debug {
572    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
573        f.debug_list().entries(self.rows()).finish()
574    }
575}