Skip to content

Commit

Permalink
feat: more docs, more comments
Browse files Browse the repository at this point in the history
  • Loading branch information
conradludgate committed May 25, 2021
1 parent 8e6fcfd commit 8270ed2
Showing 1 changed file with 61 additions and 33 deletions.
94 changes: 61 additions & 33 deletions src/array_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,18 @@ pub struct Arrays<I: Iterator, const N: usize>
}

impl<I: Iterator, const N: usize> Arrays<I, N> {
/// Creates a new Arrays iterator.
/// See [`.arrays()`](crate::Itertools::arrays) for more information.
pub fn new(iter: I) -> Self {
Self { iter, buf: ArrayVec::new() }
}
}
impl<I: Iterator, const N: usize> Arrays<I, N> where I::Item: Clone {
pub fn remaining(self) -> Vec<I::Item> {
self.buf.into_vec()
/// Returns any remaining data left in the iterator.
/// This is useful if the iterator has left over values
/// that didn't fit into the array size.
pub fn remaining(&self) -> &[I::Item] {
self.buf.into_slice()
}
}

Expand All @@ -36,19 +41,23 @@ impl<I: Iterator, const N: usize> Iterator for Arrays<I, N> {
}
}

/// Reads N elements from the iter, returning them in an array. If there's not enough
/// elements, returns None.
pub fn next_array<I: Iterator, const N: usize>(iter: &mut I) -> Option<[I::Item; N]> {
let mut array_vec = ArrayVec::new();

// SAFETY:
// ArrayVec::push_unchecked is safe as long as len < N.
// This is guaranteed by the for loop
unsafe {
// SAFETY:
// ArrayVec::push_unchecked is safe as long as len < N.
// This is guaranteed by the for loop
for _ in 0..N {
array_vec.push_unchecked(iter.next()?)
}
}

array_vec.into_array()
// SAFETY:
// We have guaranteed to have filled all N elements
Some(array_vec.into_array_unchecked())
}
}

// ArrayVec is a safe wrapper around a [T; N].
Expand All @@ -70,23 +79,44 @@ impl<T, const N: usize> Drop for ArrayVec<T, N> {
}

impl<T, const N: usize> ArrayVec<T, N> {
/// Creates a new empty ArrayVec
pub const fn new() -> Self {
Self {
data: std::mem::MaybeUninit::uninit(),
len: 0,
}
}

/// Returns the number of initialised elements in the ArrayVec
pub fn len(&self) -> usize {
self.len
}

/// Returns whether the ArrayVec is empty
pub fn is_empty(&self) -> bool {
self.len == 0
}

/// Returns whether the ArrayVec is full
pub fn is_full(&self) -> bool {
self.len == N
}

/// Push a value into the ArrayVec
///
/// Panics:
/// If the ArrayVec is full, this function will panic
pub fn push(&mut self, v: T) {
assert!(self.len < N);
// SAFETY: asserts that len < N.
assert!(!self.is_full());
// SAFETY: asserted that self is not full
unsafe { self.push_unchecked(v) }
}

// Unsafety:
// len must be less than N. If `len < N` is guaranteed, this operation is safe
// This is because the contract of ArrayVec guarantees that if len < N, then the value
// at len is valid and uninitialised.
/// Push a value into the ArrayVec
///
/// Unsafety:
/// The ArrayVec must not be full. If the ArrayVec is full, this function will try write data
/// out-of-bounds.
pub unsafe fn push_unchecked(&mut self, v: T) {
// The contract of ArrayVec guarantees that the value at self.len, if < N,
// is uninitialised, and therefore does not need dropping. So use write to
Expand All @@ -96,12 +126,9 @@ impl<T, const N: usize> ArrayVec<T, N> {
self.len += 1;
}

pub fn len(&self) -> usize {
self.len
}

/// If the ArrayVec is full, returns the data owned. Otherwise, returns None
pub fn into_array(self) -> Option<[T; N]> {
if self.len == N {
if self.is_full() {
// SAFETY:
// If len == N, then all the data is initialised and this is safe
unsafe { Some(self.into_array_unchecked()) }
Expand All @@ -110,35 +137,36 @@ impl<T, const N: usize> ArrayVec<T, N> {
}
}

// Unsafety:
// len must be equal to N. If `len == N` is guaranteed, this operation is safe.
// This is because the contract of ArrayVec guarantees that if len == N, all the values
// have been initialised correctly.
/// Returns the data owned by the ArrayVec
///
/// Unsafety:
/// The ArrayVec must be full. If it is not full, some of the values in the array will be
/// unintialised. This will cause undefined behaviour
unsafe fn into_array_unchecked(mut self) -> [T; N] {
// move out without dropping
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
std::mem::forget(self);
data.assume_init()
}

// Unsafety:
// len must be equal to N. If `len == N` is guaranteed, this operation is safe.
// This is because the contract of ArrayVec guarantees that if len == N, all the values
// have been initialised correctly.
/// Returns the data owned by the ArrayVec, resetting the ArrayVec to an empty state
///
/// Unsafety:
/// The ArrayVec must be full. If it is not full, some of the values in the array will be
/// unintialised. This will cause undefined behaviour
unsafe fn take_unchecked(&mut self) -> [T; N] {
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
self.len = 0;
data.assume_init()
}
}

impl<T: Clone, const N: usize> ArrayVec<T, N> {
pub fn into_vec(mut self) -> Vec<T> {
/// Borrows the initialised data in the ArrayVec
pub fn into_slice(&self) -> &[T] {
unsafe {
let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
let len = self.len;
std::mem::forget(self);
std::slice::from_raw_parts(data.as_ptr() as *const T, len).to_vec()
// let data = std::mem::replace(&mut self.data, std::mem::MaybeUninit::uninit().assume_init());
// let len = self.len;
// std::mem::forget(self);
std::slice::from_raw_parts(self.data.as_ptr() as *const T, self.len)
}
}
}

0 comments on commit 8270ed2

Please sign in to comment.