sux/traits/bit_field_slice.rs
1/*
2 * SPDX-FileCopyrightText: 2023 Inria
3 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7
8//! Traits for slices of bit fields of constant width.
9//!
10//! Slices of bit fields are accessed with a logic similar to slices, but when
11//! indexed with [`get`](BitFieldSlice::get) return an owned value of a [fixed
12//! bit width](BitFieldSliceCore::bit_width). The associated implementation is
13//! [`BitFieldVec`](crate::bits::bit_field_vec::BitFieldVec).
14//!
15//! Implementing the
16//! [`Index`](core::ops::Index)/[`IndexMut`](core::ops::IndexMut) traits would
17//! be more natural and practical, but in certain cases it is impossible: in our
18//! main use case, [`BitFieldVec`](crate::bits::bit_field_vec::BitFieldVec), we
19//! cannot implement [`Index`](core::ops::Index) because there is no way to
20//! return a reference to a bit segment.
21//!
22//! There are three end-user traits: [`BitFieldSlice`], [`BitFieldSliceMut`] and
23//! [`AtomicBitFieldSlice`]. The trait [`BitFieldSliceCore`] contains the common
24//! methods, and in particular [`BitFieldSliceCore::bit_width`], which returns
25//! the bit width the values stored in the slice. All stored values must fit
26//! within this bit width.
27//!
28//! All the traits depends on a type parameter `W` that must implement [`Word`],
29//! and which default to `usize`, but any type satisfying the [`Word`] trait can
30//! be used, with the restriction that the bit width of the slice can be at most
31//! the bit width of `W` as defined by [`AsBytes::BITS`]. Additionally, to
32//! implement [`AtomicBitFieldSlice`], `W` must implement [`IntoAtomic`]. The
33//! methods of all traits accept and return values of type `W`.
34//!
35//! If you need to iterate over a [`BitFieldSlice`], you can use
36//! [`BitFieldSliceIterator`].
37//!
38//! Implementations must return always zero on a [`BitFieldSlice::get`] when the
39//! bit width is zero. The behavior of a [`BitFieldSliceMut::set`] in the same
40//! context is not defined.
41//!
42//! It is suggested that types implementing [`BitFieldSlice`] implement on a
43//! reference [`IntoIterator`] with item `W` using [`BitFieldSliceIterator`] as
44//! helper.
45//!
46//! We provide implementations for vectors and slices of all primitive atomic
47//! and non-atomic unsigned integer types that view their elements as values
48//! with a bit width equal to that of the type.
49//!
50//! # Simpler methods for atomic slices
51//!
52//! [`AtomicBitFieldSlice`] has rather cumbersome method names. There is however
53//! a trait [`AtomicHelper`] that can be imported that will add to
54//! [`AtomicBitFieldSlice`] equivalent methods without the `_atomic` infix. You
55//! should be however careful to not mix [`AtomicHelper`] and [`BitFieldSlice`]
56//! or a number of ambiguities in trait resolution will arise. In particular, if
57//! you plan to use [`AtomicHelper`], we suggest that you do not import the
58//! prelude.
59//! ```rust
60//! use sux::traits::bit_field_slice::{AtomicBitFieldSlice,AtomicHelper};
61//! use std::sync::atomic::Ordering;
62//!
63//! let slice = sux::bits::AtomicBitFieldVec::<usize>::new(3, 3);
64//! slice.set(0, 1, Ordering::Relaxed);
65//! assert_eq!(slice.get(0, Ordering::Relaxed), 1);
66//! ```
67
68#![allow(clippy::result_unit_err)]
69use common_traits::*;
70use core::sync::atomic::*;
71use mem_dbg::{MemDbg, MemSize};
72#[cfg(feature = "rayon")]
73use rayon::iter::{
74 IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator,
75};
76use std::marker::PhantomData;
77
78/// A derived trait that the types used as a parameter for [`BitFieldSlice`] must satisfy.
79/// To be usable in an [`AtomicBitFieldSlice`], the type must also implement [`IntoAtomic`].
80pub trait Word: UnsignedInt + FiniteRangeNumber + AsBytes {}
81impl<W: UnsignedInt + FiniteRangeNumber + AsBytes> Word for W {}
82
83/// Common methods for [`BitFieldSlice`], [`BitFieldSliceMut`], and [`AtomicBitFieldSlice`].
84///
85/// The dependence on `W` is necessary to implement this trait on vectors and slices, as
86/// we need the bit width of the values stored in the slice.
87pub trait BitFieldSliceCore<W> {
88 /// Returns the width of the slice.
89 ///
90 /// All elements stored in the slice must fit within this bit width.
91 fn bit_width(&self) -> usize;
92 /// Returns the length of the slice.
93 fn len(&self) -> usize;
94 /// Returns true if the slice has length zero.
95 fn is_empty(&self) -> bool {
96 self.len() == 0
97 }
98}
99
100macro_rules! panic_if_out_of_bounds {
101 ($index: expr, $len: expr) => {
102 if $index >= $len {
103 panic!("Index out of bounds: {} >= {}", $index, $len)
104 }
105 };
106}
107pub(crate) use panic_if_out_of_bounds;
108
109macro_rules! panic_if_value {
110 ($value: expr, $mask: expr, $bit_width: expr) => {
111 if $value & $mask != $value {
112 panic!("Value {} does not fit in {} bits", $value, $bit_width);
113 }
114 };
115}
116pub(crate) use panic_if_value;
117
118macro_rules! debug_assert_bounds {
119 ($index: expr, $len: expr) => {
120 debug_assert!(
121 $index < $len || ($index == 0 && $len == 0),
122 "Index out of bounds: {} >= {}",
123 $index,
124 $len
125 );
126 };
127}
128
129/// A slice of bit fields of constant bit width.
130pub trait BitFieldSlice<W: Word>: BitFieldSliceCore<W> {
131 /// Returns the value at the specified index.
132 ///
133 /// # Safety
134 ///
135 /// `index` must be in [0..[len](`BitFieldSliceCore::len`)). No bounds checking is performed.
136 unsafe fn get_unchecked(&self, index: usize) -> W;
137
138 /// Returns the value at the specified index.
139 ///
140 /// # Panics
141 /// May panic if the index is not in in [0..[len](`BitFieldSliceCore::len`))
142 fn get(&self, index: usize) -> W {
143 panic_if_out_of_bounds!(index, self.len());
144 unsafe { self.get_unchecked(index) }
145 }
146}
147
148/// A mutable slice of bit fields of constant bit width.
149pub trait BitFieldSliceMut<W: Word>: BitFieldSlice<W> {
150 /// Returns the mask to apply to values to ensure they fit in
151 /// [`bit_width`](BitFieldSliceCore::bit_width) bits.
152 #[inline(always)]
153 fn mask(&self) -> W {
154 // TODO: Maybe testless?
155 if self.bit_width() == 0 {
156 W::ZERO
157 } else {
158 W::MAX >> (W::BITS as u32 - self.bit_width() as u32)
159 }
160 }
161
162 /// Sets the element of the slice at the specified index.
163 /// No bounds checking is performed.
164 ///
165 /// # Safety
166 /// - `index` must be in [0..[len](`BitFieldSliceCore::len`));
167 /// - `value` must fit withing [`BitFieldSliceCore::bit_width`] bits.
168 ///
169 /// No bound or bit-width check is performed.
170 unsafe fn set_unchecked(&mut self, index: usize, value: W);
171
172 /// Sets the element of the slice at the specified index.
173 ///
174 /// May panic if the index is not in in [0..[len](`BitFieldSliceCore::len`))
175 /// or the value does not fit in [`BitFieldSliceCore::bit_width`] bits.
176 fn set(&mut self, index: usize, value: W) {
177 panic_if_out_of_bounds!(index, self.len());
178 let bit_width = self.bit_width();
179 // TODO: Maybe testless?
180 let mask = if bit_width == 0 {
181 W::ZERO
182 } else {
183 W::MAX >> (W::BITS as u32 - bit_width as u32)
184 };
185
186 panic_if_value!(value, mask, bit_width);
187 unsafe {
188 self.set_unchecked(index, value);
189 }
190 }
191
192 /// Sets all values to zero.
193 fn reset(&mut self);
194
195 /// Sets all values to zero using a parallel implementation.
196 #[cfg(feature = "rayon")]
197 fn par_reset(&mut self);
198
199 /// Copy part of the content of the vector to another vector.
200 ///
201 /// At most `len` elements are copied, compatibly with the elements
202 /// available in both vectors.
203 ///
204 /// # Arguments
205 ///
206 /// * `from`: the index of the first element to copy.
207 ///
208 /// * `dst`: the destination vector.
209 ///
210 /// * `to`: the index of the first element in the destination vector.
211 ///
212 /// * `len`: the maximum number of elements to copy.
213 ///
214 /// # Implementation Notes
215 ///
216 /// The default implementation is a simple loop that copies the elements one
217 /// by one. It is expected to be implemented in a more efficient way.
218 fn copy(&self, from: usize, dst: &mut Self, to: usize, len: usize) {
219 // Reduce len to the elements available in both vectors
220 let len = Ord::min(Ord::min(len, dst.len() - to), self.len() - from);
221 for i in 0..len {
222 dst.set(to + i, self.get(from + i));
223 }
224 }
225
226 /// Applies a function to all elements of the slice in place without
227 /// checking [bit widths](BitFieldSliceCore::bit_width).
228 ///
229 /// This method is semantically equivalent to:
230 /// ```ignore
231 /// for i in 0..self.len() {
232 /// self.set_unchecked(i, f(self.get_unchecked(i)));
233 /// }
234 /// ```
235 /// and this is indeed the default implementation.
236 ///
237 /// See [`apply_in_place`](BitFieldSliceMut::apply_in_place) for examples.
238 ///
239 /// # Safety
240 /// The function must return a value that fits the the [bit
241 /// width](BitFieldSliceCore::bit_width) of the slice.
242 unsafe fn apply_in_place_unchecked<F>(&mut self, mut f: F)
243 where
244 F: FnMut(W) -> W,
245 Self: BitFieldSlice<W>,
246 {
247 for idx in 0..self.len() {
248 let value = unsafe { self.get_unchecked(idx) };
249 let new_value = f(value);
250 unsafe { self.set_unchecked(idx, new_value) };
251 }
252 }
253
254 /// Applies a function to all elements of the slice in place.
255 ///
256 /// This method is semantically equivalent to:
257 /// ```ignore
258 /// for i in 0..self.len() {
259 /// self.set(i, f(self.get(i)));
260 /// }
261 /// ```
262 /// and this is indeed the default implementation.
263 ///
264 /// The function is applied from the first element to the last: thus,
265 /// it possible to compute cumulative sums as follows:
266 ///
267 /// ```rust
268 /// use sux::bits::BitFieldVec;
269 /// use sux::traits::BitFieldSliceMut;
270 ///
271 /// let mut vec = BitFieldVec::<u16>::new(9, 10);
272 ///
273 /// for i in 0..10 {
274 /// vec.set(i, i as u16);
275 /// }
276 ///
277 /// let mut total = 0;
278 /// vec.apply_in_place(|x| {
279 /// total += x;
280 /// total
281 /// });
282 /// ```
283 fn apply_in_place<F>(&mut self, mut f: F)
284 where
285 F: FnMut(W) -> W,
286 Self: BitFieldSlice<W>,
287 {
288 let bit_width = self.bit_width();
289 let mask = self.mask();
290 unsafe {
291 self.apply_in_place_unchecked(|x| {
292 let new_value = f(x);
293 panic_if_value!(new_value, mask, bit_width);
294 new_value
295 });
296 }
297 }
298
299 type ChunksMut<'a>: Iterator<Item: BitFieldSliceMut<W>>
300 where
301 Self: 'a;
302
303 /// Tries and returns an iterator over non-overlapping mutable chunks of a
304 /// bit-field slice, starting at the beginning of the slice.
305 ///
306 /// This might not always be possible; implementations must document when
307 /// the method will success (see, for example, [the implementation for
308 /// `BitFieldVec`](#impl-BitFieldSliceMut-for-BitFieldVec)).
309 ///
310 /// When the slice len is not evenly divided by the chunk size, the last
311 /// chunk of the iteration will be the remainder.
312 ///
313 /// # Examples
314 ///
315 /// ```
316 /// # fn main() -> Result<(), ()> {
317 /// use sux::prelude::*;
318 ///
319 /// let mut b = bit_field_vec![32; 4, 500, 2, 3, 1];
320 /// for mut c in b.try_chunks_mut(2)? {
321 /// c.set(0, 5);
322 /// }
323 /// assert_eq!(b, bit_field_vec![32; 5, 500, 5, 3, 5]);
324 /// # Ok(())
325 /// # }
326 /// ```
327 fn try_chunks_mut(&mut self, chunk_size: usize) -> Result<Self::ChunksMut<'_>, ()>;
328
329 /// Returns the backend of the slice as a mutable slice of `W`.
330 fn as_mut_slice(&mut self) -> &mut [W];
331}
332
333/// A (tentatively) thread-safe slice of bit fields of constant bit width supporting atomic operations.
334///
335/// Different implementations might provide different atomicity guarantees. See
336/// [`BitFieldVec`](crate::bits::bit_field_vec::BitFieldVec) for an example.
337pub trait AtomicBitFieldSlice<W: Word + IntoAtomic>: BitFieldSliceCore<W::AtomicType>
338where
339 W::AtomicType: AtomicUnsignedInt + AsBytes,
340{
341 /// Returns the value at the specified index.
342 ///
343 /// # Safety
344 /// `index` must be in [0..[len](`BitFieldSliceCore::len`)).
345 /// No bound or bit-width check is performed.
346 unsafe fn get_atomic_unchecked(&self, index: usize, order: Ordering) -> W;
347
348 /// Returns the value at the specified index.
349 ///
350 /// # Panics
351 /// May panic if the index is not in in [0..[len](`BitFieldSliceCore::len`))
352 fn get_atomic(&self, index: usize, order: Ordering) -> W {
353 panic_if_out_of_bounds!(index, self.len());
354 unsafe { self.get_atomic_unchecked(index, order) }
355 }
356
357 /// Sets the element of the slice at the specified index.
358 ///
359 /// # Safety
360 /// - `index` must be in [0..[len](`BitFieldSliceCore::len`));
361 /// - `value` must fit withing [`BitFieldSliceCore::bit_width`] bits.
362 ///
363 /// No bound or bit-width check is performed.
364 unsafe fn set_atomic_unchecked(&self, index: usize, value: W, order: Ordering);
365
366 /// Sets the element of the slice at the specified index.
367 ///
368 /// May panic if the index is not in in [0..[len](`BitFieldSliceCore::len`))
369 /// or the value does not fit in [`BitFieldSliceCore::bit_width`] bits.
370 fn set_atomic(&self, index: usize, value: W, order: Ordering) {
371 if index >= self.len() {
372 panic_if_out_of_bounds!(index, self.len());
373 }
374 let bw = self.bit_width();
375
376 let mask = if bw == 0 {
377 W::ZERO
378 } else {
379 W::MAX >> (W::BITS as u32 - bw as u32)
380 };
381 panic_if_value!(value, mask, bw);
382 unsafe {
383 self.set_atomic_unchecked(index, value, order);
384 }
385 }
386
387 /// Sets all values to zero.
388 ///
389 /// This method takes an exclusive reference because usually one needs to
390 /// reset a vector to reuse it, and the mutable reference makes it
391 /// impossible to have any other reference hanging around.
392 fn reset_atomic(&mut self, order: Ordering);
393
394 /// Sets all values to zero using a parallel implementation.
395 ///
396 /// See [`reset_atomic`](AtomicBitFieldSlice::reset_atomic) for more
397 /// details.
398 #[cfg(feature = "rayon")]
399 fn par_reset_atomic(&mut self, order: Ordering);
400}
401
402/// An [`Iterator`] implementation returning the elements of a [`BitFieldSlice`].
403///
404/// You can easily implement [`IntoIterator`] on a reference to your type using this structure.
405#[derive(Debug, Clone, MemDbg, MemSize)]
406pub struct BitFieldSliceIterator<'a, W: Word, B: BitFieldSlice<W>> {
407 slice: &'a B,
408 index: usize,
409 _marker: PhantomData<W>,
410}
411
412impl<'a, V: Word, B: BitFieldSlice<V>> BitFieldSliceIterator<'a, V, B> {
413 pub fn new(slice: &'a B, from: usize) -> Self {
414 if from > slice.len() {
415 panic!("Start index out of bounds: {} > {}", from, slice.len());
416 }
417 Self {
418 slice,
419 index: from,
420 _marker: PhantomData,
421 }
422 }
423}
424
425impl<W: Word, B: BitFieldSlice<W>> Iterator for BitFieldSliceIterator<'_, W, B> {
426 type Item = W;
427 fn next(&mut self) -> Option<Self::Item> {
428 if self.index < self.slice.len() {
429 // SAFETY: self.index is always within bounds
430 let res = unsafe { self.slice.get_unchecked(self.index) };
431 self.index += 1;
432 Some(res)
433 } else {
434 None
435 }
436 }
437}
438
439// Implementations for slices of non-atomic types
440
441macro_rules! impl_core {
442 ($($ty:ty),*) => {$(
443 impl<T: AsRef<[$ty]>> BitFieldSliceCore<$ty> for T {
444 #[inline(always)]
445 fn bit_width(&self) -> usize {
446 <$ty>::BITS as usize
447 }
448 #[inline(always)]
449 fn len(&self) -> usize {
450 self.as_ref().len()
451 }
452 }
453 )*};
454}
455
456impl_core!(u8, u16, u32, u64, u128, usize);
457// This implementation is not necessary, but it avoids ambiguity
458// with expressions like [1, 2, 3].len() when using the prelude.
459// Without this implementation, the compiler complains that
460// BitFieldSliceCore is not implemented for [i32; 3].
461impl_core!(i8, i16, i32, i64, i128, isize);
462
463macro_rules! impl_ref {
464 ($($ty:ty),*) => {$(
465 impl<T: AsRef<[$ty]>> BitFieldSlice<$ty> for T {
466 #[inline(always)]
467 unsafe fn get_unchecked(&self, index: usize) -> $ty {
468 debug_assert_bounds!(index, self.len());
469 *self.as_ref().get_unchecked(index)
470 }
471 }
472 )*};
473}
474
475impl_ref!(u8, u16, u32, u64, u128, usize);
476
477macro_rules! impl_mut {
478 ($($ty:ty),*) => {$(
479 impl<T: AsMut<[$ty]> + AsRef<[$ty]>> BitFieldSliceMut<$ty> for T {
480 #[inline(always)]
481 unsafe fn set_unchecked(&mut self, index: usize, value: $ty) {
482 debug_assert_bounds!(index, self.len());
483 *self.as_mut().get_unchecked_mut(index) = value;
484 }
485
486 fn reset(&mut self) {
487 for idx in 0..self.len() {
488 unsafe{self.set_unchecked(idx, 0)};
489 }
490 }
491
492 #[cfg(feature = "rayon")]
493 fn par_reset(&mut self) {
494 self.as_mut()
495 .par_iter_mut()
496 .with_min_len(crate::RAYON_MIN_LEN)
497 .for_each(|w| { *w = 0 });
498 }
499
500 fn copy(&self, from: usize, dst: &mut Self, to: usize, len: usize) {
501 let len = Ord::min(Ord::min(len, dst.len() - to), self.len() - from);
502 dst.as_mut()[to..][..len].copy_from_slice(&self.as_ref()[from..][..len]);
503 }
504
505 type ChunksMut<'a> = std::slice::ChunksMut<'a, $ty> where Self: 'a;
506
507 fn try_chunks_mut(&mut self, chunk_size: usize) -> Result<Self::ChunksMut<'_>, ()> {
508 Ok(self.as_mut().chunks_mut(chunk_size))
509 }
510
511 fn as_mut_slice(&mut self) -> &mut [$ty] {
512 self.as_mut()
513 }
514 }
515 )*};
516}
517
518impl_mut!(u8, u16, u32, u64, u128, usize);
519
520// Implementations for slices of atomic types
521
522macro_rules! impl_core_atomic {
523 ($($aty:ty),*) => {$(
524 impl<T: AsRef<[$aty]>> BitFieldSliceCore<$aty> for T {
525 #[inline(always)]
526 fn bit_width(&self) -> usize {
527 <$aty>::BITS as usize
528 }
529 #[inline(always)]
530 fn len(&self) -> usize {
531 self.as_ref().len()
532 }
533 }
534 )*};
535}
536
537impl_core_atomic!(AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize);
538
539macro_rules! impl_atomic {
540 ($std:ty, $atomic:ty) => {
541 impl<T: AsRef<[$atomic]>> AtomicBitFieldSlice<$std> for T {
542 #[inline(always)]
543 unsafe fn get_atomic_unchecked(&self, index: usize, order: Ordering) -> $std {
544 debug_assert_bounds!(index, self.len());
545 self.as_ref().get_unchecked(index).load(order)
546 }
547 #[inline(always)]
548 unsafe fn set_atomic_unchecked(&self, index: usize, value: $std, order: Ordering) {
549 debug_assert_bounds!(index, self.len());
550 self.as_ref().get_unchecked(index).store(value, order);
551 }
552
553 fn reset_atomic(&mut self, order: Ordering) {
554 for idx in 0..self.len() {
555 unsafe { self.set_atomic_unchecked(idx, 0, order) };
556 }
557 }
558
559 #[cfg(feature = "rayon")]
560 fn par_reset_atomic(&mut self, order: Ordering) {
561 self.as_ref()
562 .par_iter()
563 .with_min_len(crate::RAYON_MIN_LEN)
564 .for_each(|w| w.store(0, order));
565 }
566 }
567 };
568}
569
570impl_atomic!(u8, AtomicU8);
571impl_atomic!(u16, AtomicU16);
572impl_atomic!(u32, AtomicU32);
573impl_atomic!(u64, AtomicU64);
574impl_atomic!(usize, AtomicUsize);
575
576/// Helper trait eliminating `_atomic` from all methods of [`AtomicBitFieldSlice`]
577/// using a blanked implementation.
578///
579/// Note that using this trait and [`BitFieldSlice`] in the same module might cause
580/// ambiguity problems.
581pub trait AtomicHelper<W: Word + IntoAtomic>: AtomicBitFieldSlice<W>
582where
583 W::AtomicType: AtomicUnsignedInt + AsBytes,
584{
585 /// Delegates to [`AtomicBitFieldSlice::get_atomic_unchecked`]
586 /// # Safety
587 /// See [`AtomicBitFieldSlice::get_atomic_unchecked`]
588 #[inline(always)]
589 unsafe fn get_unchecked(&self, index: usize, order: Ordering) -> W {
590 self.get_atomic_unchecked(index, order)
591 }
592
593 /// Delegates to [`AtomicBitFieldSlice::set_atomic`]
594 #[inline(always)]
595 fn get(&self, index: usize, order: Ordering) -> W {
596 self.get_atomic(index, order)
597 }
598
599 /// Delegates to [`AtomicBitFieldSlice::set_atomic_unchecked`]
600 /// # Safety
601 /// See [`AtomicBitFieldSlice::get_atomic_unchecked`]
602 #[inline(always)]
603 unsafe fn set_unchecked(&self, index: usize, value: W, order: Ordering) {
604 self.set_atomic_unchecked(index, value, order)
605 }
606
607 /// Delegates to [`AtomicBitFieldSlice::set_atomic`]
608 #[inline(always)]
609 fn set(&self, index: usize, value: W, order: Ordering) {
610 self.set_atomic(index, value, order)
611 }
612
613 /// Delegates to [`AtomicBitFieldSlice::reset_atomic`]
614 #[inline(always)]
615 fn reset(&mut self, order: Ordering) {
616 self.reset_atomic(order);
617 }
618}
619
620impl<T, W: Word + IntoAtomic> AtomicHelper<W> for T
621where
622 T: AtomicBitFieldSlice<W>,
623 W::AtomicType: AtomicUnsignedInt + AsBytes,
624{
625}