• 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

77.22
/src/array/partial_array.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
//! Immutable partial array implementations.
8

9
use crate::bits::BitVec;
10
use crate::dict::EliasFanoBuilder;
11
use crate::dict::elias_fano::EfDict;
12
use crate::rank_sel::Rank9;
13
use crate::traits::{BitVecOps, BitVecOpsMut};
14
use crate::traits::{RankUnchecked, SuccUnchecked};
15
use mem_dbg::*;
16

17
type DenseIndex = Rank9<BitVec<Box<[usize]>>>;
18

19
/// Builder for creating an immutable partial array.
20
///
21
/// The builder allows you to specify the array length and then add
22
/// (position, value) pairs. Positions must be added in strictly
23
/// increasing order.
24
///
25
/// To get a builder you can use either [new_dense] or [new_sparse].
26
#[derive(Debug, Clone, MemDbg, MemSize)]
27
pub struct PartialArrayBuilder<T, B> {
28
    builder: B,
29
    values: Vec<T>,
30
    len: usize,
31
    min_next_pos: usize,
32
}
33

34
/// Creates a new builder for a dense partial array of the given length.
35
///
36
/// A dense partial array stores a bit vector of the given length to mark
37
/// which positions contain values, and use ranking on this bit vector to
38
/// map positions to indices in a contiguous value array.
39
///
40
/// If your set of values is really sparse, consider using a
41
/// [sparse partial array](new_sparse).
42
///
43
/// # Examples
44
///
45
/// ```rust
46
/// use sux::array::partial_array;
47
///
48
/// let mut builder = partial_array::new_dense(10);
49
/// builder.set(1, "foo");
50
/// builder.set(2, "hello");
51
/// builder.set(7, "world");
52
///
53
/// let array = builder.build();
54
/// assert_eq!(array.get(1), Some(&"foo"));
55
/// assert_eq!(array.get(2), Some(&"hello"));
56
/// assert_eq!(array.get(3), None);
57
/// assert_eq!(array.get(7), Some(&"world"));
58
/// ```
59
pub fn new_dense<T>(len: usize) -> PartialArrayBuilder<T, BitVec<Box<[usize]>>> {
6✔
60
    PartialArrayBuilder {
61
        builder: BitVec::new(len).into(),
24✔
62
        values: vec![],
6✔
63
        len,
64
        min_next_pos: 0,
65
    }
66
}
67

68
impl<T> PartialArrayBuilder<T, BitVec<Box<[usize]>>> {
69
    /// Sets a value at the given position.
70
    ///
71
    /// The provided position must be greater than the last position set.
72
    pub fn set(&mut self, position: usize, value: T) {
7✔
73
        if position < self.min_next_pos {
7✔
74
            panic!(
1✔
UNCOV
75
                "Positions must be set in increasing order: got {} after {}",
×
UNCOV
76
                position,
×
77
                self.min_next_pos - 1
1✔
78
            );
79
        }
80
        if position >= self.len {
6✔
81
            panic!(
1✔
UNCOV
82
                "Position {} is out of bounds for array of len {}",
×
UNCOV
83
                position, self.len
×
84
            );
85
        }
86
        // SAFETY: position < len
87
        unsafe {
88
            self.builder.set_unchecked(position, true);
10✔
89
        }
90
        self.values.push(value);
15✔
91
        self.min_next_pos = position + 1;
5✔
92
    }
93

94
    /// Builds the immutable dense partial array.
95
    pub fn build(self) -> PartialArray<T, DenseIndex> {
4✔
96
        let (bit_vec, values) = (self.builder, self.values);
12✔
97
        let rank9 = Rank9::new(bit_vec);
12✔
98
        let values = values.into_boxed_slice();
12✔
99

100
        PartialArray {
101
            index: rank9,
102
            values,
103
        }
104
    }
105
}
106

107
/// Creates a new builder for a sparse partial array of the given length.
108
///
109
/// A sparse partial array stores the non-empty positions of the array in an
110
/// [Elias-Fano](crate::dict::EliasFano) structure.
111
///
112
/// If your set of values is really dense, consider using a [dense partial
113
/// array](new_dense).
114
///
115
/// # Examples
116
///
117
/// ```rust
118
/// use sux::array::partial_array;
119
///
120
/// let mut builder = partial_array::new_sparse(10, 3);
121
/// builder.set(1, "foo");
122
/// builder.set(2, "hello");
123
/// builder.set(7, "world");
124
///
125
/// let array = builder.build();
126
/// assert_eq!(array.get(1), Some(&"foo"));
127
/// assert_eq!(array.get(2), Some(&"hello"));
128
/// assert_eq!(array.get(3), None);
129
/// assert_eq!(array.get(7), Some(&"world"));
130
/// ```
131
///
132
/// Note that you must specify the number of values in advance.
133
pub fn new_sparse<T>(len: usize, num_values: usize) -> PartialArrayBuilder<T, EliasFanoBuilder> {
6✔
134
    PartialArrayBuilder {
135
        builder: EliasFanoBuilder::new(num_values, len),
24✔
136
        values: vec![],
6✔
137
        len,
138
        min_next_pos: 0,
139
    }
140
}
141

142
impl<T> PartialArrayBuilder<T, EliasFanoBuilder> {
143
    /// Sets a value at the given position.
144
    ///
145
    /// The provided position must be greater than the last position
146
    /// set.
147
    pub fn set(&mut self, position: usize, value: T) {
7✔
148
        if position < self.min_next_pos {
7✔
149
            panic!(
1✔
UNCOV
150
                "Positions must be set in increasing order: got {} after {}",
×
UNCOV
151
                position,
×
152
                self.min_next_pos - 1
1✔
153
            );
154
        }
155
        if position >= self.len {
6✔
156
            panic!(
1✔
UNCOV
157
                "Position {} is out of bounds for array of len {}",
×
UNCOV
158
                position, self.len
×
159
            );
160
        }
161
        // SAFETY: conditions have been just checked.
162
        unsafe { self.builder.push_unchecked(position) };
15✔
163
        self.values.push(value);
15✔
164
        self.min_next_pos = position + 1;
5✔
165
    }
166

167
    /// Builds the immutable sparse partial array.
168
    pub fn build(self) -> PartialArray<T, (EfDict, usize)> {
4✔
169
        let (builder, values) = (self.builder, self.values);
12✔
170
        let ef_dict = builder.build_with_dict();
12✔
171
        let values = values.into_boxed_slice();
12✔
172

173
        PartialArray {
174
            index: (ef_dict, self.min_next_pos),
4✔
175
            values,
176
        }
177
    }
178
}
179

180
/// Extends the builder with an iterator of (position, value) pairs.
181
///
182
/// Position must be in strictly increasing order. The first returned
183
/// position must be greater than the last position set.
184
impl<T> Extend<(usize, T)> for PartialArrayBuilder<T, BitVec<Box<[usize]>>> {
UNCOV
185
    fn extend<I: IntoIterator<Item = (usize, T)>>(&mut self, iter: I) {
×
UNCOV
186
        for (pos, val) in iter {
×
UNCOV
187
            self.set(pos, val);
×
188
        }
189
    }
190
}
191

192
/// Extends the builder with an iterator of (position, value) pairs.
193
///
194
/// Position must be in strictly increasing order. The first returned
195
/// position must be greater than the last position set.
196
impl<T> Extend<(usize, T)> for PartialArrayBuilder<T, EliasFanoBuilder> {
UNCOV
197
    fn extend<I: IntoIterator<Item = (usize, T)>>(&mut self, iter: I) {
×
UNCOV
198
        for (pos, val) in iter {
×
UNCOV
199
            self.set(pos, val);
×
200
        }
201
    }
202
}
203

204
/// An immutable partial array that supports efficient queries
205
/// in compacted storage.
206
///
207
/// This structure stores a *partial array*—an array in which only
208
/// some positions contain values. There is a [dense](new_dense)
209
/// and a [sparse](new_sparse) implementation with different
210
/// space/time trade-offs.
211
///
212
/// See [`PartialArrayBuilder`] for details on how to create a partial array.
213
#[derive(Debug, Clone, MemDbg, MemSize)]
214
#[cfg_attr(feature = "epserde", derive(epserde::Epserde))]
215
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
216
pub struct PartialArray<T, P> {
217
    index: P,
218
    values: Box<[T]>,
219
}
220

221
impl<T, P> PartialArray<T, P> {
222
    /// Returns the number of values stored in the array.
223
    #[inline(always)]
224
    pub fn num_values(&self) -> usize {
6✔
225
        self.values.len()
12✔
226
    }
227
}
228

229
impl<T> PartialArray<T, DenseIndex> {
230
    /// Returns the total length of the array.
231
    ///
232
    /// This is the length that was specified when creating the builder,
233
    /// not the number of values actually stored.
234
    #[inline(always)]
235
    pub fn len(&self) -> usize {
6✔
236
        self.index.len()
12✔
237
    }
238

239
    /// Returns true if the array length is 0.
240
    #[inline(always)]
241
    pub fn is_empty(&self) -> bool {
1✔
242
        self.len() == 0
1✔
243
    }
244

245
    /// Gets a reference to the value at the given position.
246
    ///
247
    /// Returns `Some(&value)` if a value is present at the position,
248
    /// or `None` if no value was stored there.
249
    ///
250
    /// # Examples
251
    ///
252
    /// ```rust
253
    /// use sux::array::partial_array;
254
    /// let mut builder = partial_array::new_dense(10);
255
    /// builder.set(5, 42);
256
    ///
257
    /// let array = builder.build();
258
    /// assert_eq!(array.get(5), Some(&42));
259
    /// assert_eq!(array.get(6), None);
260
    /// ```
261
    pub fn get(&self, position: usize) -> Option<&T> {
1,011✔
262
        if position >= self.len() {
2,022✔
263
            panic!(
1✔
UNCOV
264
                "Position {} is out of bounds for array of len {}",
×
UNCOV
265
                position,
×
266
                self.len()
2✔
267
            );
268
        }
269
        // Check if there's a value at this position
270
        // SAFETY: position < len()
271
        if !unsafe { self.index.get_unchecked(position) } {
2,020✔
272
            return None;
1,006✔
273
        }
274

275
        // Use ranking to find the index in the values array
276
        // SAFETY: position < len()
277
        let value_index = unsafe { self.index.rank_unchecked(position) };
16✔
278

279
        // SAFETY: necessarily value_index < num_values().
280
        Some(unsafe { self.values.get_unchecked(value_index) })
8✔
281
    }
282
}
283

284
impl<T> PartialArray<T, (EfDict, usize)> {
285
    /// Returns the total length of the array.
286
    ///
287
    /// This is the length that was specified when creating the builder,
288
    /// not the number of values actually stored.
289
    #[inline(always)]
290
    pub fn len(&self) -> usize {
5✔
291
        self.index.0.upper_bound()
10✔
292
    }
293

294
    /// Returns true if the array len is 0.
295
    #[inline(always)]
296
    pub fn is_empty(&self) -> bool {
1✔
297
        self.index.0.len() == 0
1✔
298
    }
299

300
    /// Gets a reference to the value at the given position.
301
    ///
302
    /// Returns `Some(&value)` if a value is present at the position,
303
    /// or `None` if no value was stored there.
304
    ///
305
    /// # Examples
306
    ///
307
    /// ```rust
308
    /// use sux::array::partial_array;
309
    /// let mut builder = partial_array::new_sparse(10, 1);
310
    /// builder.set(5, 42);
311
    ///
312
    /// let array = builder.build();
313
    /// assert_eq!(array.get(5), Some(&42));
314
    /// assert_eq!(array.get(6), None);
315
    /// ```
316
    pub fn get(&self, position: usize) -> Option<&T> {
1,011✔
317
        if position >= self.index.1 {
1,011✔
318
            if position >= self.len() {
1,004✔
319
                panic!(
1✔
UNCOV
320
                    "Position {} is out of bounds for array of len {}",
×
UNCOV
321
                    position,
×
322
                    self.len()
2✔
323
                );
324
            }
325
            return None;
501✔
326
        }
327
        // Check if there's a value at this position
328
        // SAFETY: position <= last set position
329
        let (index, pos) = unsafe { self.index.0.succ_unchecked::<false>(position) };
2,036✔
330

331
        if pos != position {
509✔
332
            None
505✔
333
        } else {
334
            // SAFETY: necessarily value_index < num values.
335
            Some(unsafe { self.values.get_unchecked(index) })
8✔
336
        }
337
    }
338
}
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