bitvec/slice/
traits.rs

1#![doc = include_str!("../../doc/slice/traits.md")]
2
3#[cfg(feature = "alloc")]
4use alloc::borrow::ToOwned;
5use core::{
6	cmp,
7	convert::TryFrom,
8	fmt::{
9		self,
10		Binary,
11		Debug,
12		Display,
13		Formatter,
14		LowerHex,
15		Octal,
16		Pointer,
17		UpperHex,
18	},
19	hash::{
20		Hash,
21		Hasher,
22	},
23	str,
24};
25
26use wyz::fmt::FmtForward;
27
28use super::BitSlice;
29#[cfg(feature = "alloc")]
30use crate::vec::BitVec;
31use crate::{
32	domain::Domain,
33	mem,
34	order::{
35		BitOrder,
36		Lsb0,
37		Msb0,
38	},
39	store::BitStore,
40	view::BitView,
41};
42
43/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-AsRef%3C%5BT%5D%3E)
44impl<T, O> AsRef<Self> for BitSlice<T, O>
45where
46	T: BitStore,
47	O: BitOrder,
48{
49	#[inline]
50	fn as_ref(&self) -> &Self {
51		self
52	}
53}
54
55/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-AsMut%3C%5BT%5D%3E)
56impl<T, O> AsMut<Self> for BitSlice<T, O>
57where
58	T: BitStore,
59	O: BitOrder,
60{
61	#[inline]
62	fn as_mut(&mut self) -> &mut Self {
63		self
64	}
65}
66
67/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Eq)
68impl<T, O> Eq for BitSlice<T, O>
69where
70	T: BitStore,
71	O: BitOrder,
72{
73}
74
75/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Ord)
76impl<T, O> Ord for BitSlice<T, O>
77where
78	T: BitStore,
79	O: BitOrder,
80{
81	#[inline]
82	fn cmp(&self, rhs: &Self) -> cmp::Ordering {
83		self.partial_cmp(rhs)
84			.expect("BitSlice has a total ordering")
85	}
86}
87
88/** Tests if two `BitSlice`s are semantically — not representationally — equal.
89
90It is valid to compare slices of different ordering or memory types.
91
92The equality condition requires that they have the same length and that at each
93index, the two slices have the same bit value.
94
95[Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-PartialEq%3C%5BB%5D%3E)
96**/
97impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for BitSlice<T1, O1>
98where
99	T1: BitStore,
100	T2: BitStore,
101	O1: BitOrder,
102	O2: BitOrder,
103{
104	#[inline]
105	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
106		if let (Some(this), Some(that)) =
107			(self.coerce::<T1, Lsb0>(), rhs.coerce::<T1, Lsb0>())
108		{
109			this.sp_eq(that)
110		}
111		else if let (Some(this), Some(that)) =
112			(self.coerce::<T1, Msb0>(), rhs.coerce::<T1, Msb0>())
113		{
114			this.sp_eq(that)
115		}
116		else {
117			self.len() == rhs.len()
118				&& self
119					.iter()
120					.by_vals()
121					.zip(rhs.iter().by_vals())
122					.all(|(l, r)| l == r)
123		}
124	}
125}
126
127//  ref-to-val equality
128
129impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for &BitSlice<T1, O1>
130where
131	T1: BitStore,
132	T2: BitStore,
133	O1: BitOrder,
134	O2: BitOrder,
135{
136	#[inline]
137	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
138		**self == rhs
139	}
140}
141
142impl<T1, T2, O1, O2> PartialEq<BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
143where
144	T1: BitStore,
145	T2: BitStore,
146	O1: BitOrder,
147	O2: BitOrder,
148{
149	#[inline]
150	fn eq(&self, rhs: &BitSlice<T2, O2>) -> bool {
151		**self == rhs
152	}
153}
154
155//  val-to-ref equality
156
157impl<T1, T2, O1, O2> PartialEq<&BitSlice<T2, O2>> for BitSlice<T1, O1>
158where
159	T1: BitStore,
160	T2: BitStore,
161	O1: BitOrder,
162	O2: BitOrder,
163{
164	#[inline]
165	fn eq(&self, rhs: &&BitSlice<T2, O2>) -> bool {
166		*self == **rhs
167	}
168}
169
170impl<T1, T2, O1, O2> PartialEq<&mut BitSlice<T2, O2>> for BitSlice<T1, O1>
171where
172	T1: BitStore,
173	T2: BitStore,
174	O1: BitOrder,
175	O2: BitOrder,
176{
177	#[inline]
178	fn eq(&self, rhs: &&mut BitSlice<T2, O2>) -> bool {
179		*self == **rhs
180	}
181}
182
183/** Compares two `BitSlice`s by semantic — not representational — ordering.
184
185The comparison sorts by testing at each index if one slice has a high bit where
186the other has a low. At the first index where the slices differ, the slice with
187the high bit is greater. If the slices are equal until at least one terminates,
188then they are compared by length.
189
190[Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-PartialOrd%3C%5BT%5D%3E)
191**/
192impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for BitSlice<T1, O1>
193where
194	T1: BitStore,
195	T2: BitStore,
196	O1: BitOrder,
197	O2: BitOrder,
198{
199	#[inline]
200	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
201		for (l, r) in self.iter().by_vals().zip(rhs.iter().by_vals()) {
202			match (l, r) {
203				(true, false) => return Some(cmp::Ordering::Greater),
204				(false, true) => return Some(cmp::Ordering::Less),
205				_ => continue,
206			}
207		}
208		self.len().partial_cmp(&rhs.len())
209	}
210}
211
212//  ref-to-val ordering
213
214impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for &BitSlice<T1, O1>
215where
216	T1: BitStore,
217	T2: BitStore,
218	O1: BitOrder,
219	O2: BitOrder,
220{
221	#[inline]
222	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
223		(*self).partial_cmp(rhs)
224	}
225}
226
227impl<T1, T2, O1, O2> PartialOrd<BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
228where
229	T1: BitStore,
230	T2: BitStore,
231	O1: BitOrder,
232	O2: BitOrder,
233{
234	#[inline]
235	fn partial_cmp(&self, rhs: &BitSlice<T2, O2>) -> Option<cmp::Ordering> {
236		(**self).partial_cmp(rhs)
237	}
238}
239
240//  val-to-ref ordering
241
242impl<T1, T2, O1, O2> PartialOrd<&BitSlice<T2, O2>> for BitSlice<T1, O1>
243where
244	T1: BitStore,
245	T2: BitStore,
246	O1: BitOrder,
247	O2: BitOrder,
248{
249	#[inline]
250	fn partial_cmp(&self, rhs: &&BitSlice<T2, O2>) -> Option<cmp::Ordering> {
251		(*self).partial_cmp(&**rhs)
252	}
253}
254
255impl<T1, T2, O1, O2> PartialOrd<&mut BitSlice<T2, O2>> for BitSlice<T1, O1>
256where
257	T1: BitStore,
258	T2: BitStore,
259	O1: BitOrder,
260	O2: BitOrder,
261{
262	#[inline]
263	fn partial_cmp(&self, rhs: &&mut BitSlice<T2, O2>) -> Option<cmp::Ordering> {
264		(*self).partial_cmp(&**rhs)
265	}
266}
267
268//  &mut-to-& ordering
269
270impl<T1, T2, O1, O2> PartialOrd<&mut BitSlice<T2, O2>> for &BitSlice<T1, O1>
271where
272	T1: BitStore,
273	T2: BitStore,
274	O1: BitOrder,
275	O2: BitOrder,
276{
277	#[inline]
278	fn partial_cmp(&self, rhs: &&mut BitSlice<T2, O2>) -> Option<cmp::Ordering> {
279		(**self).partial_cmp(&**rhs)
280	}
281}
282
283impl<T1, T2, O1, O2> PartialOrd<&BitSlice<T2, O2>> for &mut BitSlice<T1, O1>
284where
285	T1: BitStore,
286	T2: BitStore,
287	O1: BitOrder,
288	O2: BitOrder,
289{
290	#[inline]
291	fn partial_cmp(&self, rhs: &&BitSlice<T2, O2>) -> Option<cmp::Ordering> {
292		(**self).partial_cmp(&**rhs)
293	}
294}
295
296/** Calls [`BitSlice::try_from_slice`], but returns the original Rust slice on
297error instead of the failure event.
298
299This only fails if `slice.len()` exceeds `BitSlice::MAX_ELTS`.
300
301[`BitSlice::try_from_slice`]: crate::slice::BitSlice::try_from_slice
302**/
303impl<'a, T, O> TryFrom<&'a [T]> for &'a BitSlice<T, O>
304where
305	T: BitStore,
306	O: BitOrder,
307{
308	type Error = &'a [T];
309
310	#[inline]
311	fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
312		BitSlice::try_from_slice(slice).map_err(|_| slice)
313	}
314}
315
316/** Calls [`BitSlice::try_from_slice_mut`], but returns the original Rust slice
317on error instead of the failure event.
318
319This only fails if `slice.len()` exceeds `BitSlice::MAX_ELTS`.
320
321[`BitSlice::try_from_slice_mut`]: crate::slice::BitSlice::try_from_slice_mut
322**/
323impl<'a, T, O> TryFrom<&'a mut [T]> for &'a mut BitSlice<T, O>
324where
325	T: BitStore,
326	O: BitOrder,
327{
328	type Error = &'a mut [T];
329
330	#[inline]
331	fn try_from(slice: &'a mut [T]) -> Result<Self, Self::Error> {
332		let slice_ptr = slice as *mut [T];
333		BitSlice::try_from_slice_mut(slice)
334			.map_err(|_| unsafe { &mut *slice_ptr })
335	}
336}
337
338/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Default-1)
339impl<T, O> Default for &BitSlice<T, O>
340where
341	T: BitStore,
342	O: BitOrder,
343{
344	#[inline]
345	fn default() -> Self {
346		BitSlice::empty()
347	}
348}
349
350/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Default)
351impl<T, O> Default for &mut BitSlice<T, O>
352where
353	T: BitStore,
354	O: BitOrder,
355{
356	#[inline]
357	fn default() -> Self {
358		BitSlice::empty_mut()
359	}
360}
361
362/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Debug)
363impl<T, O> Debug for BitSlice<T, O>
364where
365	T: BitStore,
366	O: BitOrder,
367{
368	#[inline]
369	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
370		self.as_bitspan().render(fmt, "Slice", None)?;
371		fmt.write_str(" ")?;
372		Display::fmt(self, fmt)
373	}
374}
375
376impl<T, O> Display for BitSlice<T, O>
377where
378	T: BitStore,
379	O: BitOrder,
380{
381	#[inline]
382	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
383		fmt.debug_list()
384			.entries(self.iter().by_vals().map(|b| if b { 1 } else { 0 }))
385			.finish()
386	}
387}
388
389impl<T, O> Pointer for BitSlice<T, O>
390where
391	T: BitStore,
392	O: BitOrder,
393{
394	#[inline]
395	fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
396		Pointer::fmt(&self.as_bitspan(), fmt)
397	}
398}
399
400/// Encodes a short bit-slice into an ASCII b36 value.
401#[inline(always)]
402fn bits_to_ascii<T, O>(bits: &BitSlice<T, O>, alpha: u8) -> u8
403where
404	T: BitStore,
405	O: BitOrder,
406{
407	let mut val = 0u8;
408	for bit in bits.iter().by_vals() {
409		val <<= 1;
410		val |= bit as u8;
411	}
412	match val {
413		v @ 0 ..= 9 => b'0' + v,
414		v @ 10 ..= 35 => alpha - 10 + v,
415		_ => unreachable!(
416			"bit-slices wider than five bits cannot be rendered to ASCII b36"
417		),
418	}
419}
420
421/** Encodes an arbitrary bit-slice into an ASCII b36 string.
422
423## Parameters
424
425- `bits`: the bit-slice to encode.
426- `into`: a provided buffer into which the bit-slice is encoded.
427- `radix`: the bit width of each digit (log2 of its radix).
428- `skip`: the number of bytes to skip before beginning the write.
429- `alpha`: one of `b'a'` or `b'A'`.
430
431## Returns
432
433A subset of `into` that is now initialized to the ASCII encoding.
434**/
435#[inline(always)]
436fn encode_ascii<'a, T, O>(
437	bits: &BitSlice<T, O>,
438	into: &'a mut [u8],
439	radix: usize,
440	mut skip: usize,
441	alpha: u8,
442) -> &'a str
443where
444	T: BitStore,
445	O: BitOrder,
446{
447	for (chunk, slot) in
448		bits.rchunks(radix).rev().zip(into.iter_mut().skip(skip))
449	{
450		*slot = bits_to_ascii(chunk, alpha);
451		skip += 1;
452	}
453	unsafe { str::from_utf8_unchecked(&into[.. skip]) }
454}
455
456/// Constructs the numeric formatting implementations.
457macro_rules! fmt {
458	($($trait:ident: $alpha:expr, $pfx:expr, $radix:expr;)+) => { $(
459		#[doc = include_str!("../../doc/slice/format.md")]
460		impl<T, O> $trait for BitSlice<T, O>
461		where
462			T: BitStore,
463			O: BitOrder,
464		{
465			#[inline]
466			#[allow(clippy::modulo_one)] // I know what I’m doing.
467			//  TODO(myrrlyn): See if Binary codegen ditches the loops.
468			fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
469				const D: usize = mem::bits_of::<usize>() / $radix;
470				const M: usize = mem::bits_of::<usize>() % $radix;
471				const W: usize = D + (M != 0) as usize;
472				let mut txt: [u8; W + 2] = [b'0'; W + 2];
473				txt[1] = $pfx;
474
475				let start = if fmt.alternate() { 0 } else { 2 };
476				let mut seq = fmt.debug_list();
477				match self.domain() {
478					Domain::Enclave(elem) => {
479						seq.entry(&encode_ascii(
480							elem.into_bitslice(),
481							&mut txt[start ..],
482							$radix,
483							2 - start,
484							$alpha,
485						).fmt_display());
486					},
487					Domain::Region { head, body, tail } => {
488						if let Some(elem) = head {
489							seq.entry(&encode_ascii(
490								elem.into_bitslice(),
491								&mut txt[start ..],
492								$radix,
493								2 - start,
494								$alpha,
495							).fmt_display());
496						}
497						for elem in body.iter().map(BitStore::load_value) {
498							seq.entry(&encode_ascii(
499								elem.view_bits::<O>(),
500								&mut txt[start ..],
501								$radix,
502								2 - start,
503								$alpha,
504							).fmt_display());
505						}
506						if let Some(elem) = tail {
507							seq.entry(&encode_ascii(
508								elem.into_bitslice(),
509								&mut txt[start ..],
510								$radix,
511								2 - start,
512								$alpha,
513							).fmt_display());
514						}
515					},
516				}
517				seq.finish()
518			}
519		}
520	)+ };
521}
522
523fmt! {
524	Binary: b'0', b'b', 1;
525	Octal: b'0', b'o', 3;
526	LowerHex: b'a', b'x', 4;
527	UpperHex: b'A', b'x', 4;
528}
529
530/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Hash)
531#[cfg(not(tarpaulin_include))]
532impl<T, O> Hash for BitSlice<T, O>
533where
534	T: BitStore,
535	O: BitOrder,
536{
537	#[inline]
538	fn hash<H>(&self, hasher: &mut H)
539	where H: Hasher {
540		self.iter().by_vals().for_each(|bit| bit.hash(hasher));
541	}
542}
543
544#[doc = include_str!("../../doc/slice/threadsafe.md")]
545unsafe impl<T, O> Send for BitSlice<T, O>
546where
547	T: BitStore + Sync,
548	O: BitOrder,
549{
550}
551
552#[doc = include_str!("../../doc/slice/threadsafe.md")]
553unsafe impl<T, O> Sync for BitSlice<T, O>
554where
555	T: BitStore + Sync,
556	O: BitOrder,
557{
558}
559
560/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-Unpin)
561impl<T, O> Unpin for BitSlice<T, O>
562where
563	T: BitStore,
564	O: BitOrder,
565{
566}
567
568/// [Original](https://doc.rust-lang.org/std/primitive.slice.html#impl-ToOwned)
569#[cfg(feature = "alloc")]
570#[cfg(not(tarpaulin_include))]
571impl<T, O> ToOwned for BitSlice<T, O>
572where
573	T: BitStore,
574	O: BitOrder,
575{
576	type Owned = BitVec<T, O>;
577
578	#[inline]
579	fn to_owned(&self) -> Self::Owned {
580		BitVec::from_bitslice(self)
581	}
582}