|
| 1 | +/// An iterator that groups the items in arrays of a specific size. |
| 2 | +/// |
| 3 | +/// See [`.arrays()`](crate::Itertools::arrays) for more information. |
| 4 | +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] |
| 5 | +pub struct Arrays<I: Iterator, const N: usize> |
| 6 | +{ |
| 7 | + iter: I, |
| 8 | + buf: ArrayVec<I::Item, N>, |
| 9 | +} |
| 10 | + |
| 11 | +impl<I: Iterator, const N: usize> Arrays<I, N> { |
| 12 | + pub fn new(iter: I) -> Self { |
| 13 | + Self { iter, buf: ArrayVec::new() } |
| 14 | + } |
| 15 | +} |
| 16 | +impl<I: Iterator, const N: usize> Arrays<I, N> where I::Item: Clone { |
| 17 | + pub fn remaining(self) -> Vec<I::Item> { |
| 18 | + self.buf.into_vec() |
| 19 | + } |
| 20 | +} |
| 21 | + |
| 22 | +impl<I: Iterator, const N: usize> Iterator for Arrays<I, N> { |
| 23 | + type Item = [I::Item; N]; |
| 24 | + |
| 25 | + fn next(&mut self) -> Option<Self::Item> { |
| 26 | + // SAFETY: |
| 27 | + // ArrayVec::push_unchecked is safe as long as len < N. |
| 28 | + // This is guaranteed by the for loop |
| 29 | + unsafe { |
| 30 | + for _ in self.buf.len()..N { |
| 31 | + self.buf.push_unchecked(self.iter.next()?) |
| 32 | + } |
| 33 | + |
| 34 | + Some(self.buf.take_unchecked()) |
| 35 | + } |
| 36 | + } |
| 37 | +} |
| 38 | + |
| 39 | +pub fn next_array<I: Iterator, const N: usize>(iter: &mut I) -> Option<[I::Item; N]> { |
| 40 | + let mut array_vec = ArrayVec::new(); |
| 41 | + |
| 42 | + // SAFETY: |
| 43 | + // ArrayVec::push_unchecked is safe as long as len < N. |
| 44 | + // This is guaranteed by the for loop |
| 45 | + unsafe { |
| 46 | + for _ in 0..N { |
| 47 | + array_vec.push_unchecked(iter.next()?) |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + array_vec.into_array() |
| 52 | +} |
| 53 | + |
| 54 | +// ArrayVec is a safe wrapper around a [T; N]. |
| 55 | +// It allows safely initialising an empty array |
| 56 | +pub struct ArrayVec<T, const N: usize> { |
| 57 | + data: std::mem::MaybeUninit<[T; N]>, |
| 58 | + len: usize, |
| 59 | +} |
| 60 | + |
| 61 | +impl<T, const N: usize> Drop for ArrayVec<T, N> { |
| 62 | + fn drop(&mut self) { |
| 63 | + // SAFETY: |
| 64 | + // The contract of the ArrayVec ensures that data[..len] is initialised |
| 65 | + unsafe { |
| 66 | + let ptr = self.data.as_mut_ptr() as *mut T; |
| 67 | + drop(std::slice::from_raw_parts_mut(ptr, self.len)); |
| 68 | + } |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +impl<T, const N: usize> ArrayVec<T, N> { |
| 73 | + pub const fn new() -> Self { |
| 74 | + Self { |
| 75 | + data: std::mem::MaybeUninit::uninit(), |
| 76 | + len: 0, |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + pub fn push(&mut self, v: T) { |
| 81 | + assert!(self.len < N); |
| 82 | + // SAFETY: asserts that len < N. |
| 83 | + unsafe { self.push_unchecked(v) } |
| 84 | + } |
| 85 | + |
| 86 | + // Unsafety: |
| 87 | + // len must be less than N. If `len < N` is guaranteed, this operation is safe |
| 88 | + // This is because the contract of ArrayVec guarantees that if len < N, then the value |
| 89 | + // at len is valid and uninitialised. |
| 90 | + pub unsafe fn push_unchecked(&mut self, v: T) { |
| 91 | + // The contract of ArrayVec guarantees that the value at self.len, if < N, |
| 92 | + // is uninitialised, and therefore does not need dropping. So use write to |
| 93 | + // overwrite the value |
| 94 | + let ptr = (self.data.as_mut_ptr() as *mut T).add(self.len); |
| 95 | + std::ptr::write(ptr, v); |
| 96 | + self.len += 1; |
| 97 | + } |
| 98 | + |
| 99 | + pub fn len(&self) -> usize { |
| 100 | + self.len |
| 101 | + } |
| 102 | + |
| 103 | + pub fn into_array(self) -> Option<[T; N]> { |
| 104 | + if self.len == N { |
| 105 | + // SAFETY: |
| 106 | + // If len == N, then all the data is initialised and this is safe |
| 107 | + unsafe { Some(self.into_array_unchecked()) } |
| 108 | + } else { |
| 109 | + None |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + // Unsafety: |
| 114 | + // len must be equal to N. If `len == N` is guaranteed, this operation is safe. |
| 115 | + // This is because the contract of ArrayVec guarantees that if len == N, all the values |
| 116 | + // have been initialised correctly. |
| 117 | + unsafe fn into_array_unchecked(mut self) -> [T; N] { |
| 118 | + // move out without dropping |
| 119 | + let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init()); |
| 120 | + std::mem::forget(self); |
| 121 | + data.assume_init() |
| 122 | + } |
| 123 | + |
| 124 | + // Unsafety: |
| 125 | + // len must be equal to N. If `len == N` is guaranteed, this operation is safe. |
| 126 | + // This is because the contract of ArrayVec guarantees that if len == N, all the values |
| 127 | + // have been initialised correctly. |
| 128 | + unsafe fn take_unchecked(&mut self) -> [T; N] { |
| 129 | + let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init()); |
| 130 | + self.len = 0; |
| 131 | + data.assume_init() |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +impl<T: Clone, const N: usize> ArrayVec<T, N> { |
| 136 | + pub fn into_vec(mut self) -> Vec<T> { |
| 137 | + unsafe { |
| 138 | + let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init()); |
| 139 | + let len = self.len; |
| 140 | + std::mem::forget(self); |
| 141 | + std::slice::from_raw_parts(data.as_ptr() as *const T, len).to_vec() |
| 142 | + } |
| 143 | + } |
| 144 | +} |
0 commit comments