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> {}