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}