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

vigna / sux-rs / 24281101826

11 Apr 2026 11:03AM UTC coverage: 71.146% (+0.1%) from 71.047%
24281101826

push

github

vigna
Completed inline link removal

7200 of 10120 relevant lines covered (71.15%)

17328249.11 hits per line

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

28.89
/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
use super::shard_edge::FuseLge3Shards;
11
use crate::bits::BitFieldVec;
12
use crate::func::shard_edge::ShardEdge;
13
use crate::traits::Word;
14
use crate::utils::*;
15
use mem_dbg::*;
16
use std::borrow::Borrow;
17

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

90
impl<K: ?Sized, D: SliceByValue, S, E> Backend for VFunc<K, D, S, E> {
91
    type Word = D::Value;
92
}
93

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

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

133
    /// Returns the value associated with the given key, or a random value if the
134
    /// key is not present.
135
    #[inline(always)]
136
    pub fn get(&self, key: impl Borrow<K>) -> D::Value {
14,820,910✔
137
        self.get_by_sig(K::to_sig(key.borrow(), self.seed))
88,925,460✔
138
    }
139

140
    /// Returns the number of keys in the function.
141
    pub const fn len(&self) -> usize {
2✔
142
        self.num_keys
2✔
143
    }
144

145
    /// Returns whether the function has no keys.
146
    pub const fn is_empty(&self) -> bool {
×
147
        self.num_keys == 0
×
148
    }
149
}
150

151
// ── Aligned ↔ Unaligned conversions ─────────────────────────────────
152

153
use crate::traits::{TryIntoUnaligned, Unaligned};
154

155
impl<K: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>> TryIntoUnaligned
156
    for VFunc<K, BitFieldVec<Box<[W]>>, S, E>
157
{
158
    type Unaligned = VFunc<K, Unaligned<BitFieldVec<Box<[W]>>>, S, E>;
159
    fn try_into_unaligned(
162✔
160
        self,
161
    ) -> Result<Self::Unaligned, crate::traits::UnalignedConversionError> {
162
        Ok(VFunc {
×
163
            shard_edge: self.shard_edge,
162✔
164
            seed: self.seed,
162✔
165
            num_keys: self.num_keys,
162✔
166
            data: self.data.try_into_unaligned()?,
324✔
167
            _marker: std::marker::PhantomData,
162✔
168
        })
169
    }
170
}
171

172
impl<K: ?Sized, W: Word, S: Sig, E: ShardEdge<S, 3>>
173
    From<Unaligned<VFunc<K, BitFieldVec<Box<[W]>>, S, E>>>
174
    for VFunc<K, BitFieldVec<Box<[W]>>, S, E>
175
{
176
    fn from(vf: Unaligned<VFunc<K, BitFieldVec<Box<[W]>>, S, E>>) -> Self {
×
177
        VFunc {
178
            shard_edge: vf.shard_edge,
×
179
            seed: vf.seed,
×
180
            num_keys: vf.num_keys,
×
181
            data: BitFieldVec::from(vf.data),
×
182
            _marker: std::marker::PhantomData,
183
        }
184
    }
185
}
186

187
// ── Constructors (require rayon) ──────────────────────────────────
188

189
#[cfg(feature = "rayon")]
190
mod build {
191
    use super::*;
192
    use crate::func::VBuilder;
193
    use crate::traits::bit_field_slice::BitFieldSliceMut;
194
    use anyhow::Result;
195
    use core::error::Error;
196
    use dsi_progress_logger::ProgressLog;
197
    use lender::*;
198
    use rdst::RadixKey;
199
    use std::ops::{BitXor, BitXorAssign};
200
    use value_traits::slices::SliceByValueMut;
201

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

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

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

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

488
    impl<K, W, S, E> VFunc<K, BitFieldVec<Box<[W]>>, S, E>
489
    where
490
        K: ?Sized + ToSig<S> + std::fmt::Debug,
491
        W: Word + BinSafe,
492
        S: Sig + Send + Sync,
493
        E: ShardEdge<S, 3>,
494
        SigVal<S, W>: RadixKey,
495
        SigVal<E::LocalSig, W>: BitXor + BitXorAssign,
496
    {
497
        /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from keys and
498
        /// values using default [`VBuilder`] settings.
499
        ///
500
        /// * `keys` and `values` must be provided as
501
        ///   [`FallibleRewindableLender`]s, aligned (one value per key,
502
        ///   same order). The [`lenders`] module provides easy ways to
503
        ///   build such lenders.
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`] with
509
        /// `VBuilder::default()`.
510
        ///
511
        /// If keys and values are available as slices, [`try_par_new`]
512
        /// parallelizes the hash computation for faster construction.
513
        ///
514
        /// # Examples
515
        ///
516
        /// ```rust
517
        /// # #[cfg(feature = "rayon")]
518
        /// # fn main() -> anyhow::Result<()> {
519
        /// # use sux::func::VFunc;
520
        /// # use sux::bits::BitFieldVec;
521
        /// # use dsi_progress_logger::no_logging;
522
        /// # use sux::utils::FromCloneableIntoIterator;
523
        /// let func = <VFunc<usize, BitFieldVec<Box<[usize]>>>>::try_new(
524
        ///     FromCloneableIntoIterator::new(0..100),
525
        ///     FromCloneableIntoIterator::new(0..100),
526
        ///     100,
527
        ///     no_logging![],
528
        /// )?;
529
        ///
530
        /// for i in 0..100 {
531
        ///     assert_eq!(i, func.get(&i));
532
        /// }
533
        /// # Ok(())
534
        /// # }
535
        /// # #[cfg(not(feature = "rayon"))]
536
        /// # fn main() {}
537
        /// ```
538
        ///
539
        /// [`try_new_with_builder`]: Self::try_new_with_builder
540
        /// [`try_par_new`]: Self::try_par_new
541
        pub fn try_new<B: ?Sized + Borrow<K>>(
×
542
            keys: impl FallibleRewindableLender<
543
                RewindError: Error + Send + Sync + 'static,
544
                Error: Error + Send + Sync + 'static,
545
            > + for<'lend> FallibleLending<'lend, Lend = &'lend B>,
546
            values: impl FallibleRewindableLender<
547
                RewindError: Error + Send + Sync + 'static,
548
                Error: Error + Send + Sync + 'static,
549
            > + for<'lend> FallibleLending<'lend, Lend = &'lend W>,
550
            n: usize,
551
            pl: &mut (impl ProgressLog + Clone + Send + Sync),
552
        ) -> Result<Self> {
553
            Self::try_new_with_builder(keys, values, n, VBuilder::default(), pl)
×
554
        }
555

556
        /// Builds a [`VFunc`] with a [`BitFieldVec`] backend from keys and
557
        /// values using the given [`VBuilder`] configuration.
558
        ///
559
        /// * `keys` and `values` must be provided as
560
        ///   [`FallibleRewindableLender`]s, aligned (one value per key,
561
        ///   same order). The [`lenders`] module provides easy ways to
562
        ///   build such lenders.
563
        /// * `n` is the expected number of keys.
564
        ///
565
        /// The builder controls construction parameters such as [offline
566
        /// mode], [thread count],
567
        /// [sharding overhead], and [PRNG seed].
568
        ///
569
        /// See also [`try_par_new_with_builder`]
570
        /// for parallel hash computation from slices.
571
        ///
572
        /// # Examples
573
        ///
574
        /// ```rust
575
        /// # #[cfg(feature = "rayon")]
576
        /// # fn main() -> anyhow::Result<()> {
577
        /// # use sux::func::{VBuilder, VFunc};
578
        /// # use sux::bits::BitFieldVec;
579
        /// # use dsi_progress_logger::no_logging;
580
        /// # use sux::utils::FromCloneableIntoIterator;
581
        /// let func = <VFunc<usize, BitFieldVec<Box<[usize]>>>>::try_new_with_builder(
582
        ///     FromCloneableIntoIterator::new(0..100),
583
        ///     FromCloneableIntoIterator::new(0..100),
584
        ///     100,
585
        ///     VBuilder::default().offline(true),
586
        ///     no_logging![],
587
        /// )?;
588
        ///
589
        /// for i in 0..100 {
590
        ///     assert_eq!(i, func.get(&i));
591
        /// }
592
        /// # Ok(())
593
        /// # }
594
        /// # #[cfg(not(feature = "rayon"))]
595
        /// # fn main() {}
596
        /// ```
597
        ///
598
        /// [offline mode]: VBuilder::offline
599
        /// [thread count]: VBuilder::max_num_threads
600
        /// [sharding overhead]: VBuilder::eps
601
        /// [PRNG seed]: VBuilder::seed
602
        /// [`try_par_new_with_builder`]: Self::try_par_new_with_builder
603
        pub fn try_new_with_builder<B: ?Sized + Borrow<K>>(
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_padded(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`] for large in-memory key sets.
634
        ///
635
        /// This is a convenience wrapper around
636
        /// [`try_par_new_with_builder`]
637
        /// with `VBuilder::default()`.
638
        ///
639
        /// If keys are produced sequentially (e.g., from a file), use
640
        /// [`try_new`] instead.
641
        ///
642
        /// # Examples
643
        ///
644
        /// ```rust
645
        /// # #[cfg(feature = "rayon")]
646
        /// # fn main() -> anyhow::Result<()> {
647
        /// # use sux::func::VFunc;
648
        /// # use sux::bits::BitFieldVec;
649
        /// # use dsi_progress_logger::no_logging;
650
        /// let keys: Vec<u64> = (0..1000).collect();
651
        /// let values: Vec<usize> = (0..1000).collect();
652
        /// let func =
653
        ///     <VFunc<u64, BitFieldVec<Box<[usize]>>>>::try_par_new(&keys, &values, no_logging![])?;
654
        /// for (i, &key) in keys.iter().enumerate() {
655
        ///     assert_eq!(func.get(key), i);
656
        /// }
657
        /// # Ok(())
658
        /// # }
659
        /// # #[cfg(not(feature = "rayon"))]
660
        /// # fn main() {}
661
        /// ```
662
        ///
663
        /// [`try_new`]: Self::try_new
664
        /// [`try_par_new_with_builder`]: Self::try_par_new_with_builder
665
        pub fn try_par_new(
×
666
            keys: &[impl Borrow<K> + Sync],
667
            values: &[W],
668
            pl: &mut (impl ProgressLog + Clone + Send + Sync),
669
        ) -> Result<Self>
670
        where
671
            K: Sync,
672
            S: Send,
673
            W: Copy,
674
        {
675
            Self::try_par_new_with_builder(keys, values, VBuilder::default(), pl)
×
676
        }
677

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