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

vigna / sux-rs / 20064783806

09 Dec 2025 01:15PM UTC coverage: 74.646% (+0.05%) from 74.599%
20064783806

Pull #91

github

web-flow
Merge f4491a6b1 into f3bb2f3ad
Pull Request #91: Add PartialValueArray

67 of 87 new or added lines in 2 files covered. (77.01%)

18 existing lines in 1 file now uncovered.

4537 of 6078 relevant lines covered (74.65%)

120230887.83 hits per line

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

76.74
/src/array/partial_value_array.rs
1
/*
2
 * SPDX-FileCopyrightText: 2025 Inria
3
 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4
 *
5
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6
 */
7

8
//! Immutable partial array implementations for cheaply-clonable values
9
//!
10
//! This supports storing values in [`BitFieldVec`] to save memory.
11

12
use std::marker::PhantomData;
13

14
use mem_dbg::*;
15
use value_traits::slices::SliceByValue;
16

17
use crate::bits::BitFieldVec;
18
use crate::bits::BitVec;
19
use crate::dict::EliasFanoBuilder;
20
use crate::dict::elias_fano::EfDict;
21
use crate::rank_sel::Rank9;
22
use crate::traits::Word;
23
use crate::traits::{BitVecOps, BitVecOpsMut};
24
use crate::traits::{RankUnchecked, SuccUnchecked};
25

26
type DenseIndex = Rank9<BitVec<Box<[usize]>>>;
27

28
/// Builder for creating an immutable partial array.
29
///
30
/// The builder allows you to specify the array length and then add
31
/// (position, value) pairs. Positions must be added in strictly
32
/// increasing order.
33
///
34
/// To get a builder you can use either [new_dense] or [new_sparse].
35
#[derive(Debug, Clone, MemDbg, MemSize)]
36
pub struct PartialValueArrayBuilder<T, B> {
37
    builder: B,
38
    values: Vec<T>,
39
    len: usize,
40
    min_next_pos: usize,
41
}
42

43
/// Creates a new builder for a dense partial array of the given length.
44
///
45
/// A dense partial array stores a bit vector of the given length to mark
46
/// which positions contain values, and use ranking on this bit vector to
47
/// map positions to indices in a contiguous value array.
48
///
49
/// If your set of values is really sparse, consider using a
50
/// [sparse partial array](new_sparse).
51
///
52
/// # Examples
53
///
54
/// ```rust
55
/// use sux::array::partial_value_array;
56
///
57
/// let mut builder = partial_value_array::new_dense(10);
58
/// builder.set(1, 123u32);
59
/// builder.set(2, 45678);
60
/// builder.set(7, 90);
61
///
62
/// let array = builder.build_bitfieldvec();
63
/// assert_eq!(array.get(1), Some(123));
64
/// assert_eq!(array.get(2), Some(45678));
65
/// assert_eq!(array.get(3), None);
66
/// assert_eq!(array.get(7), Some(90));
67
/// ```
68
pub fn new_dense<T>(len: usize) -> PartialValueArrayBuilder<T, BitVec<Box<[usize]>>> {
5✔
69
    PartialValueArrayBuilder {
70
        builder: BitVec::new(len).into(),
20✔
71
        values: vec![],
5✔
72
        len,
73
        min_next_pos: 0,
74
    }
75
}
76

77
fn build_bitfieldvec<T: Word>(values: Vec<T>) -> BitFieldVec<T> {
8✔
78
    let bit_width = values
16✔
79
        .iter()
80
        .map(|value| value.len())
24✔
81
        .max()
82
        .unwrap_or(1)
83
        .try_into()
84
        .expect("bit_width overflowed usize");
85
    let mut bfv = BitFieldVec::with_capacity(bit_width, values.len());
40✔
86
    for value in values {
24✔
87
        bfv.push(value);
16✔
88
    }
89
    bfv
8✔
90
}
91

92
impl<T: Clone> PartialValueArrayBuilder<T, BitVec<Box<[usize]>>> {
93
    /// Sets a value at the given position.
94
    ///
95
    /// The provided position must be greater than the last position set.
96
    pub fn set(&mut self, position: usize, value: T) {
6✔
97
        if position < self.min_next_pos {
6✔
98
            panic!(
1✔
NEW
99
                "Positions must be set in increasing order: got {} after {}",
×
NEW
100
                position,
×
101
                self.min_next_pos - 1
1✔
102
            );
103
        }
104
        if position >= self.len {
5✔
NEW
105
            panic!(
×
NEW
106
                "Position {} is out of bounds for array of len {}",
×
NEW
107
                position, self.len
×
108
            );
109
        }
110
        // SAFETY: position < len
111
        unsafe {
112
            self.builder.set_unchecked(position, true);
10✔
113
        }
114
        self.values.push(value);
15✔
115
        self.min_next_pos = position + 1;
5✔
116
    }
117

118
    /// Builds the immutable dense partial array using [`BitFieldVec`] as backend.
119
    pub fn build_bitfieldvec(self) -> PartialValueArray<T, DenseIndex, BitFieldVec<T>>
4✔
120
    where
121
        T: Word,
122
    {
123
        let (bit_vec, values) = (self.builder, self.values);
12✔
124
        let rank9 = Rank9::new(bit_vec);
12✔
125
        let values = build_bitfieldvec(values);
12✔
126

127
        PartialValueArray {
128
            index: rank9,
129
            values,
130
            _marker: PhantomData,
131
        }
132
    }
133
}
134

135
/// Creates a new builder for a sparse partial array of the given length.
136
///
137
/// A sparse partial array stores the non-empty positions of the array in an
138
/// [Elias-Fano](crate::dict::EliasFano) structure.
139
///
140
/// If your set of values is really dense, consider using a [dense partial
141
/// array](new_dense).
142
///
143
/// # Examples
144
///
145
/// ```rust
146
/// use sux::array::partial_value_array;
147
///
148
/// let mut builder = partial_value_array::new_sparse(10, 3);
149
/// builder.set(1, 123u32);
150
/// builder.set(2, 45678);
151
/// builder.set(7, 90);
152
///
153
/// let array = builder.build_bitfieldvec();
154
/// assert_eq!(array.get(1), Some(123));
155
/// assert_eq!(array.get(2), Some(45678));
156
/// assert_eq!(array.get(3), None);
157
/// assert_eq!(array.get(7), Some(90));
158
/// ```
159
///
160
/// Note that you must specify the number of values in advance.
161
pub fn new_sparse<T>(
5✔
162
    len: usize,
163
    num_values: usize,
164
) -> PartialValueArrayBuilder<T, EliasFanoBuilder> {
165
    PartialValueArrayBuilder {
166
        builder: EliasFanoBuilder::new(num_values, len),
20✔
167
        values: vec![],
5✔
168
        len,
169
        min_next_pos: 0,
170
    }
171
}
172

173
impl<T: Clone> PartialValueArrayBuilder<T, EliasFanoBuilder> {
174
    /// Sets a value at the given position.
175
    ///
176
    /// The provided position must be greater than the last position
177
    /// set.
178
    pub fn set(&mut self, position: usize, value: T) {
6✔
179
        if position < self.min_next_pos {
6✔
180
            panic!(
1✔
NEW
181
                "Positions must be set in increasing order: got {} after {}",
×
NEW
182
                position,
×
183
                self.min_next_pos - 1
1✔
184
            );
185
        }
186
        if position >= self.len {
5✔
NEW
187
            panic!(
×
NEW
188
                "Position {} is out of bounds for array of len {}",
×
NEW
189
                position, self.len
×
190
            );
191
        }
192
        // SAFETY: conditions have been just checked.
193
        unsafe { self.builder.push_unchecked(position) };
15✔
194
        self.values.push(value);
15✔
195
        self.min_next_pos = position + 1;
5✔
196
    }
197

198
    /// Builds the immutable sparse partial array using [`BitFieldVec`] as backend.
199
    pub fn build_bitfieldvec(self) -> PartialValueArray<T, (EfDict, usize), BitFieldVec<T>>
4✔
200
    where
201
        T: Word,
202
    {
203
        let (builder, values) = (self.builder, self.values);
12✔
204
        let ef_dict = builder.build_with_dict();
12✔
205
        let values = build_bitfieldvec(values);
12✔
206

207
        PartialValueArray {
208
            index: (ef_dict, self.min_next_pos),
8✔
209
            values,
210
            _marker: PhantomData,
211
        }
212
    }
213
}
214

215
/// Extends the builder with an iterator of (position, value) pairs.
216
///
217
/// Position must be in strictly increasing order. The first returned
218
/// position must be greater than the last position set.
219
impl<T: Clone> Extend<(usize, T)> for PartialValueArrayBuilder<T, BitVec<Box<[usize]>>> {
NEW
220
    fn extend<I: IntoIterator<Item = (usize, T)>>(&mut self, iter: I) {
×
NEW
221
        for (pos, val) in iter {
×
NEW
222
            self.set(pos, val);
×
223
        }
224
    }
225
}
226

227
/// Extends the builder with an iterator of (position, value) pairs.
228
///
229
/// Position must be in strictly increasing order. The first returned
230
/// position must be greater than the last position set.
231
impl<T: Clone> Extend<(usize, T)> for PartialValueArrayBuilder<T, EliasFanoBuilder> {
NEW
232
    fn extend<I: IntoIterator<Item = (usize, T)>>(&mut self, iter: I) {
×
NEW
233
        for (pos, val) in iter {
×
NEW
234
            self.set(pos, val);
×
235
        }
236
    }
237
}
238

239
/// An immutable partial array that supports efficient queries
240
/// in compacted storage.
241
///
242
/// This structure stores a *partial array*—an array in which only
243
/// some positions contain values. There is a [dense](new_dense)
244
/// and a [sparse](new_sparse) implementation with different
245
/// space/time trade-offs.
246
///
247
/// See [`PartialValueArrayBuilder`] for details on how to create a partial array.
248
#[derive(Debug, Clone, MemDbg, MemSize)]
249
#[cfg_attr(feature = "epserde", derive(epserde::Epserde))]
250
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
251
pub struct PartialValueArray<T: Clone, P, D: SliceByValue<Value = T>> {
252
    index: P,
253
    values: D,
254
    _marker: PhantomData<T>,
255
}
256

257
impl<T: Clone, P, D: SliceByValue<Value = T>> PartialValueArray<T, P, D> {
258
    /// Returns the number of values stored in the array.
259
    #[inline(always)]
260
    pub fn num_values(&self) -> usize {
6✔
261
        self.values.len()
12✔
262
    }
263
}
264

265
impl<T: Clone, D: SliceByValue<Value = T>> PartialValueArray<T, DenseIndex, D> {
266
    /// Returns the total length of the array.
267
    ///
268
    /// This is the length that was specified when creating the builder,
269
    /// not the number of values actually stored.
270
    #[inline(always)]
271
    pub fn len(&self) -> usize {
1,012✔
272
        self.index.len()
2,024✔
273
    }
274

275
    /// Returns true if the array length is 0.
276
    #[inline(always)]
277
    pub fn is_empty(&self) -> bool {
1✔
278
        self.len() == 0
1✔
279
    }
280

281
    /// Gets a reference to the value at the given position.
282
    ///
283
    /// Returns `Some(&value)` if a value is present at the position,
284
    /// or `None` if no value was stored there.
285
    ///
286
    /// # Examples
287
    ///
288
    /// ```rust
289
    /// use sux::array::partial_value_array;
290
    /// let mut builder = partial_value_array::new_dense(10);
291
    /// builder.set(5, 42u32);
292
    ///
293
    /// let array = builder.build_bitfieldvec();
294
    /// assert_eq!(array.get(5), Some(42));
295
    /// assert_eq!(array.get(6), None);
296
    /// ```
297
    pub fn get(&self, position: usize) -> Option<T> {
1,011✔
298
        if position >= self.len() {
2,022✔
299
            panic!(
1✔
NEW
300
                "Position {} is out of bounds for array of len {}",
×
NEW
301
                position,
×
302
                self.len()
2✔
303
            );
304
        }
305
        // Check if there's a value at this position
306
        // SAFETY: position < len()
307
        if !unsafe { self.index.get_unchecked(position) } {
2,020✔
308
            return None;
1,006✔
309
        }
310

311
        // Use ranking to find the index in the values array
312
        // SAFETY: position < len()
313
        let value_index = unsafe { self.index.rank_unchecked(position) };
16✔
314

315
        // SAFETY: necessarily value_index < num_values().
316
        Some(unsafe { self.values.get_value_unchecked(value_index) })
8✔
317
    }
318
}
319

320
impl<T: Clone, D: SliceByValue<Value = T>> PartialValueArray<T, (EfDict, usize), D> {
321
    /// Returns the total length of the array.
322
    ///
323
    /// This is the length that was specified when creating the builder,
324
    /// not the number of values actually stored.
325
    #[inline(always)]
326
    pub fn len(&self) -> usize {
503✔
327
        self.index.0.upper_bound()
1,006✔
328
    }
329

330
    /// Returns true if the array len is 0.
331
    #[inline(always)]
332
    pub fn is_empty(&self) -> bool {
1✔
333
        self.index.0.len() == 0
1✔
334
    }
335

336
    /// Gets a reference to the value at the given position.
337
    ///
338
    /// Returns `Some(&value)` if a value is present at the position,
339
    /// or `None` if no value was stored there.
340
    ///
341
    /// # Examples
342
    ///
343
    /// ```rust
344
    /// use sux::array::partial_value_array;
345
    /// let mut builder = partial_value_array::new_sparse(10, 1);
346
    /// builder.set(5, 42u32);
347
    ///
348
    /// let array = builder.build_bitfieldvec();
349
    /// assert_eq!(array.get(5), Some(42));
350
    /// assert_eq!(array.get(6), None);
351
    /// ```
352
    pub fn get(&self, position: usize) -> Option<T> {
1,011✔
353
        if position >= self.index.1 {
1,011✔
354
            if position >= self.len() {
1,004✔
355
                panic!(
1✔
NEW
356
                    "Position {} is out of bounds for array of len {}",
×
NEW
357
                    position,
×
358
                    self.len()
2✔
359
                );
360
            }
361
            return None;
501✔
362
        }
363
        // Check if there's a value at this position
364
        // SAFETY: position <= last set position
365
        let (index, pos) = unsafe { self.index.0.succ_unchecked::<false>(position) };
2,036✔
366

367
        if pos != position {
509✔
368
            None
505✔
369
        } else {
370
            // SAFETY: necessarily value_index < num values.
371
            Some(unsafe { self.values.get_value_unchecked(index) })
8✔
372
        }
373
    }
374
}
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