bitvec/slice/specialization/
lsb0.rs

1//! Specializations for `BitSlice<_, Lsb0>.
2
3use core::iter;
4
5use funty::Integral;
6use wyz::{
7	bidi::BidiIterator,
8	range::RangeExt,
9};
10
11use super::{
12	has_one,
13	has_zero,
14	WORD_BITS,
15};
16use crate::{
17	domain::Domain,
18	field::BitField,
19	mem::bits_of,
20	order::Lsb0,
21	slice::BitSlice,
22	store::BitStore,
23};
24
25impl<T> BitSlice<T, Lsb0>
26where T: BitStore
27{
28	/// Accelerates Boolean arithmetic.
29	///
30	/// This applies a Boolean-arithmetic function across all the bits in a
31	/// pair. The secondary bit-slice is zero-extended if it expires before
32	/// `self` does.
33	///
34	/// Because the two bit-slices share the same types, this is able to
35	/// batch-load `usize` chunks from each, apply the arithmetic to them, and
36	/// write the result back into `self`. Any leftover bits are handled
37	/// individually.
38	pub(crate) fn sp_bitop_assign(
39		&mut self,
40		rhs: &Self,
41		word_op: fn(usize, usize) -> usize,
42		bool_op: fn(bool, bool) -> bool,
43	) {
44		let (mut this, mut that) = (self, rhs);
45		while this.len() >= WORD_BITS && that.len() >= WORD_BITS {
46			unsafe {
47				let (l, left) = this.split_at_unchecked_mut_noalias(WORD_BITS);
48				let (r, right) = that.split_at_unchecked(WORD_BITS);
49				this = left;
50				that = right;
51				let (a, b) = (l.load_le::<usize>(), r.load_le::<usize>());
52				l.store_le(word_op(a, b));
53			}
54		}
55		//  Note: it might actually be possible to do a partial-word load/store
56		//  to exhaust the shorter bit-slice. Investigate further.
57		for (l, r) in this
58			.as_mut_bitptr_range()
59			.zip(that.iter().by_vals().chain(iter::repeat(false)))
60		{
61			unsafe {
62				l.write(bool_op(l.read(), r));
63			}
64		}
65	}
66
67	/// Accelerates copies between disjoint bit-slices with batch loads.
68	pub(crate) fn sp_copy_from_bitslice(&mut self, src: &Self) {
69		assert_eq!(
70			self.len(),
71			src.len(),
72			"copying between bit-slices requires equal lengths",
73		);
74
75		for (to, from) in unsafe { self.chunks_mut(WORD_BITS).remove_alias() }
76			.zip(src.chunks(WORD_BITS))
77		{
78			to.store_le::<usize>(from.load_le::<usize>());
79		}
80	}
81
82	/// Accelerates possibly-overlapping copies within a single bit-slice with
83	/// batch loads.
84	pub(crate) unsafe fn sp_copy_within_unchecked(
85		&mut self,
86		src: impl RangeExt<usize>,
87		dest: usize,
88	) {
89		let source = src.normalize(None, self.len());
90		let rev = source.contains(&dest);
91		let dest = dest .. dest + source.len();
92
93		let this = self.as_accessor();
94		let from = this
95			.get_unchecked(source)
96			.chunks(WORD_BITS)
97			.map(|bits| bits as *const BitSlice<T::Access, Lsb0>);
98		let to = this.get_unchecked(dest).chunks(WORD_BITS).map(|bits| {
99			bits as *const BitSlice<T::Access, Lsb0>
100				as *mut BitSlice<T::Access, Lsb0>
101		});
102		for (from, to) in from.zip(to).bidi(rev) {
103			let value = (*from).load_le::<usize>();
104			(*to).store_le::<usize>(value);
105		}
106	}
107
108	/// Accelerates equality checking with batch loads.
109	pub(crate) fn sp_eq(&self, other: &Self) -> bool {
110		self.len() == other.len()
111			&& self
112				.chunks(WORD_BITS)
113				.zip(other.chunks(WORD_BITS))
114				.all(|(a, b)| a.load_le::<usize>() == b.load_le::<usize>())
115	}
116
117	/// Seeks the index of the first `1` bit in the bit-slice.
118	pub(crate) fn sp_first_one(&self) -> Option<usize> {
119		let mut accum = 0;
120
121		match self.domain() {
122			Domain::Enclave(elem) => {
123				let val = elem.load_value();
124				if has_one(val, elem.mask().into_inner()) {
125					accum += val.trailing_zeros() as usize
126						- elem.head().into_inner() as usize;
127					return Some(accum);
128				}
129				None
130			},
131			Domain::Region { head, body, tail } => {
132				if let Some(elem) = head {
133					let val = elem.load_value();
134					accum += val.trailing_zeros() as usize
135						- elem.head().into_inner() as usize;
136					if has_one(val, elem.mask().into_inner()) {
137						return Some(accum);
138					}
139				}
140
141				for val in body.iter().map(BitStore::load_value) {
142					accum += val.trailing_zeros() as usize;
143					if has_one(val, !<T::Mem as Integral>::ZERO) {
144						return Some(accum);
145					}
146				}
147
148				if let Some(elem) = tail {
149					let val = elem.load_value();
150					if has_one(val, elem.mask().into_inner()) {
151						accum += val.trailing_zeros() as usize;
152						return Some(accum);
153					}
154				}
155
156				None
157			},
158		}
159	}
160
161	/// Seeks the index of the last `1` bit in the bit-slice.
162	pub(crate) fn sp_last_one(&self) -> Option<usize> {
163		let mut out = self.len();
164
165		match self.domain() {
166			Domain::Enclave(elem) => {
167				let val = elem.load_value();
168				let dead_bits =
169					bits_of::<T::Mem>() - elem.tail().into_inner() as usize;
170				if has_one(val, elem.mask().into_inner()) {
171					out -= val.leading_zeros() as usize - dead_bits as usize;
172					return Some(out - 1);
173				}
174				None
175			},
176			Domain::Region { head, body, tail } => {
177				if let Some(elem) = tail {
178					let val = elem.load_value();
179					let dead_bits =
180						bits_of::<T::Mem>() - elem.tail().into_inner() as usize;
181					out -= val.leading_zeros() as usize - dead_bits;
182					if has_one(val, elem.mask().into_inner()) {
183						return Some(out - 1);
184					}
185				}
186
187				for val in body.iter().map(BitStore::load_value).rev() {
188					out -= val.leading_zeros() as usize;
189					if has_one(val, !<T::Mem as Integral>::ZERO) {
190						return Some(out - 1);
191					}
192				}
193
194				if let Some(elem) = head {
195					let val = elem.load_value();
196					if has_one(val, elem.mask().into_inner()) {
197						out -= val.leading_zeros() as usize;
198						return Some(out - 1);
199					}
200				}
201
202				None
203			},
204		}
205	}
206
207	/// Seeks the index of the first `0` bit in the bit-slice.
208	pub(crate) fn sp_first_zero(&self) -> Option<usize> {
209		let mut accum = 0;
210
211		match self.domain() {
212			Domain::Enclave(elem) => {
213				let val = elem.load_value() | !elem.mask().into_inner();
214				accum += val.trailing_ones() as usize
215					- elem.head().into_inner() as usize;
216				if has_zero(val, elem.mask().into_inner()) {
217					return Some(accum);
218				}
219				None
220			},
221			Domain::Region { head, body, tail } => {
222				if let Some(elem) = head {
223					let val = elem.load_value() | !elem.mask().into_inner();
224
225					accum += val.trailing_ones() as usize
226						- elem.head().into_inner() as usize;
227					if has_zero(val, elem.mask().into_inner()) {
228						return Some(accum);
229					}
230				}
231
232				for val in body.iter().map(BitStore::load_value) {
233					accum += val.trailing_ones() as usize;
234					if has_zero(val, !<T::Mem as Integral>::ZERO) {
235						return Some(accum);
236					}
237				}
238
239				if let Some(elem) = tail {
240					let val = elem.load_value() | !elem.mask().into_inner();
241					accum += val.trailing_ones() as usize;
242					if has_zero(val, elem.mask().into_inner()) {
243						return Some(accum);
244					}
245				}
246
247				None
248			},
249		}
250	}
251
252	/// Seeks the index of the last `0` bit in the bit-slice.
253	pub(crate) fn sp_last_zero(&self) -> Option<usize> {
254		let mut out = self.len();
255
256		match self.domain() {
257			Domain::Enclave(elem) => {
258				let val = elem.load_value() | !elem.mask().into_inner();
259				let dead_bits =
260					bits_of::<T::Mem>() - elem.tail().into_inner() as usize;
261				if has_zero(val, elem.mask().into_inner()) {
262					out -= val.leading_ones() as usize - dead_bits as usize;
263					return Some(out - 1);
264				}
265				None
266			},
267			Domain::Region { head, body, tail } => {
268				if let Some(elem) = tail {
269					let val = elem.load_value() | !elem.mask().into_inner();
270					let dead_bits =
271						bits_of::<T::Mem>() - elem.tail().into_inner() as usize;
272					out -= val.leading_ones() as usize - dead_bits;
273					if has_zero(val, elem.mask().into_inner()) {
274						return Some(out - 1);
275					}
276				}
277
278				for val in body.iter().map(BitStore::load_value).rev() {
279					out -= val.leading_ones() as usize;
280					if has_zero(val, !<T::Mem as Integral>::ZERO) {
281						return Some(out - 1);
282					}
283				}
284
285				if let Some(elem) = head {
286					let val = elem.load_value() | !elem.mask().into_inner();
287					if has_zero(val, elem.mask().into_inner()) {
288						out -= val.leading_ones() as usize;
289						return Some(out - 1);
290					}
291				}
292
293				None
294			},
295		}
296	}
297
298	/// Accelerates swapping memory.
299	pub(crate) fn sp_swap_with_bitslice(&mut self, other: &mut Self) {
300		for (this, that) in unsafe {
301			self.chunks_mut(WORD_BITS)
302				.remove_alias()
303				.zip(other.chunks_mut(WORD_BITS).remove_alias())
304		} {
305			let (a, b) = (this.load_le::<usize>(), that.load_le::<usize>());
306			this.store_le(b);
307			that.store_le(a);
308		}
309	}
310}