diff --git a/.gitignore b/.gitignore index ac98a7d842..5293c63cdd 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ *.rlib *.dll +.vscode + # Executables *.exe diff --git a/src/functional.rs b/src/functional.rs new file mode 100644 index 0000000000..461210b108 --- /dev/null +++ b/src/functional.rs @@ -0,0 +1,90 @@ +//! Functional programming with generic sequences + +use core::iter::FromIterator; + +use super::ArrayLength; +use sequence::*; + +/// Defines the relationship between one generic sequence and another, +/// for operations such as `map` and `zip`. +pub unsafe trait MappedGenericSequence: GenericSequence +where + Self::Length: ArrayLength, +{ + /// Mapped sequence type + type Mapped: GenericSequence; +} + +unsafe impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a S +where + &'a S: GenericSequence, + S: GenericSequence>::Length>, + >::Length: ArrayLength, +{ + type Mapped = >::Mapped; +} + +unsafe impl<'a, T, U, S: MappedGenericSequence> MappedGenericSequence for &'a mut S +where + &'a mut S: GenericSequence, + S: GenericSequence>::Length>, + >::Length: ArrayLength, +{ + type Mapped = >::Mapped; +} + +/// Accessor type for a mapped generic sequence +pub type MappedSequence = <>::Mapped as GenericSequence>::Sequence; + +/// Defines functional programming methods for generic sequences +pub unsafe trait FunctionalSequence: GenericSequence { + /// Maps a `GenericSequence` to another `GenericSequence`. + /// + /// If the mapping function panics, any already initialized elements in the new sequence + /// will be dropped, AND any unused elements in the source sequence will also be dropped. + fn map(self, f: F) -> MappedSequence + where + Self: MappedGenericSequence, + Self::Length: ArrayLength, + F: FnMut(Self::Item) -> U, + { + FromIterator::from_iter(self.into_iter().map(f)) + } + + /// Combines two `GenericSequence` instances and iterates through both of them, + /// initializing a new `GenericSequence` with the result of the zipped mapping function. + /// + /// If the mapping function panics, any already initialized elements in the new sequence + /// will be dropped, AND any unused elements in the source sequences will also be dropped. + #[inline] + fn zip(self, rhs: Rhs, f: F) -> MappedSequence + where + Self: MappedGenericSequence, + Rhs: MappedGenericSequence>, + Self::Length: ArrayLength + ArrayLength, + Rhs: GenericSequence, + F: FnMut(Self::Item, Rhs::Item) -> U, + { + rhs.inverted_zip2(self, f) + } + + /// Folds (or reduces) a sequence of data into a single value. + /// + /// If the fold function panics, any unused elements will be dropped. + fn fold(self, init: U, f: F) -> U + where + F: FnMut(U, Self::Item) -> U, + { + self.into_iter().fold(init, f) + } +} + +unsafe impl<'a, T, S: GenericSequence> FunctionalSequence for &'a S +where + &'a S: GenericSequence, +{} + +unsafe impl<'a, T, S: GenericSequence> FunctionalSequence for &'a mut S +where + &'a mut S: GenericSequence, +{} \ No newline at end of file diff --git a/src/impls.rs b/src/impls.rs index 8aa0b4675b..c439f591ee 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -1,9 +1,12 @@ -use super::{ArrayLength, GenericArray}; use core::borrow::{Borrow, BorrowMut}; use core::cmp::Ordering; use core::fmt::{self, Debug}; use core::hash::{Hash, Hasher}; +use super::{ArrayLength, GenericArray}; +use sequence::*; +use functional::*; + impl Default for GenericArray where N: ArrayLength, @@ -19,7 +22,7 @@ where N: ArrayLength, { fn clone(&self) -> GenericArray { - self.map_ref(|x| x.clone()) + self.map(|x| x.clone()) } } diff --git a/src/iter.rs b/src/iter.rs index b9282761a4..9ea49796b1 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -14,6 +14,9 @@ pub struct GenericArrayIter> { index_back: usize, } +unsafe impl> Send for GenericArrayIter {} +unsafe impl> Sync for GenericArrayIter {} + impl IntoIterator for GenericArray where N: ArrayLength, @@ -34,6 +37,7 @@ impl Drop for GenericArrayIter where N: ArrayLength, { + #[inline] fn drop(&mut self) { // Drop values that are still alive. for p in &mut self.array[self.index..self.index_back] { @@ -50,23 +54,28 @@ where { type Item = T; + #[inline] fn next(&mut self) -> Option { - if self.len() > 0 { - unsafe { - let p = self.array.get_unchecked(self.index); - self.index += 1; - Some(ptr::read(p)) - } + if self.index < self.index_back { + let p = unsafe { + Some(ptr::read(self.array.get_unchecked(self.index))) + }; + + self.index += 1; + + p } else { None } } + #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.len(); (len, Some(len)) } + #[inline] fn count(self) -> usize { self.len() } @@ -95,11 +104,11 @@ where N: ArrayLength, { fn next_back(&mut self) -> Option { - if self.len() > 0 { + if self.index < self.index_back { self.index_back -= 1; + unsafe { - let p = self.array.get_unchecked(self.index_back); - Some(ptr::read(p)) + Some(ptr::read(self.array.get_unchecked(self.index_back))) } } else { None @@ -114,4 +123,4 @@ where fn len(&self) -> usize { self.index_back - self.index } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f340885c99..cdefff4faf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,7 @@ use core::{mem, ptr, slice}; use core::marker::PhantomData; use core::mem::ManuallyDrop; use core::ops::{Deref, DerefMut}; +use core::iter::FromIterator; use typenum::bit::{B0, B1}; use typenum::uint::{UInt, UTerm, Unsigned}; @@ -60,6 +61,10 @@ use typenum::uint::{UInt, UTerm, Unsigned}; pub mod arr; pub mod iter; pub mod sequence; +pub mod functional; + +use sequence::*; +use functional::*; pub use iter::GenericArrayIter; @@ -134,6 +139,9 @@ pub struct GenericArray> { data: U::ArrayType, } +unsafe impl> Send for GenericArray {} +unsafe impl> Sync for GenericArray {} + impl Deref for GenericArray where N: ArrayLength, @@ -210,121 +218,200 @@ impl> Drop for ArrayConsumer { } } -impl GenericArray +impl<'a, T: 'a, N> IntoIterator for &'a GenericArray +where + N: ArrayLength +{ + type IntoIter = slice::Iter<'a, T>; + type Item = &'a T; + + fn into_iter(self: &'a GenericArray) -> Self::IntoIter { + self.as_slice().iter() + } +} + +impl<'a, T: 'a, N> IntoIterator for &'a mut GenericArray +where + N: ArrayLength +{ + type IntoIter = slice::IterMut<'a, T>; + type Item = &'a mut T; + + fn into_iter(self: &'a mut GenericArray) -> Self::IntoIter { + self.as_mut_slice().iter_mut() + } +} + +impl FromIterator for GenericArray where N: ArrayLength, { - /// Initializes a new `GenericArray` instance using the given function. - /// - /// If the generator function panics while initializing the array, - /// any already initialized elements will be dropped. - pub fn generate(f: F) -> GenericArray + fn from_iter(iter: I) -> GenericArray where - F: Fn(usize) -> T, + I: IntoIterator, { let mut destination = ArrayBuilder::new(); - for (i, dst) in destination.array.iter_mut().enumerate() { + for (src, dst) in iter.into_iter().zip(destination.array.iter_mut()) { unsafe { - ptr::write(dst, f(i)); + ptr::write(dst, src); } destination.position += 1; } + if destination.position < N::to_usize() { + from_iter_length_fail(destination.position, N::to_usize()); + } + destination.into_inner() } +} - /// Map a function over a slice to a `GenericArray`. - /// - /// The length of the slice *must* be equal to the length of the array. - #[inline] - pub fn map_slice T>(s: &[S], f: F) -> GenericArray { - assert_eq!(s.len(), N::to_usize()); +#[inline(never)] +#[cold] +fn from_iter_length_fail(length: usize, expected: usize) -> ! { + panic!("GenericArray::from_iter received {} elements but expected {}", length, expected); +} - Self::generate(|i| f(unsafe { s.get_unchecked(i) })) - } +unsafe impl GenericSequence for GenericArray +where + N: ArrayLength, + Self: IntoIterator, +{ + type Length = N; + type Sequence = Self; - /// Maps a `GenericArray` to another `GenericArray`. - /// - /// If the mapping function panics, any already initialized elements in the new array - /// will be dropped, AND any unused elements in the source array will also be dropped. - pub fn map(self, f: F) -> GenericArray + fn generate(mut f: F) -> GenericArray where - F: Fn(T) -> U, - N: ArrayLength, + F: FnMut(usize) -> T, { - let mut source = ArrayConsumer::new(self); let mut destination = ArrayBuilder::new(); - for (dst, src) in destination.array.iter_mut().zip(source.array.iter()) { + for (i, dst) in destination.array.iter_mut().enumerate() { unsafe { - ptr::write(dst, f(ptr::read(src))); + ptr::write(dst, f(i)); } - source.position += 1; destination.position += 1; } destination.into_inner() } - /// Maps a `GenericArray` to another `GenericArray` by reference. - /// - /// If the mapping function panics, any already initialized elements will be dropped. - #[inline] - pub fn map_ref(&self, f: F) -> GenericArray + fn inverted_zip(self, lhs: GenericArray, mut f: F) -> MappedSequence, B, U> where - F: Fn(&T) -> U, - N: ArrayLength, + GenericArray: + GenericSequence + + MappedGenericSequence, + Self: MappedGenericSequence, + Self::Length: ArrayLength + ArrayLength, + F: FnMut(B, Self::Item) -> U { - GenericArray::generate(|i| f(unsafe { self.get_unchecked(i) })) + let mut left = ArrayConsumer::new(lhs); + let mut right = ArrayConsumer::new(self); + + let ArrayConsumer { array: ref left_array, position: ref mut left_position } = left; + let ArrayConsumer { array: ref right_array, position: ref mut right_position } = right; + + FromIterator::from_iter(left_array.iter().zip(right_array.iter()).map(|(l, r)| { + let left_value = unsafe { ptr::read(l) }; + let right_value = unsafe { ptr::read(r) }; + + *left_position += 1; + *right_position += 1; + + f(left_value, right_value) + })) } - /// Combines two `GenericArray` instances and iterates through both of them, - /// initializing a new `GenericArray` with the result of the zipped mapping function. - /// - /// If the mapping function panics, any already initialized elements in the new array - /// will be dropped, AND any unused elements in the source arrays will also be dropped. - pub fn zip(self, rhs: GenericArray, f: F) -> GenericArray + fn inverted_zip2(self, lhs: Lhs, mut f: F) -> MappedSequence where - F: Fn(T, B) -> U, - N: ArrayLength + ArrayLength, + Lhs: GenericSequence + MappedGenericSequence, + Self: MappedGenericSequence, + Self::Length: ArrayLength + ArrayLength, + F: FnMut(Lhs::Item, Self::Item) -> U { - let mut left = ArrayConsumer::new(self); - let mut right = ArrayConsumer::new(rhs); + let mut right = ArrayConsumer::new(self); - let mut destination = ArrayBuilder::new(); + let ArrayConsumer { array: ref right_array, position: ref mut right_position } = right; - for (dst, (lhs, rhs)) in destination - .array - .iter_mut() - .zip(left.array.iter().zip(right.array.iter())) - { - unsafe { - ptr::write(dst, f(ptr::read(lhs), ptr::read(rhs))); - } + FromIterator::from_iter(lhs.into_iter().zip(right_array.iter()).map(|(left_value, r)| { + let right_value = unsafe { ptr::read(r) }; - destination.position += 1; - left.position += 1; - right.position += 1; - } + *right_position += 1; - destination.into_inner() + f(left_value, right_value) + })) } +} - /// Combines two `GenericArray` instances and iterates through both of them by reference, - /// initializing a new `GenericArray` with the result of the zipped mapping function. - /// - /// If the mapping function panics, any already initialized elements will be dropped. - pub fn zip_ref(&self, rhs: &GenericArray, f: F) -> GenericArray +unsafe impl MappedGenericSequence for GenericArray +where + N: ArrayLength + ArrayLength, + GenericArray: GenericSequence, +{ + type Mapped = GenericArray; +} + +unsafe impl FunctionalSequence for GenericArray +where + N: ArrayLength, + Self: GenericSequence +{ + fn map(self, mut f: F) -> MappedSequence where - F: Fn(&T, &B) -> U, - N: ArrayLength + ArrayLength, + Self::Length: ArrayLength, + Self: MappedGenericSequence, + F: FnMut(T) -> U, { - GenericArray::generate(|i| unsafe { f(self.get_unchecked(i), rhs.get_unchecked(i)) }) + let mut source = ArrayConsumer::new(self); + + let ArrayConsumer { ref array, ref mut position } = source; + + FromIterator::from_iter(array.iter().map(|src| { + let value = unsafe { ptr::read(src) }; + + *position += 1; + + f(value) + })) + } + + #[inline] + fn zip(self, rhs: Rhs, f: F) -> MappedSequence + where + Self: MappedGenericSequence, + Rhs: MappedGenericSequence>, + Self::Length: ArrayLength + ArrayLength, + Rhs: GenericSequence, + F: FnMut(T, Rhs::Item) -> U, + { + rhs.inverted_zip(self, f) + } + + fn fold(self, init: U, mut f: F) -> U + where + F: FnMut(U, T) -> U + { + let mut source = ArrayConsumer::new(self); + + let ArrayConsumer { ref array, ref mut position } = source; + + array.iter().fold(init, |acc, src| { + let value = unsafe { ptr::read(src) }; + + *position += 1; + + f(acc, value) + }) } +} +impl GenericArray +where + N: ArrayLength, +{ /// Extracts a slice containing the entire array. #[inline] pub fn as_slice(&self) -> &[T] { @@ -342,16 +429,36 @@ where /// Length of the slice must be equal to the length of the array. #[inline] pub fn from_slice(slice: &[T]) -> &GenericArray { + slice.into() + } + + /// Converts mutable slice to a mutable generic array reference + /// + /// Length of the slice must be equal to the length of the array. + #[inline] + pub fn from_mut_slice(slice: &mut [T]) -> &mut GenericArray { + slice.into() + } +} + +impl<'a, T, N: ArrayLength> From<&'a [T]> for &'a GenericArray { + /// Converts slice to a generic array reference with inferred length; + /// + /// Length of the slice must be equal to the length of the array. + #[inline] + fn from(slice: &[T]) -> &GenericArray { assert_eq!(slice.len(), N::to_usize()); unsafe { &*(slice.as_ptr() as *const GenericArray) } } +} +impl<'a, T, N: ArrayLength> From<&'a mut [T]> for &'a mut GenericArray { /// Converts mutable slice to a mutable generic array reference /// /// Length of the slice must be equal to the length of the array. #[inline] - pub fn from_mut_slice(slice: &mut [T]) -> &mut GenericArray { + fn from(slice: &mut [T]) -> &mut GenericArray { assert_eq!(slice.len(), N::to_usize()); unsafe { &mut *(slice.as_mut_ptr() as *mut GenericArray) } @@ -397,44 +504,13 @@ where destination.position += 1; } - let array = unsafe { ptr::read(&destination.array) }; - - mem::forget(destination); - - Some(ManuallyDrop::into_inner(array)) + Some(destination.into_inner()) } else { None } } } -impl ::core::iter::FromIterator for GenericArray -where - N: ArrayLength, - T: Default, -{ - fn from_iter(iter: I) -> GenericArray - where - I: IntoIterator, - { - let mut destination = ArrayBuilder::new(); - - let defaults = ::core::iter::repeat(()).map(|_| T::default()); - - for (dst, src) in destination - .array - .iter_mut() - .zip(iter.into_iter().chain(defaults)) - { - unsafe { - ptr::write(dst, src); - } - } - - destination.into_inner() - } -} - /// A reimplementation of the `transmute` function, avoiding problems /// when the compiler can't prove equal sizes. #[inline] @@ -464,11 +540,17 @@ mod test { #[test] fn test_assembly() { + use functional::*; + let a = black_box(arr![i32; 1, 3, 5, 7]); let b = black_box(arr![i32; 2, 4, 6, 8]); - let c = a.zip_ref(&b, |l, r| l + r); + let c = (&a).zip(b, |l, r| l + r); + + let d = a.fold(0, |a, x| a + x); assert_eq!(c, arr![i32; 3, 7, 11, 15]); + + assert_eq!(d, 16); } } diff --git a/src/sequence.rs b/src/sequence.rs index 5624bea44e..a1775ce31f 100644 --- a/src/sequence.rs +++ b/src/sequence.rs @@ -5,23 +5,94 @@ use core::{mem, ptr}; use core::ops::{Add, Sub}; use typenum::operator_aliases::*; -/// Defines some `GenericArray` sequence with an associated length. +/// Defines some sequence with an associated length and iteration capabilities. /// /// This is useful for passing N-length generic arrays as generics. -pub unsafe trait GenericSequence: Sized { +pub unsafe trait GenericSequence: Sized + IntoIterator { /// `GenericArray` associated length type Length: ArrayLength; + + /// Concrete sequence type used in conjuction with reference implementations of `GenericSequence` + type Sequence: GenericSequence + FromIterator; + + /// Initializes a new sequence instance using the given function. + /// + /// If the generator function panics while initializing the sequence, + /// any already initialized elements will be dropped. + fn generate(f: F) -> Self::Sequence + where F: FnMut(usize) -> T; + + #[doc(hidden)] + fn inverted_zip(self, lhs: GenericArray, mut f: F) -> MappedSequence, B, U> + where + GenericArray: + GenericSequence + + MappedGenericSequence, + Self: MappedGenericSequence, + Self::Length: ArrayLength + ArrayLength, + F: FnMut(B, Self::Item) -> U + { + + let mut left = ArrayConsumer::new(lhs); + + let ArrayConsumer { array: ref left_array, position: ref mut left_position } = left; + + FromIterator::from_iter(left_array.iter().zip(self.into_iter()).map(|(l, right_value)| { + let left_value = unsafe { ptr::read(l) }; + + *left_position += 1; + + f(left_value, right_value) + })) + } + + #[doc(hidden)] + fn inverted_zip2(self, lhs: Lhs, mut f: F) -> MappedSequence + where + Lhs: GenericSequence + MappedGenericSequence, + Self: MappedGenericSequence, + Self::Length: ArrayLength + ArrayLength, + F: FnMut(Lhs::Item, Self::Item) -> U + { + FromIterator::from_iter(lhs.into_iter().zip(self.into_iter()).map(|(l, r)| f(l, r) )) + } +} + +unsafe impl<'a, T: 'a, S: GenericSequence> GenericSequence for &'a S +where + &'a S: IntoIterator +{ + type Length = S::Length; + type Sequence = S::Sequence; + + #[inline] + fn generate(f: F) -> Self::Sequence + where F: FnMut(usize) -> T + { + S::generate(f) + } } -unsafe impl> GenericSequence for GenericArray { - type Length = N; +unsafe impl<'a, T: 'a, S: GenericSequence> GenericSequence for &'a mut S +where + &'a mut S: IntoIterator +{ + type Length = S::Length; + type Sequence = S::Sequence; + + #[inline] + fn generate(f: F) -> Self::Sequence + where F: FnMut(usize) -> T + { + S::generate(f) + } } /// Defines any `GenericSequence` which can be lengthened or extended by appending /// or prepending an element to it. /// /// Any lengthened sequence can be shortened back to the original using `pop_front` or `pop_back` -pub unsafe trait Lengthen: GenericSequence { +pub unsafe trait Lengthen: Sized + GenericSequence { /// `GenericSequence` that has one more element than `Self` type Longer: Shorten; @@ -56,7 +127,7 @@ pub unsafe trait Lengthen: GenericSequence { /// /// Additionally, any shortened sequence can be lengthened by /// appending or prepending an element to it. -pub unsafe trait Shorten: GenericSequence { +pub unsafe trait Shorten: Sized + GenericSequence { /// `GenericSequence` that has one less element than `Self` type Shorter: Lengthen; diff --git a/tests/mod.rs b/tests/mod.rs index 10cb4f5b6f..b637fcaf99 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -3,9 +3,10 @@ #[macro_use] extern crate generic_array; use core::cell::Cell; -use core::ops::Drop; +use core::ops::{Add, Drop}; use generic_array::GenericArray; use generic_array::sequence::*; +use generic_array::functional::*; use generic_array::typenum::{U1, U3, U4, U97}; #[test] @@ -155,13 +156,15 @@ fn test_zip() { let a: GenericArray<_, U4> = GenericArray::generate(|i| i + 1); let b: GenericArray<_, U4> = GenericArray::generate(|i| i as i32 * 4); - let c = a.zip(b, |r, l| r as i32 + l); + // Uses reference and non-reference arguments + let c = (&a).zip(b, |r, l| *r as i32 + l); assert_eq!(c, arr![i32; 1, 6, 11, 16]); } #[test] -fn test_from_iter() { +#[should_panic] +fn test_from_iter_short() { use core::iter::repeat; let a: GenericArray<_, U4> = repeat(11).take(3).collect(); @@ -169,15 +172,23 @@ fn test_from_iter() { assert_eq!(a, arr![i32; 11, 11, 11, 0]); } +#[test] +fn test_from_iter() { + use core::iter::{repeat, once}; + + let a: GenericArray<_, U4> = repeat(11).take(3).chain(once(0)).collect(); + + assert_eq!(a, arr![i32; 11, 11, 11, 0]); +} + #[test] fn test_sizes() { #![allow(dead_code)] use core::mem::{size_of, size_of_val}; - #[derive(Debug)] + #[derive(Debug, Copy, Clone)] #[repr(C)] #[repr(packed)] - #[derive(Default)] struct Test { t: u16, s: u32, @@ -256,4 +267,27 @@ fn test_concat() { assert_eq!(d, arr![i32; 1]); assert_eq!(e, arr![i32; 2, 3, 4]); +} + +#[test] +fn test_fold() { + let a = arr![i32; 1, 2, 3, 4]; + + assert_eq!(10, a.fold(0, |a, x| a + x)); +} + +fn sum_generic(s: S) -> i32 +where + S: FunctionalSequence, + S::Item: Add, // `+` + i32: Add, // reflexive +{ + s.fold(0, |a, x| a + x) +} + +#[test] +fn test_sum() { + let a = sum_generic(arr![i32; 1, 2, 3, 4]); + + assert_eq!(a, 10); } \ No newline at end of file diff --git a/tests/std.rs b/tests/std.rs new file mode 100644 index 0000000000..68d6837e2d --- /dev/null +++ b/tests/std.rs @@ -0,0 +1,22 @@ +#![recursion_limit="128"] + +extern crate generic_array; + +use std::fmt::Debug; +use std::ops::Add; + +//use generic_array::GenericArray; +use generic_array::sequence::*; +use generic_array::functional::*; + +pub fn test_generic(s: S) +where + S: FunctionalSequence, // `.map` + S::Item: Add, // `+` + S: MappedGenericSequence, // `i32` -> `i32` + MappedSequence: Debug // println! +{ + let a = s.map(|x| x + 1); + + println!("{:?}", a); +} \ No newline at end of file