You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
5.4 KiB
223 lines
5.4 KiB
//! Ad-hoc exact size owning iterator macro and other optional utils
|
|
//!
|
|
//! The macro can be used exactly as `vec!`.
|
|
|
|
#[cfg(feature="maybe-many")] pub mod maybe;
|
|
|
|
/// A bespoke iterator type with an exact size over elements.
|
|
///
|
|
/// This has an advantage over using simple inline slices (e.g `[1,2,3]`) as they currently cannot implement `IntoIterator` properly, and will always yield references.
|
|
/// This can be a hinderance when you want an ad-hoc iterator of non-`Copy` items.
|
|
///
|
|
/// The iterator types created from this macro consume the values.
|
|
/// It has the advantage over `vec![].into_iter()` as there are no heap allocations needed and size of the iterator is known at compile time.
|
|
///
|
|
/// # Example
|
|
/// ```
|
|
/// # use ad_hoc_iter::iter;
|
|
/// let sum: i32 = iter![1, 2].chain(3..=4).chain(iter![5, 6]).sum();
|
|
/// assert_eq!(sum, 21);
|
|
/// ```
|
|
/// # Functions
|
|
/// The iterators returned from this method have these associated functions:
|
|
///
|
|
/// ## The length of the whole iterator
|
|
/// ```
|
|
/// pub const fn len(&self) -> usize
|
|
/// ```
|
|
///
|
|
/// ## The rest of the iterator that has not been consumed.
|
|
/// ```
|
|
/// pub fn rest(&self) -> &[T]
|
|
/// ```
|
|
///
|
|
/// ## The whole array.
|
|
/// All values that have not been consumed are initialised, values that have been consumed are uninitialised.
|
|
/// ```
|
|
/// pub fn array(&self) -> &[MaybeUninit<T>; Self::LEN]
|
|
/// ```
|
|
///
|
|
/// ## How many items have since been consumed.
|
|
/// ```
|
|
/// pub const fn consumed(&self) -> usize
|
|
/// ```
|
|
#[macro_export] macro_rules! iter {
|
|
(@) => (0usize);
|
|
(@ $x:tt $($xs:tt)* ) => (1usize + $crate::iter!(@ $($xs)*));
|
|
|
|
() => {
|
|
{
|
|
#[derive(Debug)]
|
|
struct Empty;
|
|
impl Iterator for Empty
|
|
{
|
|
type Item = std::convert::Infallible;
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
None
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>)
|
|
{
|
|
(0, Some(0))
|
|
}
|
|
}
|
|
impl ::std::iter::FusedIterator for Empty{}
|
|
impl ::std::iter::ExactSizeIterator for Empty{}
|
|
|
|
Empty
|
|
}
|
|
};
|
|
|
|
($($value:expr),*) => {
|
|
{
|
|
use ::std::mem::MaybeUninit;
|
|
use ::std::ops::Drop;
|
|
struct Arr<T>([MaybeUninit<T>; $crate::iter!(@ $($value)*)], usize);
|
|
impl<T> Arr<T>
|
|
{
|
|
#![allow(dead_code)]
|
|
/// The length of the whole iterator
|
|
const LEN: usize = $crate::iter!(@ $($value)*);
|
|
|
|
/// The length of the whole iterator
|
|
// This exists as an associated function because this type is opaque.
|
|
#[inline] pub const fn len(&self) -> usize
|
|
{
|
|
Self::LEN
|
|
}
|
|
|
|
/// Consume this iterator into the backing buffer.
|
|
///
|
|
/// # Safety
|
|
/// Non-consumed items are safe to `assume_init`. However, items that have already been consumed are uninitialised.
|
|
fn into_inner(self) -> [MaybeUninit<T>; $crate::iter!(@ $($value)*)]
|
|
{
|
|
//XXX: We will have to do something really unsafe for this to work on stable...
|
|
todo!()
|
|
}
|
|
|
|
/// The rest of the iterator that has not been consumed.
|
|
///
|
|
// # Safety
|
|
// All values in this slice are initialised.
|
|
#[inline] pub fn rest(&self) -> &[T]
|
|
{
|
|
let slice = &self.0[self.1..];
|
|
//std::mem::MaybeUninit::slice_get_ref(&self.0[self.1..]) //nightly only...
|
|
|
|
unsafe { &*(slice as *const [std::mem::MaybeUninit<T>] as *const [T]) }
|
|
}
|
|
|
|
/// The whole array.
|
|
///
|
|
/// # Safety
|
|
/// All values that have not been consumed are initialised, values that have been consumed are uninitialised.
|
|
#[inline] pub fn array(&self) -> &[MaybeUninit<T>; $crate::iter!(@ $($value)*)]
|
|
{
|
|
&self.0
|
|
}
|
|
|
|
/// How many items have since been consumed.
|
|
pub const fn consumed(&self) -> usize
|
|
{
|
|
self.1
|
|
}
|
|
}
|
|
impl<T> Iterator for Arr<T>
|
|
{
|
|
type Item = T;
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
if self.1 >= self.0.len() {
|
|
None
|
|
} else {
|
|
//take one
|
|
let one = unsafe {
|
|
::std::mem::replace(&mut self.0[self.1], MaybeUninit::uninit()).assume_init()
|
|
};
|
|
self.1+=1;
|
|
Some(one)
|
|
}
|
|
}
|
|
|
|
#[inline] fn size_hint(&self) -> (usize, Option<usize>)
|
|
{
|
|
(Self::LEN, Some(Self::LEN))
|
|
}
|
|
}
|
|
impl<T> ::std::iter::FusedIterator for Arr<T>{}
|
|
impl<T> ::std::iter::ExactSizeIterator for Arr<T>{}
|
|
|
|
impl<T> Drop for Arr<T>
|
|
{
|
|
fn drop(&mut self) {
|
|
if ::std::mem::needs_drop::<T>() {
|
|
for idx in self.1..self.0.len() {
|
|
unsafe {
|
|
::std::mem::replace(&mut self.0[idx], MaybeUninit::uninit()).assume_init();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Arr([$(MaybeUninit::new($value)),*], 0)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests
|
|
{
|
|
#[test]
|
|
fn iter_over()
|
|
{
|
|
const EXPECT: usize = 10 + 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1;
|
|
let iter = iter![10,9,8,7,6,5,4,3,2,1];
|
|
|
|
assert_eq!(iter.len(), 10);
|
|
assert_eq!(iter.sum::<usize>(), EXPECT);
|
|
|
|
}
|
|
|
|
|
|
macro_rules! string {
|
|
($val:literal) => (String::from($val));
|
|
}
|
|
#[test]
|
|
fn non_copy()
|
|
{
|
|
|
|
const EXPECT: &str = "Hello world!";
|
|
let whole: String = iter![string!("Hell"), string!("o "), string!("world"), string!("!")].collect();
|
|
assert_eq!(EXPECT, &whole[..]);
|
|
}
|
|
|
|
#[test]
|
|
fn empty()
|
|
{
|
|
let empty: Vec<_> = iter![].collect();
|
|
assert_eq!(empty.len(), 0);
|
|
}
|
|
|
|
#[test]
|
|
fn assoc()
|
|
{
|
|
let mut iter = iter![1,2,3,4];
|
|
|
|
assert_eq!(iter.len(), 4);
|
|
iter.next();
|
|
assert_eq!(iter.consumed(), 1);
|
|
assert_eq!(iter.rest(), &[2,3,4]);
|
|
|
|
|
|
let mut iter = iter![string!("Hell"), string!("o "), string!("world"), string!("!")];
|
|
|
|
iter.next();
|
|
iter.next();
|
|
|
|
assert_eq!(iter.rest().iter().map(|x| x.as_str()).collect::<Vec<_>>().as_slice(), &["world", "!"]);
|
|
}
|
|
}
|