sux/traits/mod.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//! Main traits used in the implementation of succinct and compressed data
9//! structures.
10
11pub mod bal_paren;
12pub use bal_paren::*;
13
14pub mod bit_field_slice;
15use std::{rc::Rc, sync::Arc};
16
17#[allow(unused_imports)]
18use crate::bits::bit_vec::BitVec;
19use ambassador::delegatable_trait;
20#[allow(unused_imports)]
21use atomic_primitive::PrimitiveAtomicUnsigned;
22pub use bit_field_slice::*;
23
24pub mod indexed_dict;
25use impl_tools::autoimpl;
26pub use indexed_dict::*;
27
28pub mod iter;
29pub use iter::*;
30
31pub mod rank_sel;
32use num_primitive::PrimitiveUnsigned;
33pub use rank_sel::*;
34
35pub mod bit_vec_ops;
36pub use bit_vec_ops::*;
37
38/// Error returned by [`TryIntoUnaligned::try_into_unaligned`] when the
39/// bit width does not satisfy the constraints for unaligned reads.
40#[derive(Debug)]
41pub struct UnalignedConversionError(pub String);
42
43impl std::fmt::Display for UnalignedConversionError {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 f.write_str(&self.0)
46 }
47}
48
49impl std::error::Error for UnalignedConversionError {}
50
51/// A convenient alias for the unaligned variant of a type that implements
52/// [`TryIntoUnaligned`].
53pub type Unaligned<T> = <T as TryIntoUnaligned>::Unaligned;
54
55/// A trait for types that can be converted into an unaligned variant
56/// that uses branchless [unaligned reads].
57///
58/// The conversion will fail if the bit width for which unaligned reads are
59/// required does not satisfy the constraints for the word type used by the
60/// structure or its components.
61///
62/// Compound types (e.g., [`SignedFunc`]) implement this trait by recursively
63/// converting their inner components.
64///
65/// You can obtain an unaligned variant of a structure just by chaining the
66/// [`try_into_unaligned`] method at construction time. Since the unaligned
67/// variant of a type can be quite complicated, there is an [`Unaligned`]
68/// type alias to refer to it more conveniently. For example,
69///
70/// ```rust
71/// # #[cfg(feature = "rayon")]
72/// # fn main() -> anyhow::Result<()> {
73/// # use dsi_progress_logger::no_logging;
74/// # use sux::func::LcpMmphfInt;
75/// # use sux::utils::FromSlice;
76/// # use sux::traits::{Unaligned, TryIntoUnaligned};
77/// let keys: Vec<u64> = vec![10, 20, 30, 40, 50];
78/// let unaligned_func: Unaligned<LcpMmphfInt<u64>> =
79/// LcpMmphfInt::try_new(FromSlice::new(&keys), keys.len(), no_logging![])?.try_into_unaligned()?;
80/// # Ok(())
81/// # }
82/// # #[cfg(not(feature = "rayon"))]
83/// # fn main() {}
84/// ```
85///
86/// Or, equivalently, without chaining:
87///
88/// ```rust
89/// # #[cfg(feature = "rayon")]
90/// # fn main() -> anyhow::Result<()> {
91/// # use dsi_progress_logger::no_logging;
92/// # use sux::func::LcpMmphfInt;
93/// # use sux::utils::FromSlice;
94/// # use sux::traits::TryIntoUnaligned;
95/// # let keys: Vec<u64> = vec![10, 20, 30, 40, 50];
96/// let func: LcpMmphfInt<u64> =
97/// LcpMmphfInt::try_new(FromSlice::new(&keys), keys.len(), no_logging![])?;
98/// let unaligned_func = func.try_into_unaligned()?;
99/// # Ok(())
100/// # }
101/// # #[cfg(not(feature = "rayon"))]
102/// # fn main() {}
103/// ```
104///
105/// [unaligned reads]: crate::bits::BitFieldVec::get_unaligned
106/// [`SignedFunc`]: crate::func::SignedFunc
107/// [`try_into_unaligned`]: Self::try_into_unaligned
108pub trait TryIntoUnaligned {
109 /// The unaligned version of this type.
110 type Unaligned;
111 /// Converts `self` into the unaligned variant.
112 ///
113 /// # Errors
114 ///
115 /// Returns an error if the bit width for which unaligned reads are required
116 /// does not satisfy the constraints for the word type used by the structure
117 /// or its components.
118 fn try_into_unaligned(self) -> Result<Self::Unaligned, UnalignedConversionError>;
119}
120
121/// A trait for primitive types that can be used in [backends].
122///
123/// This trait is equivalent to [`PrimitiveUnsigned`], but it has a shorter
124/// name and provides constants [`ZERO`] and [`ONE`], which avoid a dependency
125/// on the [`num-traits`] crate.
126///
127/// [`num-traits`]: https://crates.io/crates/num-traits
128/// [backends]: crate::traits::Backend
129/// [`ZERO`]: Self::ZERO
130/// [`ONE`]: Self::ONE
131pub trait Word: PrimitiveUnsigned {
132 const ZERO: Self;
133 const ONE: Self;
134}
135
136// Note: once we can switch to a more recent num-primitive,
137// we will be able to use CONST[0] and CONST[1] to define ZERO and ONE
138// and we will be able to remove this macro.
139macro_rules! impl_word {
140 ($($ty:ty),*) => {
141 $(impl Word for $ty {
142 const ZERO: Self = 0;
143 const ONE: Self = 1;
144 })*
145 };
146}
147
148impl_word!(u8, u16, u32, u64, u128, usize);
149
150/// The basic trait defining backends.
151///
152/// Backends are types used as underlying storage by other types. Backends are
153/// made of contiguous sequences of words, and the type of such words is given
154/// by the [`Backend::Word`] associated type.
155///
156/// For example, the type `BitVec<Vec<usize>>` represents a bit vector using a
157/// vector of `usize` as its backend.
158///
159/// This trait provides no methods: access to the underlying data is provided by
160/// other traits, such as [`AsRef`] or [`AsMut`]. Moreover, the [`Backend::Word`]
161/// associated type is usually bound with an appropriate trait such as [`Word`] or
162/// [`PrimitiveAtomicUnsigned`].
163///
164/// For example, the typical read-only word-based backend `B` for bit vectors
165/// and vectors of bit fields satisfies the bound
166///
167/// ```ignore
168/// B: Backend<Word: Word> + AsRef<[B::Word]>
169/// ```
170///
171/// whereas an analogous atomic backend satisfies
172///
173/// ```ignore
174/// B: Backend<Word: PrimitiveAtomicUnsigned<Value: Word>> + AsRef<[B::Word]>
175/// ```
176///
177/// Bit-based backends satisfy also the [`BitLength`] trait, which specifies the
178/// number of valid bits in the backend. For example, [`BitVec`]'s only
179/// parameter is a word-based backend, but [`BitVec`] itself is a bit-based
180/// backend, and thus it implements [`BitLength`] and delegates [`Backend`],
181/// [`AsRef`] and [`AsMut`] to its word-based backend.
182///
183/// Note that *traits* manipulating backends such as [`BitVecOps`] do not use
184/// this trait, but are rather parametrized by a word type `W`, and extend
185/// traits such as [`AsRef<[W]>`](core::convert::AsRef) as needed.
186///
187/// However, *types* with a backend need this trait to avoid a redundant
188/// specification of the word type in isolation and as part of the backend. If
189/// we did not have this trait to specify the word type, we would need to write
190/// something like `BitVec<u64, Vec<u64>>`, because there is no way to infer `W`
191/// from `Vec<W>` when `Vec<W>` is an opaque type parameter, and in an `impl`
192/// block a syntax like `impl<W, B: AsRef<[W]>>` will not compile unless the
193/// type (or the implemented trait) contains `W`.
194///
195/// This trait is delegated by every [rank/select structure] to its backend (an
196/// inner field) together with [`AsRef`] and [`BitLength`] so that, for
197/// example, a rank/select structure can be used as a backend for another
198/// structure without any boilerplate.
199///
200/// We implement this trait for slices, vectors, and arrays; moreover, we
201/// delegate it automatically to references, boxed types, and reference-counted
202/// wrappers.
203///
204/// [rank/select structure]: crate::rank_sel
205/// [`AsMut`]: core::convert::AsMut
206/// [`Backend::Word`]: Self::Word
207/// [`BitVec<Vec<usize>>`]: BitVec
208/// [`Word`]: crate::traits::Word
209/// [`BitLength`]: crate::traits::BitLength
210/// [`BitVec`]: BitVec
211/// [`BitVecOps`]: crate::traits::BitVecOps
212/// [`PrimitiveAtomicUnsigned`]: PrimitiveAtomicUnsigned
213#[autoimpl(for<T: trait + ?Sized> &T, &mut T, Box<T>, Rc<T>, Arc<T>)]
214#[delegatable_trait]
215pub trait Backend {
216 /// The word type used by this backend.
217 type Word;
218}
219
220impl<W> Backend for [W] {
221 type Word = W;
222}
223
224impl<W> Backend for Vec<W> {
225 type Word = W;
226}
227
228impl<W, const N: usize> Backend for [W; N] {
229 type Word = W;
230}