wyz/
bidi.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
//! A bidirectional iterator that only checks its direction once.

use core::iter::FusedIterator;

/** An iterator that conditionally reverses itself upon creation.

This acts as a conditional `.rev()` adapter: it reverses the direction of
iteration, swapping `.next()` and `.next_back()`, but only if the provided
condition is true. If the condition is false, then iteration proceeds normally.

The condition is only evaluated when the adapter is constructed, and all calls
to drive the iterator are branchless.

## Usage

This can be constructed directly with `Bidi::new(some_iterator)`, but it is more
conveniently accessed as an extension method on double-ended iterators. Import
`wyz::BidiIterator` or `wyz::bidi::*;` and then call `.bidi()` in your iterator
adapter sequence.

## Examples

This can be used to hand-roll a `memmove` implementation that correctly handles
the case where the destination begins in the source region:

```rust
use wyz::bidi::*;

unsafe fn memmove<T>(from: *const T, to: *mut T, count: usize) {
 let src = from .. from.add(count);
 let rev = src.contains(&(to as *const T));
 for idx in (0 .. count).bidi(rev) {
  to.add(idx).write(from.add(idx).read());
 }
}
```

This example detects if `to` is between `from` and `from.add(count)` and uses
that to determine whether to iterate forward from `0` to `count - 1` or backward
from `count - 1` to `0`.
**/
pub struct Bidi<I>
where I: DoubleEndedIterator
{
	/// The iterator being governed.
	inner: I,
	/// A pointer to either `I::next` or `I::next_back`.
	next: fn(&mut I) -> Option<<I as Iterator>::Item>,
	/// A pointer to either `I::next_back` or `I::next`.
	next_back: fn(&mut I) -> Option<<I as Iterator>::Item>,
	/// A pointer to either `I::nth` or `I::nth_back`.
	nth: fn(&mut I, usize) -> Option<<I as Iterator>::Item>,
	/// A pointer to either `I::nth_back` or `I::nth`.
	nth_back: fn(&mut I, usize) -> Option<<I as Iterator>::Item>,
}

impl<I> Bidi<I>
where I: DoubleEndedIterator
{
	/// Applies the `Bidi` adapter to a double-ended iterator and selects the
	/// direction of traversal.
	///
	/// ## Parameters
	///
	/// - `iter`: anything that can be made into a double-ended iterator
	/// - `cond`: determines whether iteration proceeds ordinarily or reversed
	pub fn new<II>(iter: II, cond: bool) -> Self
	where II: IntoIterator<IntoIter = I> {
		let inner = iter.into_iter();
		if cond {
			Self {
				inner,
				next: <I as DoubleEndedIterator>::next_back,
				next_back: <I as Iterator>::next,
				nth: <I as DoubleEndedIterator>::nth_back,
				nth_back: <I as Iterator>::nth,
			}
		}
		else {
			Self {
				inner,
				next: <I as Iterator>::next,
				next_back: <I as DoubleEndedIterator>::next_back,
				nth: <I as Iterator>::nth,
				nth_back: <I as DoubleEndedIterator>::nth_back,
			}
		}
	}
}

impl<I> Iterator for Bidi<I>
where I: DoubleEndedIterator
{
	type Item = <I as Iterator>::Item;

	#[inline]
	fn next(&mut self) -> Option<Self::Item> {
		(&mut self.next)(&mut self.inner)
	}

	#[inline]
	fn nth(&mut self, n: usize) -> Option<Self::Item> {
		(&mut self.nth)(&mut self.inner, n)
	}

	#[inline]
	#[cfg(not(tarpaulin_include))]
	fn size_hint(&self) -> (usize, Option<usize>) {
		self.inner.size_hint()
	}

	#[inline]
	#[cfg(not(tarpaulin_include))]
	fn count(self) -> usize {
		self.inner.count()
	}

	#[inline]
	#[cfg(not(tarpaulin_include))]
	fn last(mut self) -> Option<Self::Item> {
		self.next_back()
	}
}

impl<I> DoubleEndedIterator for Bidi<I>
where I: DoubleEndedIterator
{
	#[inline]
	fn next_back(&mut self) -> Option<Self::Item> {
		(&mut self.next_back)(&mut self.inner)
	}

	#[inline]
	fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
		(&mut self.nth_back)(&mut self.inner, n)
	}
}

impl<I> ExactSizeIterator for Bidi<I>
where I: DoubleEndedIterator + ExactSizeIterator
{
	#[inline]
	#[cfg(not(tarpaulin_include))]
	fn len(&self) -> usize {
		self.inner.len()
	}
}

impl<I> FusedIterator for Bidi<I> where I: DoubleEndedIterator + FusedIterator
{
}

/// Extension trait that provides `.bidi()` for all double-ended iterators.
pub trait BidiIterator
where
	Self: Sized + IntoIterator,
	<Self as IntoIterator>::IntoIter: DoubleEndedIterator,
{
	/// Conditionally reverses the direction of iteration.
	///
	/// When `cond` is true, this adapter swaps the `next` and `nth` methods
	/// with `next_back` and `nth_back`. The resulting iterator is equivalent to
	/// `if cond { self.rev() } else { self }`.
	///
	/// ## Examples
	///
	/// ```rust
	/// use wyz::BidiIterator;
	///
	/// let data = [1, 2, 3];
	/// let mut iter = data.iter().copied().bidi(false);
	/// assert_eq!(iter.next(), Some(1));
	/// assert_eq!(iter.next_back(), Some(3));
	///
	/// let mut iter = data.iter().copied().bidi(true);
	/// assert_eq!(iter.next(), Some(3));
	/// assert_eq!(iter.next_back(), Some(1));
	/// ```
	fn bidi(self, cond: bool) -> Bidi<Self::IntoIter> {
		Bidi::new(self, cond)
	}
}

impl<I> BidiIterator for I
where
	I: Sized + IntoIterator,
	<I as IntoIterator>::IntoIter: DoubleEndedIterator,
{
}

#[cfg(test)]
mod tests {
	use super::*;

	#[test]
	fn forward() {
		let mut iter = (0 .. 6).bidi(false);

		assert_eq!(iter.next(), Some(0));
		assert_eq!(iter.next_back(), Some(5));
		assert_eq!(iter.nth(1), Some(2));
		assert_eq!(iter.nth_back(1), Some(3));
		assert!(iter.next().is_none());
	}

	#[test]
	fn reverse() {
		let mut iter = (0 .. 6).bidi(true);

		assert_eq!(iter.next(), Some(5));
		assert_eq!(iter.next_back(), Some(0));
		assert_eq!(iter.nth(1), Some(3));
		assert_eq!(iter.nth_back(1), Some(2));
		assert!(iter.next().is_none());
	}
}