bidivec/collections/
bidimutslice.rs

1use crate::bidiiter::{Iter, IterMut};
2use std::iter::Iterator;
3#[rustversion::since(1.48)]
4use std::ops::Range;
5use std::ops::{Index, IndexMut};
6
7use crate::*;
8
9/// A bidimensional view over a mutable slice (for the immutable version,
10/// see [`BidiSlice`]).
11/// Items are expected to be linearly arranged per rows.
12///
13/// Most methods will not implicitly panic if out-of-bounds accesses are attempted,
14/// but they will return a [`BidiError`] for graceful error handling.
15///
16/// # Examples
17///
18/// ```
19/// use bidivec::BidiMutSlice;
20///
21/// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
22/// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
23///
24/// assert_eq!(bslice[(1, 1)], 5);
25///
26/// bslice[(1, 1)] = 12;
27///
28/// assert_eq!(bslice[(1, 1)], 12);
29/// ```
30#[derive(Debug)]
31pub struct BidiMutSlice<'a, T> {
32    pub(crate) data: &'a mut [T],
33    pub(crate) row_size: usize,
34}
35
36impl<'a, T> BidiMutSlice<'a, T> {
37    /// Constructs a new, empty [`BidiMutSlice<T>`].
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use bidivec::BidiMutSlice;
43    ///
44    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
45    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
46    /// ```
47    #[inline]
48    pub fn new(data: &'a mut [T], row_size: usize) -> Result<Self, BidiError> {
49        if (data.is_empty() && row_size == 0) || row_size != 0 && (data.len() % row_size) == 0 {
50            Ok(Self { data, row_size })
51        } else {
52            Err(BidiError::IncompatibleSize)
53        }
54    }
55
56    /// Returns the number of items contained in the bidislice.
57    ///
58    /// # Examples
59    ///
60    /// ```
61    /// use bidivec::BidiMutSlice;
62    ///
63    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
64    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
65    ///
66    /// assert_eq!(bslice.len(), 9);
67    /// ```
68    pub fn len(&self) -> usize {
69        self.data.len()
70    }
71
72    /// Returns the width (that is, the size of a row) in the bidislice.
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// use bidivec::BidiMutSlice;
78    ///
79    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
80    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
81    ///
82    /// assert_eq!(bslice.width(), 3);
83    /// ```
84    pub fn width(&self) -> usize {
85        self.row_size
86    }
87
88    /// Returns the height (that is, the size of a column) in the bidislice.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use bidivec::BidiMutSlice;
94    ///
95    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
96    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
97    ///
98    /// assert_eq!(bslice.height(), 3);
99    /// ```
100    pub fn height(&self) -> usize {
101        self.data.len() / self.row_size
102    }
103
104    /// Returns true if the bidislice contains no elements (that
105    /// implies that its width, height and len are all zero).
106    ///
107    /// # Examples
108    ///
109    /// ```
110    /// use bidivec::BidiMutSlice;
111    ///
112    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
113    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
114    ///
115    /// assert!(!bslice.is_empty());
116    /// ```
117    pub fn is_empty(&self) -> bool {
118        self.data.is_empty()
119    }
120
121    /// Turns this bidislice back into the slice that was used to
122    /// create it.
123    pub fn into_slice(self) -> &'a mut [T] {
124        self.data
125    }
126
127    /// Returns a raw pointer to the bidislice's buffer.
128    ///
129    /// The caller must ensure that the underlying slice outlives the pointer this
130    /// function returns, or else it will end up pointing to garbage.
131    ///
132    /// The caller must also ensure that the memory the pointer (non-transitively) points to
133    /// is never written to (except inside an [`UnsafeCell`][std::cell::UnsafeCell]) using this pointer or any pointer
134    /// derived from it. If you need to mutate the contents of the slice, use [`Vec::as_mut_ptr`].
135    pub fn as_ptr(&self) -> *const T {
136        self.data.as_ptr()
137    }
138
139    /// Returns an unsafe mutable pointer to the bidislice's buffer.
140    ///
141    /// The caller must ensure that the underlying slice outlives the pointer this
142    /// function returns, or else it will end up pointing to garbage.
143    pub fn as_mut_ptr(&mut self) -> *mut T {
144        self.data.as_mut_ptr()
145    }
146
147    /// Returns the two raw pointers spanning the slice.
148    ///
149    /// The returned range is half-open, which means that the end pointer
150    /// points *one past* the last element of the slice. This way, an empty
151    /// slice is represented by two equal pointers, and the difference between
152    /// the two pointers represents the size of the slice.
153    ///
154    /// This function is useful for interacting with foreign interfaces which
155    /// use two pointers to refer to a range of elements in memory, as is
156    /// common in C++.
157    ///
158    /// Requires rustc 1.48 or later.
159    #[rustversion::since(1.48)]
160    pub fn as_ptr_range(&self) -> Range<*const T> {
161        self.data.as_ptr_range()
162    }
163
164    /// Returns the two mutable raw pointers spanning the slice.
165    ///
166    /// The returned range is half-open, which means that the end pointer
167    /// points *one past* the last element of the slice. This way, an empty
168    /// slice is represented by two equal pointers, and the difference between
169    /// the two pointers represents the size of the slice.
170    ///
171    /// This function is useful for interacting with foreign interfaces which
172    /// use two pointers to refer to a range of elements in memory, as is
173    /// common in C++.
174    ///
175    /// Requires rustc 1.48 or later.
176    #[rustversion::since(1.48)]
177    pub fn as_mut_ptr_range(&mut self) -> Range<*mut T> {
178        self.data.as_mut_ptr_range()
179    }
180
181    /// Swaps two elements in the bidislice. If any of the coordinates are out
182    /// of range, [`BidiError::OutOfBounds`] is returned
183    ///
184    /// # Examples
185    ///
186    /// ```
187    /// use bidivec::BidiMutSlice;
188    ///
189    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
190    /// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
191    ///
192    /// assert_eq!(bslice[(2, 0)], 3);
193    /// assert_eq!(bslice[(1, 2)], 8);
194    ///
195    /// bslice.swap((2, 0), (1, 2)).unwrap();
196    ///
197    /// assert_eq!(bslice[(2, 0)], 8);
198    /// assert_eq!(bslice[(1, 2)], 3);
199    /// ```
200    #[inline(always)]
201    pub fn swap(&mut self, a: (usize, usize), b: (usize, usize)) -> Result<(), BidiError> {
202        let idx_a = self.calc_index(a.0, a.1)?;
203        let idx_b = self.calc_index(b.0, b.1)?;
204
205        self.data.swap(idx_a, idx_b);
206        Ok(())
207    }
208
209    /// Accesses an element in the BidiMutSlice, using its cartesian coordinates.
210    /// If coordinates are outside of range, [`None`] is returned.
211    ///
212    /// If the error is not going to be handled, direct indexing is an easier
213    /// way to achieve the same results.
214    ///
215    /// # Examples
216    ///
217    /// ```
218    /// use bidivec::BidiMutSlice;
219    ///
220    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
221    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
222    ///
223    /// assert_eq!(*bslice.get(1, 1).unwrap(), 5);
224    /// assert_eq!(bslice[(1, 1)], 5);
225    /// ```
226    #[inline(always)]
227    pub fn get(&self, x: usize, y: usize) -> Option<&T> {
228        match self.calc_index(x, y) {
229            Ok(idx) => Some(unsafe { self.data.get_unchecked(idx) }),
230            Err(_) => None,
231        }
232    }
233
234    /// Mutably accesses an element in the BidiMutSlice, using its cartesian coordinates.
235    /// If coordinates are outside of range, [`None`] is returned.
236    ///
237    /// If the error is not going to be handled, direct indexing is an easier
238    /// way to achieve the same results.
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// use bidivec::BidiMutSlice;
244    ///
245    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
246    /// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
247    ///
248    /// *bslice.get_mut(1, 1).unwrap() = 12;
249    ///
250    /// assert_eq!(*bslice.get(1, 1).unwrap(), 12);
251    ///
252    /// bslice[(1, 1)] = 13;
253    ///
254    /// assert_eq!(*bslice.get(1, 1).unwrap(), 13);
255    /// ```
256    #[inline(always)]
257    pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
258        match self.calc_index(x, y) {
259            Ok(idx) => Some(unsafe { self.data.get_unchecked_mut(idx) }),
260            Err(_) => None,
261        }
262    }
263
264    /// Checks if the specified coordinates are inside the bidislice bounds
265    ///
266    /// # Examples
267    ///
268    /// ```
269    /// use bidivec::BidiMutSlice;
270    ///
271    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
272    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
273    ///
274    /// assert!(bslice.valid_coords(1, 1));
275    /// assert!(!bslice.valid_coords(3, 3));
276    /// ```
277    #[inline(always)]
278    pub fn valid_coords(&self, x: usize, y: usize) -> bool {
279        self.calc_index(x, y).is_ok()
280    }
281
282    #[inline(always)]
283    fn calc_index(&self, x: usize, y: usize) -> Result<usize, BidiError> {
284        let idx = y * self.row_size + x;
285        if x >= self.row_size || idx >= self.data.len() {
286            Err(BidiError::OutOfBounds)
287        } else {
288            Ok(idx)
289        }
290    }
291
292    /// Reverses the order of the items in the specified row.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// use bidivec::BidiMutSlice;
298    ///
299    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
300    /// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
301    ///
302    /// assert_eq!(bslice[(2, 1)], 6);
303    ///
304    /// bslice.reverse_row(1).unwrap();
305    ///
306    /// assert_eq!(bslice[(2, 1)], 4);
307    /// ```
308    pub fn reverse_row(&mut self, row: usize) -> Result<(), BidiError> {
309        if row >= self.height() {
310            return Err(BidiError::OutOfBounds);
311        }
312
313        let width = self.width();
314
315        for x in 0..(width / 2) {
316            self.swap((x, row), (width - 1 - x, row)).unwrap();
317        }
318
319        Ok(())
320    }
321
322    /// Reverses the order of the items in the specified column.
323    ///
324    /// # Examples
325    ///
326    /// ```
327    /// use bidivec::BidiMutSlice;
328    ///
329    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
330    /// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
331    ///
332    /// assert_eq!(bslice[(1, 2)], 8);
333    ///
334    /// bslice.reverse_col(1).unwrap();
335    ///
336    /// assert_eq!(bslice[(1, 2)], 2);
337    /// ```
338    pub fn reverse_col(&mut self, col: usize) -> Result<(), BidiError> {
339        if col >= self.width() {
340            return Err(BidiError::OutOfBounds);
341        }
342
343        let height = self.height();
344
345        for y in 0..(height / 2) {
346            self.swap((col, y), (col, height - 1 - y)).unwrap();
347        }
348
349        Ok(())
350    }
351
352    /// Transposes the bidislice, that is an operation that flips the bidislice
353    /// over its diagonal (or, more simply, switches the meaning of columns and
354    /// rows). As such, the result of a transposition is as wide as the original
355    /// was tall, and as tall as the original was wide.
356    ///
357    /// While this is performed in-place, it still requires O(n) additional memory
358    /// if the bidislice width and height are different (i.e. it's not a square).
359    pub fn transpose(&mut self) {
360        let width = self.width();
361        let height = self.height();
362
363        if width == height {
364            for x in 0..width {
365                for y in (x + 1)..height {
366                    self.swap((x, y), (y, x)).unwrap();
367                }
368            }
369        } else if width != 0 && height != 0 {
370            let last = self.data.len() - 1;
371            let mut visited = vec![false; self.data.len()].into_boxed_slice();
372
373            visited[0] = true;
374            visited[last] = true;
375
376            let mut next_index = Some(1);
377            while let Some(mut i) = next_index {
378                let cycle_start = i;
379                loop {
380                    let next = (i * width) % last;
381
382                    if visited[next] {
383                        visited[i] = true;
384                        break;
385                    }
386
387                    self.data.swap(next, i);
388                    visited[i] = true;
389                    i = next;
390
391                    if i == cycle_start {
392                        break;
393                    }
394                }
395
396                next_index = visited
397                    .iter()
398                    .enumerate()
399                    .find(|(_, v)| !**v)
400                    .map(|(idx, _)| idx);
401            }
402            self.row_size = height;
403        }
404    }
405
406    /// Rotates the bidislice 90°, counter-clockwise (or, 270° clockwise).
407    /// The result of such a rotation is as wide as the original
408    /// was tall, and as tall as the original was wide.
409    ///
410    /// While this is performed in-place, it still requires O(n) additional memory
411    /// if the bidislice width and height are different (i.e. it's not a square).
412    pub fn rotate90ccw(&mut self) {
413        self.transpose();
414        self.reverse_columns();
415    }
416
417    /// Rotates the bidislice 180°.
418    pub fn rotate180(&mut self) {
419        self.data.reverse();
420    }
421
422    /// Rotates the bidislice 270°, counter-clockwise (or, 90° clockwise).
423    /// The result of such a rotation is as wide as the original
424    /// was tall, and as tall as the original was wide.
425    ///
426    /// While this is performed in-place, it still requires O(n) additional memory
427    /// if the bidislice width and height are different (i.e. it's not a square).
428    pub fn rotate270ccw(&mut self) {
429        self.transpose();
430        self.reverse_rows();
431    }
432
433    /// Reverse the order of items in all columns. This is equivalent to flipping
434    /// the data structure over its horizontal axis.
435    pub fn reverse_columns(&mut self) {
436        for col in 0..self.width() {
437            self.reverse_col(col).unwrap();
438        }
439    }
440
441    /// Reverse the order of items in all rows. This is equivalent to flipping
442    /// the data structure over its vertical axis.
443    pub fn reverse_rows(&mut self) {
444        for row in 0..self.height() {
445            self.reverse_row(row).unwrap();
446        }
447    }
448
449    /// Converts this bidislice to an immutable [`BidiView`].
450    pub fn as_bidiview(&self) -> &dyn BidiView<Output = T> {
451        self
452    }
453
454    /// Converts this bidislice to a mutable [`BidiView`].
455    pub fn as_bidiview_mut(&mut self) -> &dyn BidiViewMut<Output = T> {
456        self
457    }
458
459    /// Returns an iterator over the items of the view
460    pub fn iter(&self) -> Iter<T, Self> {
461        Iter::new(self)
462    }
463
464    /// Returns a mutable iterator over the items of the view
465    pub fn iter_mut(&mut self) -> IterMut<T, Self> {
466        IterMut::new(self)
467    }
468}
469
470impl<'a, T> Index<(usize, usize)> for BidiMutSlice<'a, T> {
471    type Output = T;
472
473    /// Accesses an element in the BidiMutSlice, using its cartesian coordinates.
474    /// If coordinates are outside of range, it panics.
475    ///
476    /// If you want a more graceful error handling, see [`BidiMutSlice::get`].
477    ///
478    /// # Examples
479    ///
480    /// ```
481    /// use bidivec::BidiMutSlice;
482    ///
483    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
484    /// let bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
485    ///
486    /// assert_eq!(bslice[(1, 1)], 5);
487    /// ```
488    #[inline(always)]
489    fn index(&self, index: (usize, usize)) -> &Self::Output {
490        let idx = self.calc_index(index.0, index.1).unwrap_or_else(|_| {
491            panic!(
492                "Indexes out of bidislice bounds: ({},{}) out of {}x{}",
493                index.0,
494                index.1,
495                self.width(),
496                self.height()
497            )
498        });
499        unsafe { self.data.get_unchecked(idx) }
500    }
501}
502
503impl<'a, T> IndexMut<(usize, usize)> for BidiMutSlice<'a, T> {
504    /// Mutably accesses an element in the BidiMutSlice, using its cartesian coordinates.
505    /// If coordinates are outside of range, it panics.
506    ///
507    /// If you want a more graceful error handling, see [`BidiMutSlice::get_mut`].
508    ///
509    /// # Examples
510    ///
511    /// ```
512    /// use bidivec::BidiMutSlice;
513    ///
514    /// let mut slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
515    /// let mut bslice = BidiMutSlice::new(&mut slice, 3).unwrap();
516    ///
517    /// bslice[(1, 1)] = 12;
518    ///
519    /// assert_eq!(bslice[(1, 1)], 12);
520    /// ```
521    #[inline(always)]
522    fn index_mut(&mut self, index: (usize, usize)) -> &mut Self::Output {
523        let idx = self.calc_index(index.0, index.1).unwrap_or_else(|_| {
524            panic!(
525                "Indexes out of bidislice bounds: ({},{}) out of {}x{}",
526                index.0,
527                index.1,
528                self.width(),
529                self.height()
530            )
531        });
532        unsafe { self.data.get_unchecked_mut(idx) }
533    }
534}
535
536impl<'a, T> BidiView for BidiMutSlice<'a, T> {
537    fn width(&self) -> usize {
538        BidiMutSlice::<T>::width(self)
539    }
540    fn height(&self) -> usize {
541        BidiMutSlice::<T>::height(self)
542    }
543
544    fn get(&self, x: usize, y: usize) -> Option<&T> {
545        self.get(x, y)
546    }
547}
548
549impl<'a, T> BidiViewMut for BidiMutSlice<'a, T> {
550    fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
551        self.get_mut(x, y)
552    }
553}
554
555unsafe impl<'a, T> BidiViewMutIterable for BidiMutSlice<'a, T> {}