Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement more methods on stable Rust #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ default = []
nightly_features = []

[dependencies]
typenum = { version = "1.17.0", features = ["const-generics"] }
typenum_mappings = "0.1.0"
60 changes: 44 additions & 16 deletions src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,50 @@
#[cfg(feature = "nightly_features")]
pub const fn min(a: usize, b: usize) -> usize {
if a < b {
a
} else {
b
}
use typenum::{operator_aliases as ty_ops, U};

use crate::IteratorFixed;

/// A trait implemented for all `IterFixed<N>` to allow constructing `IterFixed` when `N` is unnamable.
pub trait ErasedIterFixed<I> {
/// # Safety
///
/// The N value of the [`IterFixed`] created must be the same as the number of items in the Iterator.
unsafe fn new(iter: I) -> Self;
}

#[cfg(feature = "nightly_features")]
pub const fn sub_or_zero(a: usize, b: usize) -> usize {
if a > b {
a - b
} else {
0
impl<I: Iterator, const N: usize> ErasedIterFixed<I> for IteratorFixed<I, N> {
unsafe fn new(inner: I) -> Self {
Self { inner }
}
}

#[cfg(feature = "nightly_features")]
pub const fn ceiling_div(x: usize, d: usize) -> usize {
x / d + (x % d != 0) as usize
pub trait TypenumToFixedIter<I: Iterator> {
type FixedIter: ErasedIterFixed<I>;
}

typenum_mappings::impl_typenum_mapping!(
impl<const N: usize = 0..=1024, I: Iterator> TypenumToFixedIter<I> for #TypeNumName {
type FixedIter = IteratorFixed<I, N>;
}
);

pub(crate) type RunTypeNumToFixedIter<I, T> = <T as TypenumToFixedIter<I>>::FixedIter;

pub(crate) type TyNot<T> = <T as core::ops::Not>::Output;
type TyCelDiv<X, D> = ty_ops::Sum<
// X / D
ty_ops::Quot<X, D>,
// + !
TyNot<
ty_ops::Eq<
// X % 0
ty_ops::Mod<X, D>,
// == 0
typenum::U0,
>,
>,
>;

pub(crate) type RunCelDiv<const X: usize, const D: usize> = TyCelDiv<U<X>, U<D>>;
pub(crate) type RunAdd<const X: usize, const Y: usize> = ty_ops::Sum<U<X>, U<Y>>;
pub(crate) type RunSub<const X: usize, const Y: usize> = ty_ops::Diff<U<X>, U<Y>>;
pub(crate) type RunMul<const X: usize, const Y: usize> = ty_ops::Prod<U<X>, U<Y>>;
pub(crate) type RunMin<const X: usize, const Y: usize> = ty_ops::Minimum<U<X>, U<Y>>;
92 changes: 58 additions & 34 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,26 @@
#![warn(clippy::missing_inline_in_public_items)]
#![warn(clippy::use_self)]

use core::iter;
use core::{
iter,
ops::{Add, Div, Mul, Not, Rem, Sub},
};

mod from;
mod helpers;
mod into;

#[cfg(feature = "nightly_features")]
use helpers::{ceiling_div, min, sub_or_zero};
use helpers::*;

pub use from::FromIteratorFixed;
pub use into::IntoIteratorFixed;
use typenum::{Const, Eq, IsEqual, Min, Mod, ToUInt, U, U0};

type FlattenIter<I, IIF, const M: usize> = iter::FlatMap<
I,
IteratorFixed<<IIF as IntoIteratorFixed<M>>::IntoIter, M>,
fn(IIF) -> IteratorFixed<<IIF as IntoIteratorFixed<M>>::IntoIter, M>,
>;

/// Iterator of fixed size
///
Expand All @@ -71,10 +80,10 @@ pub use into::IntoIteratorFixed;
///
/// Just like [`Iterator`], [`IteratorFixed`] provides a lot of methods like:
/// - available on stable rust:
/// [`map`], [`inspect`], [`enumerate`], [`zip`], [`rev`], [`copied`], [`cloned`]
/// [`map`], [`inspect`], [`enumerate`], [`zip`], [`rev`], [`copied`], [`cloned`], [`skip`], [`step_by`], [`chain`], [`take`], [`flatten`]
///
/// - requires nightly compiler and enable `nightly_features`:
/// [`skip`], [`step_by`], [`chain`], [`take`], [`flatten`]
/// [`flat_map`]
///
/// however it does not support methods like `filter` or `take_while` which will affect the length during runtime.
///
Expand All @@ -90,6 +99,7 @@ pub use into::IntoIteratorFixed;
/// [`chain`]: IteratorFixed::chain
/// [`take`]: IteratorFixed::take
/// [`flatten`]: IteratorFixed::flatten
/// [`flat_map`]: IteratorFixed::flat_map
pub struct IteratorFixed<I: Iterator, const N: usize> {
inner: I,
}
Expand Down Expand Up @@ -160,40 +170,52 @@ where

// TODO: what should happen when SKIP > N?
/// See [`core::iter::Iterator::skip`]
#[cfg(feature = "nightly_features")]
#[inline]
pub fn skip<const SKIP: usize>(
self,
) -> IteratorFixed<impl Iterator<Item = I::Item>, { sub_or_zero(N, SKIP) }> {
IteratorFixed {
inner: self.inner.skip(SKIP),
}
pub fn skip<const SKIP: usize>(self) -> RunTypeNumToFixedIter<iter::Skip<I>, RunSub<N, SKIP>>
where
Const<N>: ToUInt,
Const<SKIP>: ToUInt,

U<N>: Sub<U<SKIP>, Output: TypenumToFixedIter<iter::Skip<I>>>,
{
unsafe { ErasedIterFixed::new(self.inner.skip(SKIP)) }
}

/// See [`core::iter::Iterator::step_by`]
#[cfg(feature = "nightly_features")]
#[inline]
pub fn step_by<const STEP: usize>(
self,
) -> IteratorFixed<impl Iterator<Item = I::Item>, { ceiling_div(N, STEP) }> {
IteratorFixed {
inner: self.inner.step_by(STEP),
}
) -> RunTypeNumToFixedIter<iter::StepBy<I>, RunCelDiv<N, STEP>>
where
Const<N>: ToUInt,
Const<STEP>: ToUInt,

U<N>: Rem<U<STEP>, Output: IsEqual<U0, Output: Not>>,
U<N>: Div<
U<STEP>,
Output: Add<
TyNot<Eq<Mod<U<N>, U<STEP>>, U0>>,
Output: TypenumToFixedIter<iter::StepBy<I>>,
>,
>,
{
unsafe { ErasedIterFixed::new(self.inner.step_by(STEP)) }
}

/// See [`core::iter::Iterator::chain`]
#[cfg(feature = "nightly_features")]
#[inline]
pub fn chain<IIF, const M: usize>(
self,
other: IIF,
) -> IteratorFixed<impl Iterator<Item = I::Item>, { N + M }>
) -> RunTypeNumToFixedIter<iter::Chain<I, IIF::IntoIter>, RunAdd<N, M>>
where
Const<N>: ToUInt,
Const<M>: ToUInt,

IIF: IntoIteratorFixed<M, Item = I::Item>,
U<N>: Add<U<M>, Output: TypenumToFixedIter<iter::Chain<I, IIF::IntoIter>>>,
{
IteratorFixed {
inner: self.inner.chain(other.into_iter_fixed().inner),
}
unsafe { ErasedIterFixed::new(self.inner.chain(other.into_iter_fixed().inner)) }
}

/// See [`core::iter::Iterator::enumerate`]
Expand All @@ -205,14 +227,15 @@ where
}

/// See [`core::iter::Iterator::take`]
#[cfg(feature = "nightly_features")]
#[inline]
pub fn take<const TAKE: usize>(
self,
) -> IteratorFixed<impl Iterator<Item = I::Item>, { min(TAKE, N) }> {
IteratorFixed {
inner: self.inner.take(TAKE),
}
pub fn take<const TAKE: usize>(self) -> RunTypeNumToFixedIter<iter::Take<I>, RunMin<N, TAKE>>
where
Const<N>: ToUInt,
Const<TAKE>: ToUInt,

U<N>: Min<U<TAKE>, Output: TypenumToFixedIter<iter::Take<I>>>,
{
unsafe { ErasedIterFixed::new(self.inner.take(TAKE)) }
}

/// See [`core::iter::Iterator::zip`]
Expand Down Expand Up @@ -251,21 +274,22 @@ where
}
}

#[cfg(feature = "nightly_features")]
#[inline]
pub fn flatten<IIF, const M: usize>(
self,
) -> IteratorFixed<impl Iterator<Item = IIF::Item>, { M * N }>
) -> RunTypeNumToFixedIter<FlattenIter<I, IIF, M>, RunMul<N, M>>
where
Const<N>: ToUInt,
Const<M>: ToUInt,
U<N>: Mul<U<M>, Output: TypenumToFixedIter<FlattenIter<I, IIF, M>>>,

I: Iterator<Item = IIF>,
IIF: IntoIteratorFixed<M>,
{
// The call to into_iter_fixed is needed because we cannot trust that
// let x: I::Item;
// x.into_iterator() == x.into_iter_fixed().into_iterator()
IteratorFixed {
inner: self.inner.flat_map(IntoIteratorFixed::into_iter_fixed),
}
unsafe { ErasedIterFixed::new(self.inner.flat_map(IIF::into_iter_fixed as fn(_) -> _)) }
}

#[cfg(feature = "nightly_features")]
Expand Down
18 changes: 10 additions & 8 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ fn test() {
assert_eq!(res, [(1, 42), (2, 42), (3, 42)]);
}

#[cfg(feature = "nightly_features")]
#[test]
fn test_changing_length() {
let res: [_; 3] = [1, 2, 3, 4].into_iter_fixed().skip::<1>().collect();
Expand Down Expand Up @@ -59,14 +58,17 @@ fn test_changing_length() {

assert_eq!(res, [1, 2, 3, 4]);

let res: [_; 6] = [1, 2, 3].into_iter_fixed().flat_map(|x| [x, x]).collect();
#[cfg(feature = "nightly_features")]
{
let res: [_; 6] = [1, 2, 3].into_iter_fixed().flat_map(|x| [x, x]).collect();

assert_eq!(res, [1, 1, 2, 2, 3, 3]);
assert_eq!(res, [1, 1, 2, 2, 3, 3]);

let res: [_; 6] = [1, 2, 3]
.into_iter_fixed()
.flat_map(|x| IntoIteratorFixed::<2>::into_iter_fixed(core::iter::repeat(x)))
.collect();
let res: [_; 6] = [1, 2, 3]
.into_iter_fixed()
.flat_map(|x| IntoIteratorFixed::<2>::into_iter_fixed(core::iter::repeat(x)))
.collect();

assert_eq!(res, [1, 1, 2, 2, 3, 3]);
assert_eq!(res, [1, 1, 2, 2, 3, 3]);
}
}
Loading