bidivec/collections/
bidislice.rs

1use crate::bidiiter::Iter;
2use std::ops::Index;
3#[rustversion::since(1.48)]
4use std::ops::Range;
5
6use crate::{BidiError, BidiView};
7
8/// A bidimensional view over an immutable slice (for the mutable version,
9/// see [`BidiMutSlice`][crate::BidiMutSlice]).
10/// Items are expected to be linearly arranged per rows.
11///
12/// Most methods will not implicitly panic if out-of-bounds accesses are attempted,
13/// but they will return a [`BidiError`] for graceful error handling.
14///
15/// # Examples
16///
17/// ```
18/// use bidivec::BidiSlice;
19///
20/// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
21/// let bslice = BidiSlice::new(&slice, 3).unwrap();
22///
23/// assert_eq!(bslice[(1, 1)], 5);
24/// ```
25#[derive(Debug)]
26pub struct BidiSlice<'a, T> {
27    pub(crate) data: &'a [T],
28    pub(crate) row_size: usize,
29}
30
31impl<'a, T> BidiSlice<'a, T> {
32    /// Constructs a new, empty `BidiSlice<T>`.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use bidivec::BidiSlice;
38    ///
39    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
40    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
41    /// ```
42    #[inline]
43    pub fn new(data: &'a [T], row_size: usize) -> Result<Self, BidiError> {
44        if (data.is_empty() && row_size == 0) || row_size != 0 && (data.len() % row_size) == 0 {
45            Ok(Self { data, row_size })
46        } else {
47            Err(BidiError::IncompatibleSize)
48        }
49    }
50
51    /// Returns the number of items contained in the bidislice.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// use bidivec::BidiSlice;
57    ///
58    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
59    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
60    ///
61    /// assert_eq!(bslice.len(), 9);
62    /// ```
63    pub fn len(&self) -> usize {
64        self.data.len()
65    }
66
67    /// Returns the width (that is, the size of a row) in the bidislice.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use bidivec::BidiSlice;
73    ///
74    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
75    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
76    ///
77    /// assert_eq!(bslice.width(), 3);
78    /// ```
79    pub fn width(&self) -> usize {
80        self.row_size
81    }
82
83    /// Returns the height (that is, the size of a column) in the bidislice.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// use bidivec::BidiSlice;
89    ///
90    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
91    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
92    ///
93    /// assert_eq!(bslice.height(), 3);
94    /// ```
95    pub fn height(&self) -> usize {
96        self.data.len() / self.row_size
97    }
98
99    /// Returns true if the bidislice contains no elements (that
100    /// implies that its width, height and len are all zero).
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use bidivec::BidiSlice;
106    ///
107    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
108    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
109    ///
110    /// assert!(!bslice.is_empty());
111    /// ```
112    pub fn is_empty(&self) -> bool {
113        self.data.is_empty()
114    }
115
116    /// Turns this bidislice back into the slice that was used to
117    /// create it.
118    pub fn into_slice(self) -> &'a [T] {
119        self.data
120    }
121
122    /// Gets the slice that created this bidislice.
123    pub fn as_slice(self) -> &'a [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.
135    pub fn as_ptr(&self) -> *const T {
136        self.data.as_ptr()
137    }
138
139    /// Returns the two raw pointers spanning the slice.
140    ///
141    /// The returned range is half-open, which means that the end pointer
142    /// points *one past* the last element of the slice. This way, an empty
143    /// slice is represented by two equal pointers, and the difference between
144    /// the two pointers represents the size of the slice.
145    ///
146    /// This function is useful for interacting with foreign interfaces which
147    /// use two pointers to refer to a range of elements in memory, as is
148    /// common in C++.
149    ///
150    /// Requires rustc 1.48 or later.
151    #[rustversion::since(1.48)]
152    pub fn as_ptr_range(&self) -> Range<*const T> {
153        self.data.as_ptr_range()
154    }
155
156    /// Accesses an element in the BidiSlice, using its cartesian coordinates.
157    /// If coordinates are outside of range, [`None`] is returned.
158    ///
159    /// If the error is not going to be handled, direct indexing is an easier
160    /// way to achieve the same results.
161    ///
162    /// # Examples
163    ///
164    /// ```
165    /// use bidivec::BidiSlice;
166    ///
167    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
168    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
169    ///
170    /// assert_eq!(*bslice.get(1, 1).unwrap(), 5);
171    /// assert_eq!(bslice[(1, 1)], 5);
172    /// ```
173    #[inline(always)]
174    pub fn get(&self, x: usize, y: usize) -> Option<&T> {
175        match self.calc_index(x, y) {
176            Ok(idx) => Some(unsafe { self.data.get_unchecked(idx) }),
177            Err(_) => None,
178        }
179    }
180
181    /// Checks if the specified coordinates are inside the bidislice bounds
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use bidivec::BidiSlice;
187    ///
188    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
189    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
190    ///
191    /// assert!(bslice.valid_coords(1, 1));
192    /// assert!(!bslice.valid_coords(3, 3));
193    /// ```
194    #[inline(always)]
195    pub fn valid_coords(&self, x: usize, y: usize) -> bool {
196        self.calc_index(x, y).is_ok()
197    }
198
199    #[inline(always)]
200    fn calc_index(&self, x: usize, y: usize) -> Result<usize, BidiError> {
201        let idx = y * self.row_size + x;
202        if x >= self.row_size || idx >= self.data.len() {
203            Err(BidiError::OutOfBounds)
204        } else {
205            Ok(idx)
206        }
207    }
208
209    /// Converts this bidislice to an immutable [`BidiView`].
210    pub fn as_bidiview(&self) -> &dyn BidiView<Output = T> {
211        self
212    }
213
214    /// Returns an iterator over the items of the view
215    pub fn iter(&self) -> Iter<T, Self> {
216        Iter::new(self)
217    }
218}
219
220impl<'a, T> Index<(usize, usize)> for BidiSlice<'a, T> {
221    type Output = T;
222
223    /// Accesses an element in the BidiSlice, using its cartesian coordinates.
224    /// If coordinates are outside of range, it panics.
225    ///
226    /// If you want a more graceful error handling, see [`BidiSlice::get`].
227    ///
228    /// # Examples
229    ///
230    /// ```
231    /// use bidivec::BidiSlice;
232    ///
233    /// let slice = [1, 2, 3, 4, 5, 6, 7, 8, 9];
234    /// let bslice = BidiSlice::new(&slice, 3).unwrap();
235    ///
236    /// assert_eq!(bslice[(1, 1)], 5);
237    /// ```
238    #[inline(always)]
239    fn index(&self, index: (usize, usize)) -> &Self::Output {
240        let idx = self.calc_index(index.0, index.1).unwrap_or_else(|_| {
241            panic!(
242                "Indexes out of bidislice bounds: ({},{}) out of {}x{}",
243                index.0,
244                index.1,
245                self.width(),
246                self.height()
247            )
248        });
249        unsafe { self.data.get_unchecked(idx) }
250    }
251}
252
253impl<'a, T> BidiView for BidiSlice<'a, T> {
254    fn width(&self) -> usize {
255        BidiSlice::<T>::width(self)
256    }
257    fn height(&self) -> usize {
258        BidiSlice::<T>::height(self)
259    }
260
261    fn get(&self, x: usize, y: usize) -> Option<&T> {
262        self.get(x, y)
263    }
264}