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.
189 lines
4.9 KiB
189 lines
4.9 KiB
use std::cmp;
|
|
|
|
// Note: There are different ways to implement ZipSlices.
|
|
// This version performed the best in benchmarks.
|
|
//
|
|
// I also implemented a version with three pointes (tptr, tend, uptr),
|
|
// that mimiced slice::Iter and only checked bounds by using tptr == tend,
|
|
// but that was inferior to this solution.
|
|
|
|
/// An iterator which iterates two slices simultaneously.
|
|
///
|
|
/// `ZipSlices` acts like a double-ended `.zip()` iterator.
|
|
///
|
|
/// It was intended to be more efficient than `.zip()`, and it was, then
|
|
/// rustc changed how it optimizes so it can not promise improved performance
|
|
/// at this time.
|
|
///
|
|
/// Note that elements past the end of the shortest of the two slices are ignored.
|
|
///
|
|
/// Iterator element type for `ZipSlices<T, U>` is `(T::Item, U::Item)`. For example,
|
|
/// for a `ZipSlices<&'a [A], &'b mut [B]>`, the element type is `(&'a A, &'b mut B)`.
|
|
#[derive(Clone)]
|
|
pub struct ZipSlices<T, U> {
|
|
t: T,
|
|
u: U,
|
|
len: usize,
|
|
index: usize,
|
|
}
|
|
|
|
impl<'a, 'b, A, B> ZipSlices<&'a [A], &'b [B]> {
|
|
/// Create a new `ZipSlices` from slices `a` and `b`.
|
|
///
|
|
/// Act like a double-ended `.zip()` iterator, but more efficiently.
|
|
///
|
|
/// Note that elements past the end of the shortest of the two slices are ignored.
|
|
#[inline(always)]
|
|
pub fn new(a: &'a [A], b: &'b [B]) -> Self {
|
|
let minl = cmp::min(a.len(), b.len());
|
|
ZipSlices {
|
|
t: a,
|
|
u: b,
|
|
len: minl,
|
|
index: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, U> ZipSlices<T, U>
|
|
where T: Slice,
|
|
U: Slice
|
|
{
|
|
/// Create a new `ZipSlices` from slices `a` and `b`.
|
|
///
|
|
/// Act like a double-ended `.zip()` iterator, but more efficiently.
|
|
///
|
|
/// Note that elements past the end of the shortest of the two slices are ignored.
|
|
#[inline(always)]
|
|
pub fn from_slices(a: T, b: U) -> Self {
|
|
let minl = cmp::min(a.len(), b.len());
|
|
ZipSlices {
|
|
t: a,
|
|
u: b,
|
|
len: minl,
|
|
index: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, U> Iterator for ZipSlices<T, U>
|
|
where T: Slice,
|
|
U: Slice
|
|
{
|
|
type Item = (T::Item, U::Item);
|
|
|
|
#[inline(always)]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
unsafe {
|
|
if self.index >= self.len {
|
|
None
|
|
} else {
|
|
let i = self.index;
|
|
self.index += 1;
|
|
Some((
|
|
self.t.get_unchecked(i),
|
|
self.u.get_unchecked(i)))
|
|
}
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
let len = self.len - self.index;
|
|
(len, Some(len))
|
|
}
|
|
}
|
|
|
|
impl<T, U> DoubleEndedIterator for ZipSlices<T, U>
|
|
where T: Slice,
|
|
U: Slice
|
|
{
|
|
#[inline(always)]
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
unsafe {
|
|
if self.index >= self.len {
|
|
None
|
|
} else {
|
|
self.len -= 1;
|
|
let i = self.len;
|
|
Some((
|
|
self.t.get_unchecked(i),
|
|
self.u.get_unchecked(i)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, U> ExactSizeIterator for ZipSlices<T, U>
|
|
where T: Slice,
|
|
U: Slice
|
|
{}
|
|
|
|
unsafe impl<T, U> Slice for ZipSlices<T, U>
|
|
where T: Slice,
|
|
U: Slice
|
|
{
|
|
type Item = (T::Item, U::Item);
|
|
|
|
fn len(&self) -> usize {
|
|
self.len - self.index
|
|
}
|
|
|
|
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item {
|
|
(self.t.get_unchecked(i),
|
|
self.u.get_unchecked(i))
|
|
}
|
|
}
|
|
|
|
/// A helper trait to let `ZipSlices` accept both `&[T]` and `&mut [T]`.
|
|
///
|
|
/// Unsafe trait because:
|
|
///
|
|
/// - Implementors must guarantee that `get_unchecked` is valid for all indices `0..len()`.
|
|
pub unsafe trait Slice {
|
|
/// The type of a reference to the slice's elements
|
|
type Item;
|
|
#[doc(hidden)]
|
|
fn len(&self) -> usize;
|
|
#[doc(hidden)]
|
|
unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item;
|
|
}
|
|
|
|
unsafe impl<'a, T> Slice for &'a [T] {
|
|
type Item = &'a T;
|
|
#[inline(always)]
|
|
fn len(&self) -> usize { (**self).len() }
|
|
#[inline(always)]
|
|
unsafe fn get_unchecked(&mut self, i: usize) -> &'a T {
|
|
debug_assert!(i < self.len());
|
|
(**self).get_unchecked(i)
|
|
}
|
|
}
|
|
|
|
unsafe impl<'a, T> Slice for &'a mut [T] {
|
|
type Item = &'a mut T;
|
|
#[inline(always)]
|
|
fn len(&self) -> usize { (**self).len() }
|
|
#[inline(always)]
|
|
unsafe fn get_unchecked(&mut self, i: usize) -> &'a mut T {
|
|
debug_assert!(i < self.len());
|
|
// override the lifetime constraints of &mut &'a mut [T]
|
|
(*(*self as *mut [T])).get_unchecked_mut(i)
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn zipslices() {
|
|
|
|
let xs = [1, 2, 3, 4, 5, 6];
|
|
let ys = [1, 2, 3, 7];
|
|
::itertools::assert_equal(ZipSlices::new(&xs, &ys), xs.iter().zip(&ys));
|
|
|
|
let xs = [1, 2, 3, 4, 5, 6];
|
|
let mut ys = [0; 6];
|
|
for (x, y) in ZipSlices::from_slices(&xs[..], &mut ys[..]) {
|
|
*y = *x;
|
|
}
|
|
::itertools::assert_equal(&xs, &ys);
|
|
}
|