• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vigna / sux-rs / 24097483404

07 Apr 2026 06:22PM UTC coverage: 71.015% (-1.4%) from 72.384%
24097483404

push

github

vigna
Serialization for lists

6924 of 9750 relevant lines covered (71.02%)

82523075.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.26
/src/func/vfunc.rs
1
/*
2
 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
3
 *
4
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
5
 */
6

7
use crate::traits::Backend;
8
use value_traits::slices::SliceByValue;
9

10
#[cfg(feature = "rayon")]
11
use {
12
    crate::func::VBuilder,
13
    crate::traits::bit_field_slice::BitFieldSliceMut,
14
    anyhow::Result,
15
    core::error::Error,
16
    dsi_progress_logger::ProgressLog,
17
    lender::*,
18
    rdst::RadixKey,
19
    std::ops::{BitXor, BitXorAssign},
20
    value_traits::slices::SliceByValueMut,
21
};
22

23
use super::shard_edge::FuseLge3Shards;
24
use crate::bits::BitFieldVec;
25
use crate::func::shard_edge::ShardEdge;
26
use crate::traits::Word;
27
use crate::utils::*;
28
use mem_dbg::*;
29
use std::borrow::Borrow;
30

31
/// Static functions with low space overhead, fast parallel construction, and
32
/// fast queries.
33
///
34
/// *Static functions* map keys to values, but they do not store the keys:
35
/// querying a static function with a key outside of the original set will lead
36
/// to an arbitrary result. Another name for static functions is *retrieval data
37
/// structure*. Values are retrieved using the [`get`](VFunc::get) method.
38
///
39
/// In exchange, static functions have a very low space overhead, and make it
40
/// possible to store the association between keys and values just in the space
41
/// required by the values (with a small overhead).
42
///
43
/// This structure is based on “[ε-Cost Sharding: Scaling Hypergraph-Based
44
/// Static Functions and Filters to Trillions of
45
/// Keys](https://arxiv.org/abs/2503.18397)”. Space overhead with respect to the
46
/// optimum depends on the [`ShardEdge`] type. The default is
47
/// [`FuseLge3Shards`], which provides 10.5% space overhead for large key sets
48
/// (above a few million keys), which grow up to 12% going towards smaller key
49
/// sets. Details on other possible [`ShardEdge`] implementations can be found
50
/// in the [`shard_edge`](crate::func::shard_edge) module documentation.
51
///
52
/// Instances of this structure are immutable; they are built
53
/// using a [`VBuilder`](crate::func::VBuilder) and can be serialized using
54
/// [ε-serde](https://crates.io/crates/epserde). Please see the documentation of
55
/// [`VBuilder`](crate::func::VBuilder) for examples.
56
///
57
/// This structure implements the [`TryIntoUnaligned`] trait, allowing it to be
58
/// converted into (usually faster) structures using unaligned access.
59
///
60
/// # Generics
61
///
62
/// * `T`: The type of the keys.
63
/// * `W`: The word used to store the data, which is also the output type. It
64
///   can be any unsigned type.
65
/// * `D`: The backend storing the function data. It can be a
66
///   [`BitFieldVec<Box<[W]>>`](crate::bits::BitFieldVec) or a `Box<[W]>`. In the first
67
///   case, the data is stored using exactly the number of bits needed, but
68
///   access is slightly slower, while in the second case the data is stored in
69
///   a boxed slice of `W`, thus forcing the number of bits to the number of
70
///   bits of `W`, but access will be faster. Note that for most bit sizes in
71
///   the first case on some architectures you can use
72
///   [`TryIntoUnaligned`] to convert the function into one using [unaligned
73
///   reads](BitFieldVec::get_unaligned) for faster queries.
74
/// * `S`: The signature type. The default is `[u64; 2]`. You can switch to
75
///   `[u64; 1]` (and possibly
76
///   [`FuseLge3NoShards`](crate::func::shard_edge::FuseLge3NoShards)) for
77
///   slightly faster construction and queries, but the construction will not
78
///   scale beyond 3.8 billion keys.
79
/// * `E`: The sharding and edge logic type. The default is [`FuseLge3Shards`].
80
///   For small sets of keys you might try
81
///   [`FuseLge3NoShards`](crate::func::shard_edge::FuseLge3NoShards), possibly
82
///   coupled with `[u64; 1]` signatures. For functions with more than a few
83
///   dozen billion keys, you might try
84
///   [`FuseLge3FullSigs`](crate::func::shard_edge::FuseLge3FullSigs).
85
#[derive(Debug, MemDbg, MemSize)]
86
#[cfg_attr(feature = "epserde", derive(epserde::Epserde))]
87
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
88
pub struct VFunc<T: ?Sized, D, S = [u64; 2], E = FuseLge3Shards> {
89
    pub(crate) shard_edge: E,
90
    pub(crate) seed: u64,
91
    pub(crate) num_keys: usize,
92
    pub(crate) data: D,
93
    pub(crate) _marker: std::marker::PhantomData<(*const T, S)>,
94
}
95

96
impl<T: ?Sized, D: SliceByValue, S, E> Backend for VFunc<T, D, S, E> {
97
    type Word = D::Value;
98
}
99

100
impl<T: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>> VFunc<T, BitFieldVec<Box<[W]>>, S, E> {
101
    /// Creates a VFunc with zero keys.
102
    ///
103
    /// The internal data has bit width 0, so `get` always returns zero.
104
    /// This is safe because [`BitFieldVec::new(0, 0)`] allocates one
105
    /// word and `get_value_unchecked` with bit width 0 always reads
106
    /// index 0 and masks with 0.
107
    pub fn empty() -> Self {
22✔
108
        Self {
109
            shard_edge: E::default(),
44✔
110
            seed: 0,
111
            num_keys: 0,
112
            data: BitFieldVec::<Vec<W>>::new(0, 0).into(),
44✔
113
            _marker: std::marker::PhantomData,
114
        }
115
    }
116
}
117

118
impl<T: ?Sized + ToSig<S>, D: SliceByValue<Value: Word + BinSafe>, S: Sig, E: ShardEdge<S, 3>>
119
    VFunc<T, D, S, E>
120
{
121
    /// Returns the value associated with the given signature, or a random value
122
    /// if the signature is not the signature of a key.
123
    ///
124
    /// This method is mainly useful in the construction of compound functions.
125
    #[inline]
126
    pub fn get_by_sig(&self, sig: S) -> D::Value {
31,843,870✔
127
        let edge = self.shard_edge.edge(sig);
127,375,480✔
128
        // SAFETY: The ShardEdge implementation guarantees that all indices
129
        // returned by `edge()` are within bounds of `self.data`. This invariant
130
        // is established during construction by VBuilder, which ensures the
131
        // data array is sized according to the ShardEdge's `num_vertices()`
132
        unsafe {
133
            self.data.get_value_unchecked(edge[0])
159,219,350✔
134
                ^ self.data.get_value_unchecked(edge[1])
95,531,610✔
135
                ^ self.data.get_value_unchecked(edge[2])
63,687,740✔
136
        }
137
    }
138

139
    /// Returns the value associated with the given key, or a random value if the
140
    /// key is not present.
141
    #[inline(always)]
142
    pub fn get(&self, key: impl Borrow<T>) -> D::Value {
14,820,910✔
143
        self.get_by_sig(T::to_sig(key.borrow(), self.seed))
88,925,460✔
144
    }
145

146
    /// Returns the number of keys in the function.
147
    pub const fn len(&self) -> usize {
2✔
148
        self.num_keys
2✔
149
    }
150

151
    /// Returns whether the function has no keys.
152
    pub const fn is_empty(&self) -> bool {
×
153
        self.num_keys == 0
×
154
    }
155
}
156

157
// ── Aligned ↔ Unaligned conversions ─────────────────────────────────
158

159
use crate::bits::BitFieldVecU;
160
use crate::traits::TryIntoUnaligned;
161

162
impl<T: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>> TryIntoUnaligned
163
    for VFunc<T, BitFieldVec<Box<[W]>>, S, E>
164
{
165
    type Unaligned = VFunc<T, BitFieldVecU<Box<[W]>>, S, E>;
166
    fn try_into_unaligned(
162✔
167
        self,
168
    ) -> Result<Self::Unaligned, crate::traits::UnalignedConversionError> {
169
        Ok(VFunc {
×
170
            shard_edge: self.shard_edge,
162✔
171
            seed: self.seed,
162✔
172
            num_keys: self.num_keys,
162✔
173
            data: self.data.try_into_unaligned()?,
324✔
174
            _marker: std::marker::PhantomData,
162✔
175
        })
176
    }
177
}
178

179
impl<T: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>> TryIntoUnaligned for VFunc<T, Box<[W]>, S, E> {
180
    type Unaligned = VFunc<T, Box<[W]>, S, E>;
181
    fn try_into_unaligned(
×
182
        self,
183
    ) -> Result<Self::Unaligned, crate::traits::UnalignedConversionError> {
184
        Ok(self)
×
185
    }
186
}
187

188
impl<T: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>> From<VFunc<T, BitFieldVecU<Box<[W]>>, S, E>>
189
    for VFunc<T, BitFieldVec<Box<[W]>>, S, E>
190
{
191
    /// Converts a [`VFunc`] with [`BitFieldVecU`] data back into
192
    /// one with [`BitFieldVec`] data.
193
    fn from(vf: VFunc<T, BitFieldVecU<Box<[W]>>, S, E>) -> Self {
×
194
        VFunc {
195
            shard_edge: vf.shard_edge,
×
196
            seed: vf.seed,
×
197
            num_keys: vf.num_keys,
×
198
            data: BitFieldVec::from(vf.data),
×
199
            _marker: std::marker::PhantomData,
200
        }
201
    }
202
}
203

204
// ── Convenience constructors ───────────────────────────────────────
205

206
#[cfg(feature = "rayon")]
207
impl<T, W, S, E> VFunc<T, Box<[W]>, S, E>
208
where
209
    T: ?Sized + ToSig<S> + std::fmt::Debug,
210
    W: Word + BinSafe,
211
    S: Sig + Send + Sync,
212
    E: ShardEdge<S, 3>,
213
    SigVal<S, W>: RadixKey,
214
    SigVal<E::LocalSig, W>: BitXor + BitXorAssign,
215
{
216
    /// Builds a [`VFunc`] with a `Box<[W]>` backend from keys and values
217
    /// using default [`VBuilder`] settings.
218
    ///
219
    /// * `keys` and `values` must be aligned (one value per key, same
220
    ///   order) and rewindable (they may be rewound on retry).
221
    /// * `n` is the expected number of keys; a significantly wrong
222
    ///   value may degrade performance or cause extra retries.
223
    ///
224
    /// This is a convenience wrapper around
225
    /// [`try_new_with_builder`](Self::try_new_with_builder) with
226
    /// `VBuilder::default()`.
227
    ///
228
    /// # Examples
229
    ///
230
    ///
231
    ///
232
    ///
233
    /// If keys and values are available as slices, [`try_par_new`](Self::try_par_new)
234
    /// parallelizes the hash computation for faster construction.
235
    /// If keys and values are available as slices, [`try_par_new`](Self::try_par_new)
236
    /// parallelizes the hash computation for faster construction.
237
    /// ```rust
238
    /// # #[cfg(feature = "rayon")]
239
    /// # fn main() -> anyhow::Result<()> {
240
    /// # use sux::func::VFunc;
241
    /// # use sux::traits::TryIntoUnaligned;
242
    /// # use dsi_progress_logger::no_logging;
243
    /// # use sux::utils::FromCloneableIntoIterator;
244
    /// let func = <VFunc<usize, Box<[u8]>>>::try_new(
245
    ///     FromCloneableIntoIterator::new(0..100),
246
    ///     FromCloneableIntoIterator::new(0..100_u8),
247
    ///     100,
248
    ///     no_logging![],
249
    /// )?.try_into_unaligned()?;
250
    ///
251
    /// for i in 0..100_u8 {
252
    ///     assert_eq!(i, func.get(&(i as usize)));
253
    /// }
254
    /// # Ok(())
255
    /// # }
256
    /// # #[cfg(not(feature = "rayon"))]
257
    /// # fn main() {}
258
    /// ```
259
    pub fn try_new<B: ?Sized + Borrow<T>>(
×
260
        keys: impl FallibleRewindableLender<
261
            RewindError: Error + Send + Sync + 'static,
262
            Error: Error + Send + Sync + 'static,
263
        > + for<'lend> FallibleLending<'lend, Lend = &'lend B>,
264
        values: impl FallibleRewindableLender<
265
            RewindError: Error + Send + Sync + 'static,
266
            Error: Error + Send + Sync + 'static,
267
        > + for<'lend> FallibleLending<'lend, Lend = &'lend W>,
268
        n: usize,
269
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
270
    ) -> Result<Self>
271
    where
272
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: BitFieldSliceMut,
273
        for<'a> <Box<[W]> as SliceByValueMut>::ChunksMut<'a>: Send,
274
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: Send,
275
    {
276
        Self::try_new_with_builder(keys, values, n, VBuilder::default(), pl)
×
277
    }
278

279
    /// Builds a [`VFunc`] with a `Box<[W]>` backend from keys and values
280
    /// using the given [`VBuilder`] configuration.
281
    ///
282
    /// * `keys` and `values` must be aligned (one value per key, same
283
    ///   order) and rewindable (they may be rewound on retry).
284
    /// * `n` is the expected number of keys.
285
    ///
286
    /// The builder controls construction parameters such as [offline
287
    /// mode](VBuilder::offline), [thread count](VBuilder::max_num_threads),
288
    /// [sharding overhead](VBuilder::eps), and [PRNG seed](VBuilder::seed).
289
    ///
290
    /// # Examples
291
    ///
292
    ///
293
    ///
294
    ///
295
    /// See also [`try_par_new_with_builder`](Self::try_par_new_with_builder)
296
    /// for parallel hash computation from slices.
297
    /// See also [`try_par_new_with_builder`](Self::try_par_new_with_builder)
298
    /// for parallel hash computation from slices.
299
    /// for parallel hash computation from slices.
300
    /// ```rust
301
    /// # #[cfg(feature = "rayon")]
302
    /// # fn main() -> anyhow::Result<()> {
303
    /// # use sux::func::{VBuilder, VFunc};
304
    /// # use sux::traits::TryIntoUnaligned;
305
    /// # use dsi_progress_logger::no_logging;
306
    /// # use sux::utils::FromCloneableIntoIterator;
307
    /// let func = <VFunc<usize, Box<[u8]>>>::try_new_with_builder(
308
    ///     FromCloneableIntoIterator::new(0..100),
309
    ///     FromCloneableIntoIterator::new(0..100_u8),
310
    ///     100,
311
    ///     VBuilder::default().offline(true),
312
    ///     no_logging![],
313
    /// )?.try_into_unaligned()?;
314
    ///
315
    /// for i in 0..100_u8 {
316
    ///     assert_eq!(i, func.get(&(i as usize)));
317
    /// }
318
    /// # Ok(())
319
    /// # }
320
    /// # #[cfg(not(feature = "rayon"))]
321
    /// # fn main() {}
322
    /// ```
323
    pub fn try_new_with_builder<B: ?Sized + Borrow<T>>(
×
324
        keys: impl FallibleRewindableLender<
325
            RewindError: Error + Send + Sync + 'static,
326
            Error: Error + Send + Sync + 'static,
327
        > + for<'lend> FallibleLending<'lend, Lend = &'lend B>,
328
        values: impl FallibleRewindableLender<
329
            RewindError: Error + Send + Sync + 'static,
330
            Error: Error + Send + Sync + 'static,
331
        > + for<'lend> FallibleLending<'lend, Lend = &'lend W>,
332
        n: usize,
333
        builder: VBuilder<Box<[W]>, S, E>,
334
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
335
    ) -> Result<Self>
336
    where
337
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: BitFieldSliceMut,
338
        for<'a> <Box<[W]> as SliceByValueMut>::ChunksMut<'a>: Send,
339
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: Send,
340
    {
341
        Ok(builder
×
342
            .expected_num_keys(n)
×
343
            .try_build_func(
×
344
                keys,
×
345
                values,
×
346
                |_bit_width, len| vec![W::ZERO; len].into(),
×
347
                pl,
×
348
            )?
349
            .0)
×
350
    }
351

352
    /// Builds a [`VFunc`] with a `Box<[W]>` backend from in-memory key
353
    /// and value slices, parallelizing hash computation and store
354
    /// population with rayon, using default [`VBuilder`] settings.
355
    ///
356
    /// Each key is hashed on a rayon worker thread and deposited directly
357
    /// into its SigStore bucket. This is faster than
358
    /// [`try_new`](Self::try_new) for large in-memory key sets.
359
    ///
360
    /// This is a convenience wrapper around
361
    /// [`try_par_new_with_builder`](Self::try_par_new_with_builder)
362
    /// with `VBuilder::default()`.
363
    ///
364
    /// # Examples
365
    ///
366
    ///
367
    ///
368
    ///
369
    /// If keys are produced sequentially (e.g., from a file), use
370
    /// [`try_new`](Self::try_new) instead.
371
    /// If keys are produced sequentially (e.g., from a file), use
372
    /// [`try_new`](Self::try_new) instead.
373
    /// [`try_new`](Self::try_new) instead.
374
    /// ```rust
375
    /// # #[cfg(feature = "rayon")]
376
    /// # fn main() -> anyhow::Result<()> {
377
    /// # use sux::func::VFunc;
378
    /// # use sux::traits::TryIntoUnaligned;
379
    /// # use dsi_progress_logger::no_logging;
380
    /// let keys: Vec<u64> = (0..1000).collect();
381
    /// let values: Vec<u8> = (0..1000).map(|x| x as u8).collect();
382
    /// let func =
383
    ///     <VFunc<u64, Box<[u8]>>>::try_par_new(&keys, &values, no_logging![])?.try_into_unaligned()?;
384
    /// for (i, &key) in keys.iter().enumerate() {
385
    ///     assert_eq!(func.get(key), i as u8);
386
    /// }
387
    /// # Ok(())
388
    /// # }
389
    /// # #[cfg(not(feature = "rayon"))]
390
    /// # fn main() {}
391
    /// ```
392
    pub fn try_par_new(
×
393
        keys: &[impl Borrow<T> + Sync],
394
        values: &[W],
395
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
396
    ) -> Result<Self>
397
    where
398
        T: Sync,
399
        S: Send,
400
        W: Copy,
401
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: BitFieldSliceMut,
402
        for<'a> <Box<[W]> as SliceByValueMut>::ChunksMut<'a>: Send,
403
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: Send,
404
    {
405
        Self::try_par_new_with_builder(keys, values, VBuilder::default(), pl)
×
406
    }
407

408
    /// Builds a [`VFunc`] with a `Box<[W]>` backend from in-memory key
409
    /// and value slices, parallelizing hash computation and store
410
    /// population with rayon, using the given [`VBuilder`] configuration.
411
    ///
412
    /// Each key is hashed on a rayon worker thread and deposited directly
413
    /// into its SigStore bucket. This is faster than
414
    /// [`try_new`](Self::try_new) for large in-memory key sets.
415
    ///
416
    /// The builder controls construction parameters such as [offline
417
    /// mode](VBuilder::offline), [thread count](VBuilder::max_num_threads),
418
    /// [sharding overhead](VBuilder::eps), and [PRNG seed](VBuilder::seed).
419
    ///
420
    /// # Examples
421
    ///
422
    ///
423
    ///
424
    ///
425
    /// If keys are produced sequentially (e.g., from a file), use
426
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
427
    /// If keys are produced sequentially (e.g., from a file), use
428
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
429
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
430
    /// ```rust
431
    /// # #[cfg(feature = "rayon")]
432
    /// # fn main() -> anyhow::Result<()> {
433
    /// # use sux::func::{VFunc, VBuilder};
434
    /// # use sux::traits::TryIntoUnaligned;
435
    /// # use dsi_progress_logger::no_logging;
436
    /// let keys: Vec<u64> = (0..1000).collect();
437
    /// let values: Vec<u8> = (0..1000).map(|x| x as u8).collect();
438
    /// let func =
439
    ///     <VFunc<u64, Box<[u8]>>>::try_par_new_with_builder(&keys, &values, VBuilder::default().offline(true), no_logging![])?.try_into_unaligned()?;
440
    /// for (i, &key) in keys.iter().enumerate() {
441
    ///     assert_eq!(func.get(key), i as u8);
442
    /// }
443
    /// # Ok(())
444
    /// # }
445
    /// # #[cfg(not(feature = "rayon"))]
446
    /// # fn main() {}
447
    /// ```
448
    pub fn try_par_new_with_builder(
×
449
        keys: &[impl Borrow<T> + Sync],
450
        values: &[W],
451
        builder: VBuilder<Box<[W]>, S, E>,
452
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
453
    ) -> Result<Self>
454
    where
455
        T: Sync,
456
        S: Send,
457
        W: Copy,
458
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: BitFieldSliceMut,
459
        for<'a> <Box<[W]> as SliceByValueMut>::ChunksMut<'a>: Send,
460
        for<'a> <<Box<[W]> as SliceByValueMut>::ChunksMut<'a> as Iterator>::Item: Send,
461
    {
462
        let n = keys.len();
×
463
        builder.expected_num_keys(n).try_par_populate_and_build(
×
464
            keys,
×
465
            &|i| values[i],
×
466
            &mut |builder, seed, mut store, _max_value, _num_keys, pl, _state: &mut ()| {
×
467
                let data: Box<[W]> = vec![
×
468
                    W::ZERO;
×
469
                    builder.shard_edge.num_vertices()
×
470
                        * builder.shard_edge.num_shards()
×
471
                ]
472
                .into();
×
473
                let func = builder.try_build_from_shard_iter(
×
474
                    seed,
×
475
                    data,
×
476
                    store.drain(),
×
477
                    &|_, sv| sv.val,
×
478
                    &|_| {},
×
479
                    pl,
×
480
                )?;
481
                Ok(func)
×
482
            },
483
            pl,
×
484
            (),
485
        )
486
    }
487
}
488

489
#[cfg(feature = "rayon")]
490
impl<T, W, S, E> VFunc<T, BitFieldVec<Box<[W]>>, S, E>
491
where
492
    T: ?Sized + ToSig<S> + std::fmt::Debug,
493
    W: Word + BinSafe,
494
    S: Sig + Send + Sync,
495
    E: ShardEdge<S, 3>,
496
    SigVal<S, W>: RadixKey,
497
    SigVal<E::LocalSig, W>: BitXor + BitXorAssign,
498
{
499
    /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from keys and
500
    /// values using default [`VBuilder`] settings.
501
    ///
502
    /// * `keys` and `values` must be aligned (one value per key, same
503
    ///   order) and rewindable (they may be rewound on retry).
504
    /// * `n` is the expected number of keys; a significantly wrong
505
    ///   value may degrade performance or cause extra retries.
506
    ///
507
    /// This is a convenience wrapper around
508
    /// [`try_new_with_builder`](Self::try_new_with_builder) with
509
    /// `VBuilder::default()`.
510
    ///
511
    /// # Examples
512
    ///
513
    ///
514
    ///
515
    ///
516
    /// If keys and values are available as slices, [`try_par_new`](Self::try_par_new)
517
    /// parallelizes the hash computation for faster construction.
518
    /// If keys and values are available as slices, [`try_par_new`](Self::try_par_new)
519
    /// parallelizes the hash computation for faster construction.
520
    /// ```rust
521
    /// # #[cfg(feature = "rayon")]
522
    /// # fn main() -> anyhow::Result<()> {
523
    /// # use sux::func::VFunc;
524
    /// # use sux::bits::BitFieldVec;
525
    /// # use sux::traits::TryIntoUnaligned;
526
    /// # use dsi_progress_logger::no_logging;
527
    /// # use sux::utils::FromCloneableIntoIterator;
528
    /// let func = <VFunc<usize, BitFieldVec<Box<[usize]>>>>::try_new(
529
    ///     FromCloneableIntoIterator::new(0..100),
530
    ///     FromCloneableIntoIterator::new(0..100),
531
    ///     100,
532
    ///     no_logging![],
533
    /// )?.try_into_unaligned()?;
534
    ///
535
    /// for i in 0..100 {
536
    ///     assert_eq!(i, func.get(&i));
537
    /// }
538
    /// # Ok(())
539
    /// # }
540
    /// # #[cfg(not(feature = "rayon"))]
541
    /// # fn main() {}
542
    /// ```
543
    pub fn try_new<B: ?Sized + Borrow<T>>(
×
544
        keys: impl FallibleRewindableLender<
545
            RewindError: Error + Send + Sync + 'static,
546
            Error: Error + Send + Sync + 'static,
547
        > + for<'lend> FallibleLending<'lend, Lend = &'lend B>,
548
        values: impl FallibleRewindableLender<
549
            RewindError: Error + Send + Sync + 'static,
550
            Error: Error + Send + Sync + 'static,
551
        > + for<'lend> FallibleLending<'lend, Lend = &'lend W>,
552
        n: usize,
553
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
554
    ) -> Result<Self> {
555
        Self::try_new_with_builder(keys, values, n, VBuilder::default(), pl)
×
556
    }
557

558
    /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from keys and
559
    /// values using the given [`VBuilder`] configuration.
560
    ///
561
    /// * `keys` and `values` must be aligned (one value per key, same
562
    ///   order) and rewindable (they may be rewound on retry).
563
    /// * `n` is the expected number of keys.
564
    ///
565
    /// The builder controls construction parameters such as [offline
566
    /// mode](VBuilder::offline), [thread count](VBuilder::max_num_threads),
567
    /// [sharding overhead](VBuilder::eps), and [PRNG seed](VBuilder::seed).
568
    ///
569
    /// # Examples
570
    ///
571
    ///
572
    ///
573
    ///
574
    /// See also [`try_par_new_with_builder`](Self::try_par_new_with_builder)
575
    /// for parallel hash computation from slices.
576
    /// See also [`try_par_new_with_builder`](Self::try_par_new_with_builder)
577
    /// for parallel hash computation from slices.
578
    /// for parallel hash computation from slices.
579
    /// ```rust
580
    /// # #[cfg(feature = "rayon")]
581
    /// # fn main() -> anyhow::Result<()> {
582
    /// # use sux::func::{VBuilder, VFunc};
583
    /// # use sux::bits::BitFieldVec;
584
    /// # use sux::traits::TryIntoUnaligned;
585
    /// # use dsi_progress_logger::no_logging;
586
    /// # use sux::utils::FromCloneableIntoIterator;
587
    /// let func = <VFunc<usize, BitFieldVec<Box<[usize]>>>>::try_new_with_builder(
588
    ///     FromCloneableIntoIterator::new(0..100),
589
    ///     FromCloneableIntoIterator::new(0..100),
590
    ///     100,
591
    ///     VBuilder::default().offline(true),
592
    ///     no_logging![],
593
    /// )?.try_into_unaligned()?;
594
    ///
595
    /// for i in 0..100 {
596
    ///     assert_eq!(i, func.get(&i));
597
    /// }
598
    /// # Ok(())
599
    /// # }
600
    /// # #[cfg(not(feature = "rayon"))]
601
    /// # fn main() {}
602
    /// ```
603
    pub fn try_new_with_builder<B: ?Sized + Borrow<T>>(
88✔
604
        keys: impl FallibleRewindableLender<
605
            RewindError: Error + Send + Sync + 'static,
606
            Error: Error + Send + Sync + 'static,
607
        > + for<'lend> FallibleLending<'lend, Lend = &'lend B>,
608
        values: impl FallibleRewindableLender<
609
            RewindError: Error + Send + Sync + 'static,
610
            Error: Error + Send + Sync + 'static,
611
        > + for<'lend> FallibleLending<'lend, Lend = &'lend W>,
612
        n: usize,
613
        builder: VBuilder<BitFieldVec<Box<[W]>>, S, E>,
614
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
615
    ) -> Result<Self> {
616
        builder
88✔
617
            .expected_num_keys(n)
176✔
618
            .try_build_func(
619
                keys,
88✔
620
                values,
88✔
621
                |bit_width, len| BitFieldVec::<Box<[W]>>::new_unaligned(bit_width, len),
429✔
622
                pl,
88✔
623
            )
624
            .map(|res| res.0)
88✔
625
    }
626

627
    /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from in-memory
628
    /// key and value slices, parallelizing hash computation and store
629
    /// population with rayon, using default [`VBuilder`] settings.
630
    ///
631
    /// Each key is hashed on a rayon worker thread and deposited directly
632
    /// into its SigStore bucket. This is faster than
633
    /// [`try_new`](Self::try_new) for large in-memory key sets.
634
    ///
635
    /// This is a convenience wrapper around
636
    /// [`try_par_new_with_builder`](Self::try_par_new_with_builder)
637
    /// with `VBuilder::default()`.
638
    ///
639
    /// # Examples
640
    ///
641
    ///
642
    ///
643
    ///
644
    /// If keys are produced sequentially (e.g., from a file), use
645
    /// [`try_new`](Self::try_new) instead.
646
    /// If keys are produced sequentially (e.g., from a file), use
647
    /// [`try_new`](Self::try_new) instead.
648
    /// [`try_new`](Self::try_new) instead.
649
    /// ```rust
650
    /// # #[cfg(feature = "rayon")]
651
    /// # fn main() -> anyhow::Result<()> {
652
    /// # use sux::func::VFunc;
653
    /// # use sux::bits::BitFieldVec;
654
    /// # use sux::traits::TryIntoUnaligned;
655
    /// # use dsi_progress_logger::no_logging;
656
    /// let keys: Vec<u64> = (0..1000).collect();
657
    /// let values: Vec<usize> = (0..1000).collect();
658
    /// let func =
659
    ///     <VFunc<u64, BitFieldVec<Box<[usize]>>>>::try_par_new(&keys, &values, no_logging![])?.try_into_unaligned()?;
660
    /// for (i, &key) in keys.iter().enumerate() {
661
    ///     assert_eq!(func.get(key), i);
662
    /// }
663
    /// # Ok(())
664
    /// # }
665
    /// # #[cfg(not(feature = "rayon"))]
666
    /// # fn main() {}
667
    /// ```
668
    pub fn try_par_new(
×
669
        keys: &[impl Borrow<T> + Sync],
670
        values: &[W],
671
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
672
    ) -> Result<Self>
673
    where
674
        T: Sync,
675
        S: Send,
676
        W: Copy,
677
    {
678
        Self::try_par_new_with_builder(keys, values, VBuilder::default(), pl)
×
679
    }
680

681
    /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from in-memory
682
    /// key and value slices, parallelizing hash computation and store
683
    /// population with rayon, using the given [`VBuilder`] configuration.
684
    ///
685
    /// Each key is hashed on a rayon worker thread and deposited directly
686
    /// into its SigStore bucket. This is faster than
687
    /// [`try_new`](Self::try_new) for large in-memory key sets.
688
    ///
689
    /// The builder controls construction parameters such as [offline
690
    /// mode](VBuilder::offline), [thread count](VBuilder::max_num_threads),
691
    /// [sharding overhead](VBuilder::eps), and [PRNG seed](VBuilder::seed).
692
    ///
693
    /// # Examples
694
    ///
695
    ///
696
    ///
697
    ///
698
    /// If keys are produced sequentially (e.g., from a file), use
699
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
700
    /// If keys are produced sequentially (e.g., from a file), use
701
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
702
    /// [`try_new_with_builder`](Self::try_new_with_builder) instead.
703
    /// ```rust
704
    /// # #[cfg(feature = "rayon")]
705
    /// # fn main() -> anyhow::Result<()> {
706
    /// # use sux::func::{VFunc, VBuilder};
707
    /// # use sux::bits::BitFieldVec;
708
    /// # use sux::traits::TryIntoUnaligned;
709
    /// # use dsi_progress_logger::no_logging;
710
    /// let keys: Vec<u64> = (0..1000).collect();
711
    /// let values: Vec<usize> = (0..1000).collect();
712
    /// let func =
713
    ///     <VFunc<u64, BitFieldVec<Box<[usize]>>>>::try_par_new_with_builder(
714
    ///         &keys, &values, VBuilder::default().offline(true), no_logging![],
715
    ///     )?.try_into_unaligned()?;
716
    /// for (i, &key) in keys.iter().enumerate() {
717
    ///     assert_eq!(func.get(key), i);
718
    /// }
719
    /// # Ok(())
720
    /// # }
721
    /// # #[cfg(not(feature = "rayon"))]
722
    /// # fn main() {}
723
    /// ```
724
    pub fn try_par_new_with_builder(
×
725
        keys: &[impl Borrow<T> + Sync],
726
        values: &[W],
727
        builder: VBuilder<BitFieldVec<Box<[W]>>, S, E>,
728
        pl: &mut (impl ProgressLog + Clone + Send + Sync),
729
    ) -> Result<Self>
730
    where
731
        T: Sync,
732
        S: Send,
733
        W: Copy,
734
    {
735
        let n = keys.len();
×
736
        builder.expected_num_keys(n).try_par_populate_and_build(
×
737
            keys,
×
738
            &|i| values[i],
×
739
            &mut |builder, seed, mut store, max_value, _num_keys, pl, _state: &mut ()| {
×
740
                builder.bit_width = max_value.bit_len() as usize;
×
741
                let data = BitFieldVec::<Box<[W]>>::new_unaligned(
×
742
                    builder.bit_width,
×
743
                    builder.shard_edge.num_vertices() * builder.shard_edge.num_shards(),
×
744
                );
745
                let func = builder.try_build_from_shard_iter(
×
746
                    seed,
×
747
                    data,
×
748
                    store.drain(),
×
749
                    &|_, sv| sv.val,
×
750
                    &|_| {},
×
751
                    pl,
×
752
                )?;
753
                Ok(func)
×
754
            },
755
            pl,
×
756
            (),
757
        )
758
    }
759
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc