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

vigna / webgraph-rs / 22354752459

24 Feb 2026 02:17PM UTC coverage: 71.432% (-0.04%) from 71.468%
22354752459

push

github

vigna
Cosmetic changes and fused impls

5 of 11 new or added lines in 4 files covered. (45.45%)

6276 of 8786 relevant lines covered (71.43%)

51319574.53 hits per line

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

86.36
/webgraph/src/graphs/bvgraph/sequential.rs
1
/*
2
 * SPDX-FileCopyrightText: 2023 Inria
3
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
4
 *
5
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6
 */
7

8
use std::path::PathBuf;
9

10
use super::*;
11
use crate::utils::CircularBuffer;
12
use anyhow::Result;
13
use bitflags::Flags;
14
use dsi_bitstream::codes::ToInt;
15
use dsi_bitstream::traits::BE;
16
use dsi_bitstream::traits::BitSeek;
17
use lender::*;
18

19
/// A sequential graph in the BV format.
20
///
21
/// The graph depends on a [`SequentialDecoderFactory`] that can be used to
22
/// create decoders for the graph. This allows to decouple the graph from the
23
/// underlying storage format, and to use different storage formats for the same
24
/// graph. For example, one can use a memory-mapped file for the graph, or load
25
/// it in memory, or even generate it on the fly.
26
///
27
/// Note that the knowledge of the codes used by the graph is in the factory,
28
/// which provides methods to read each component of the BV format (outdegree,
29
/// reference offset, block count, etc.), whereas the knowledge of the
30
/// compression parameters (compression window and minimum interval length) is
31
/// in this structure.
32
#[derive(Debug, Clone)]
33
pub struct BvGraphSeq<F> {
34
    factory: F,
35
    number_of_nodes: usize,
36
    number_of_arcs: Option<u64>,
37
    compression_window: usize,
38
    min_interval_length: usize,
39
}
40

41
impl BvGraphSeq<()> {
42
    /// Returns a load configuration that can be customized.
43
    pub fn with_basename(
249,112✔
44
        basename: impl AsRef<std::path::Path>,
45
    ) -> LoadConfig<BE, Sequential, Dynamic, Mmap, Mmap> {
46
        LoadConfig {
47
            basename: PathBuf::from(basename.as_ref()),
996,448✔
48
            graph_load_flags: Flags::empty(),
498,224✔
49
            offsets_load_flags: Flags::empty(),
249,112✔
50
            _marker: std::marker::PhantomData,
51
        }
52
    }
53
}
54

55
impl<F: SequentialDecoderFactory> SplitLabeling for BvGraphSeq<F>
56
where
57
    for<'a> <F as SequentialDecoderFactory>::Decoder<'a>: Clone + Send + Sync,
58
{
59
    type SplitLender<'a>
60
        = split::seq::Lender<'a, BvGraphSeq<F>>
61
    where
62
        Self: 'a;
63
    type IntoIterator<'a>
64
        = split::seq::IntoIterator<'a, BvGraphSeq<F>>
65
    where
66
        Self: 'a;
67

68
    fn split_iter(&self, how_many: usize) -> Self::IntoIterator<'_> {
15✔
69
        split::seq::Iter::new(self.iter(), self.num_nodes(), how_many)
90✔
70
    }
71
}
72

73
impl<F: SequentialDecoderFactory> SequentialLabeling for BvGraphSeq<F> {
74
    type Label = usize;
75
    type Lender<'a>
76
        = NodeLabels<F::Decoder<'a>>
77
    where
78
        Self: 'a;
79

80
    /// Returns the number of nodes in the graph.
81
    #[inline(always)]
82
    fn num_nodes(&self) -> usize {
746,591✔
83
        self.number_of_nodes
746,591✔
84
    }
85

86
    #[inline(always)]
87
    fn num_arcs_hint(&self) -> Option<u64> {
17✔
88
        self.number_of_arcs
17✔
89
    }
90

91
    #[inline(always)]
92
    fn iter_from(&self, from: usize) -> Self::Lender<'_> {
755,587✔
93
        let mut iter = NodeLabels::new(
94
            self.factory.new_decoder().unwrap(),
2,266,761✔
95
            self.number_of_nodes,
755,587✔
96
            self.compression_window,
755,587✔
97
            self.min_interval_length,
755,587✔
98
        );
99

100
        debug_assert!(
755,587✔
101
            from <= self.number_of_nodes,
755,587✔
NEW
102
            "from ({from}) is greater than the number of nodes ({})",
×
NEW
103
            self.number_of_nodes
×
104
        );
105
        // advance_by returns Err only if the lender is exhausted before
106
        // reaching `from`, which is expected when `from == num_nodes`.
107
        let _ = iter.advance_by(from);
1,511,174✔
108

109
        iter
755,587✔
110
    }
111

112
    fn build_dcf(&self) -> DCF {
3✔
113
        let n = self.num_nodes();
9✔
114
        let num_arcs = self
9✔
115
            .num_arcs_hint()
6✔
116
            .expect("build_dcf requires num_arcs_hint()") as usize;
3✔
117
        let mut efb = sux::dict::EliasFanoBuilder::new(n + 1, num_arcs);
12✔
118
        efb.push(0);
6✔
119
        let mut cumul_deg = 0usize;
6✔
120
        let mut iter = OffsetDegIter::new(
121
            self.factory.new_decoder().unwrap(),
9✔
122
            n,
3✔
123
            self.compression_window,
3✔
124
            self.min_interval_length,
3✔
125
        );
126
        for _ in 0..n {
325,576✔
127
            cumul_deg += iter.next_degree().unwrap();
976,719✔
128
            efb.push(cumul_deg);
651,146✔
129
        }
130
        unsafe {
131
            efb.build().map_high_bits(|high_bits| {
12✔
132
                sux::rank_sel::SelectZeroAdaptConst::<_, _, 12, 4>::new(
3✔
133
                    sux::rank_sel::SelectAdaptConst::<_, _, 12, 4>::new(high_bits),
6✔
134
                )
135
            })
136
        }
137
    }
138
}
139

140
impl<F: SequentialDecoderFactory> SequentialGraph for BvGraphSeq<F> {}
141

142
/// Convenience implementation that makes it possible to iterate
143
/// over the graph using the [`for_`] macro
144
/// (see the [crate documentation](crate)).
145
impl<'a, F: SequentialDecoderFactory> IntoLender for &'a BvGraphSeq<F> {
146
    type Lender = <BvGraphSeq<F> as SequentialLabeling>::Lender<'a>;
147

148
    #[inline(always)]
149
    fn into_lender(self) -> Self::Lender {
×
150
        self.iter()
×
151
    }
152
}
153

154
impl<F: SequentialDecoderFactory> BvGraphSeq<F> {
155
    /// Creates a new sequential graph from a codes reader builder
156
    /// and the number of nodes.
157
    pub fn new(
755,522✔
158
        codes_reader_builder: F,
159
        number_of_nodes: usize,
160
        number_of_arcs: Option<u64>,
161
        compression_window: usize,
162
        min_interval_length: usize,
163
    ) -> Self {
164
        Self {
165
            factory: codes_reader_builder,
166
            number_of_nodes,
167
            number_of_arcs,
168
            compression_window,
169
            min_interval_length,
170
        }
171
    }
172

173
    #[inline(always)]
174
    pub fn map_factory<F1, F0>(self, map_func: F0) -> BvGraphSeq<F1>
×
175
    where
176
        F0: FnOnce(F) -> F1,
177
        F1: SequentialDecoderFactory,
178
    {
179
        BvGraphSeq {
180
            factory: map_func(self.factory),
×
181
            number_of_nodes: self.number_of_nodes,
×
182
            number_of_arcs: self.number_of_arcs,
×
183
            compression_window: self.compression_window,
×
184
            min_interval_length: self.min_interval_length,
×
185
        }
186
    }
187

188
    /// Consumes self and returns the factory.
189
    #[inline(always)]
190
    pub fn into_inner(self) -> F {
×
191
        self.factory
×
192
    }
193
}
194

195
impl<F: SequentialDecoderFactory> BvGraphSeq<F> {
196
    /// Creates an iterator specialized in the degrees of the nodes.
197
    /// This is slightly faster because it can avoid decoding some of
198
    /// the nodes and completely skip the merging step.
199
    #[inline(always)]
200
    pub fn offset_deg_iter(&self) -> OffsetDegIter<F::Decoder<'_>> {
257,551✔
201
        OffsetDegIter::new(
202
            self.factory.new_decoder().unwrap(),
772,653✔
203
            self.number_of_nodes,
257,551✔
204
            self.compression_window,
257,551✔
205
            self.min_interval_length,
257,551✔
206
        )
207
    }
208
}
209

210
/// A fast sequential iterator over the nodes of the graph and their successors.
211
/// This iterator does not require to know the offsets of each node in the graph.
212
#[derive(Debug, Clone)]
213
pub struct NodeLabels<D: Decode> {
214
    pub(crate) number_of_nodes: usize,
215
    pub(crate) compression_window: usize,
216
    pub(crate) min_interval_length: usize,
217
    pub(crate) decoder: D,
218
    pub(crate) backrefs: CircularBuffer<Vec<usize>>,
219
    pub(crate) current_node: usize,
220
}
221

222
impl<D: Decode + BitSeek> NodeLabels<D> {
223
    /// Forwards the call of `get_pos` to the inner `codes_reader`.
224
    /// This returns the current bits offset in the bitstream.
225
    #[inline(always)]
226
    pub fn bit_pos(&mut self) -> Result<u64, <D as BitSeek>::Error> {
1,469,850✔
227
        self.decoder.bit_pos()
2,939,700✔
228
    }
229
}
230

231
impl<D: Decode> NodeLabels<D> {
232
    /// Creates a new iterator from a codes reader
233
    pub fn new(
1,253,293✔
234
        decoder: D,
235
        number_of_nodes: usize,
236
        compression_window: usize,
237
        min_interval_length: usize,
238
    ) -> Self {
239
        Self {
240
            number_of_nodes,
241
            compression_window,
242
            min_interval_length,
243
            decoder,
244
            backrefs: CircularBuffer::new(compression_window + 1),
1,253,293✔
245
            current_node: 0,
246
        }
247
    }
248

249
    /// Gets the successors of the next node in the stream.
250
    pub fn next_successors(&mut self) -> Result<&[usize]> {
×
251
        let mut res = self.backrefs.take(self.current_node);
×
252
        self.get_successors_iter_priv(self.current_node, &mut res)?;
×
253
        let res = self.backrefs.replace(self.current_node, res);
×
254
        self.current_node += 1;
×
255
        Ok(res)
×
256
    }
257

258
    /// Inner method called by `next_successors` and the iterator `next` method
259
    fn get_successors_iter_priv(&mut self, node_id: usize, results: &mut Vec<usize>) -> Result<()> {
282,154,233✔
260
        let degree = self.decoder.read_outdegree() as usize;
564,308,466✔
261
        // no edges, we are done!
262
        if degree == 0 {
282,154,233✔
263
            return Ok(());
34,138,708✔
264
        }
265

266
        results.clear();
496,031,050✔
267
        // ensure that we have enough capacity in the vector for not reallocating
268
        results.reserve(degree);
744,046,575✔
269
        // read the reference offset
270
        let ref_delta = if self.compression_window != 0 {
496,031,050✔
271
            self.decoder.read_reference_offset() as usize
215,426,084✔
272
        } else {
273
            0
32,589,441✔
274
        };
275
        // if we copy nodes from a previous one
276
        if ref_delta != 0 {
248,015,525✔
277
            // compute the node id of the reference
278
            let reference_node_id = node_id - ref_delta;
187,505,426✔
279
            // retrieve the data
280
            let successors = &self.backrefs[reference_node_id];
187,505,426✔
281
            // get the info on which destinations to copy
282
            let number_of_blocks = self.decoder.read_block_count() as usize;
187,505,426✔
283
            // no blocks, we copy everything
284
            if number_of_blocks == 0 {
125,843,171✔
285
                results.extend_from_slice(successors);
64,180,916✔
286
            } else {
287
                // otherwise we copy only the blocks of even index
288
                // the first block could be zero
289
                let mut idx = self.decoder.read_block() as usize;
123,324,510✔
290
                results.extend_from_slice(&successors[..idx]);
184,986,765✔
291

292
                // while the other can't
293
                for block_id in 1..number_of_blocks {
161,210,493✔
294
                    let block = self.decoder.read_block() as usize;
199,096,476✔
295
                    let end = idx + block + 1;
199,096,476✔
296
                    if block_id % 2 == 0 {
131,244,336✔
297
                        results.extend_from_slice(&successors[idx..end]);
126,784,392✔
298
                    }
299
                    idx = end;
99,548,238✔
300
                }
301
                if number_of_blocks & 1 == 0 {
97,818,297✔
302
                    results.extend_from_slice(&successors[idx..]);
108,468,126✔
303
                }
304
            }
305
        };
306

307
        // if we still have to read nodes
308
        let nodes_left_to_decode = degree - results.len();
744,046,575✔
309
        if nodes_left_to_decode != 0 && self.min_interval_length != 0 {
466,252,300✔
310
            // read the number of intervals
311
            let number_of_intervals = self.decoder.read_interval_count() as usize;
350,926,356✔
312
            if number_of_intervals != 0 {
175,463,178✔
313
                // pre-allocate with capacity for efficiency
314
                let node_id_offset = self.decoder.read_interval_start().to_int();
174,934,640✔
315
                let mut start = (node_id as i64 + node_id_offset) as usize;
87,467,320✔
316
                let mut delta = self.decoder.read_interval_len() as usize;
87,467,320✔
317
                delta += self.min_interval_length;
43,733,660✔
318
                // save the first interval
319
                results.extend(start..(start + delta));
174,934,640✔
320
                start += delta;
43,733,660✔
321
                // decode the intervals
322
                for _ in 1..number_of_intervals {
69,927,092✔
323
                    start += 1 + self.decoder.read_interval_start() as usize;
52,386,864✔
324
                    delta = self.decoder.read_interval_len() as usize;
52,386,864✔
325
                    delta += self.min_interval_length;
52,386,864✔
326

327
                    results.extend(start..(start + delta));
104,773,728✔
328

329
                    start += delta;
26,193,432✔
330
                }
331
            }
332
        }
333

334
        // decode the extra nodes if needed
335
        let nodes_left_to_decode = degree - results.len();
744,046,575✔
336
        if nodes_left_to_decode != 0 {
248,015,525✔
337
            // pre-allocate with capacity for efficiency
338
            let node_id_offset = self.decoder.read_first_residual().to_int();
864,070,620✔
339
            let mut extra = (node_id as i64 + node_id_offset) as usize;
432,035,310✔
340
            results.push(extra);
648,052,965✔
341
            // decode the successive extra nodes
342
            for _ in 1..nodes_left_to_decode {
1,444,891,924✔
343
                extra += 1 + self.decoder.read_residual() as usize;
2,147,483,647✔
344
                results.push(extra);
2,147,483,647✔
345
            }
346
        }
347

348
        results.sort();
248,015,525✔
349
        Ok(())
248,015,525✔
350
    }
351
}
352

353
impl<'succ, D: Decode> NodeLabelsLender<'succ> for NodeLabels<D> {
354
    type Label = usize;
355
    type IntoIterator = crate::traits::labels::AssumeSortedIterator<
356
        std::iter::Copied<std::slice::Iter<'succ, Self::Label>>,
357
    >;
358
}
359

360
impl<'succ, D: Decode> Lending<'succ> for NodeLabels<D> {
361
    type Lend = (usize, <Self as NodeLabelsLender<'succ>>::IntoIterator);
362
}
363

364
impl<D: Decode> Lender for NodeLabels<D> {
365
    check_covariance!();
366

367
    fn next(&mut self) -> Option<Lend<'_, Self>> {
282,154,733✔
368
        if self.current_node >= self.number_of_nodes {
282,154,733✔
369
            return None;
500✔
370
        }
371
        let mut res = self.backrefs.take(self.current_node);
1,128,616,932✔
372
        res.clear();
564,308,466✔
373
        self.get_successors_iter_priv(self.current_node, &mut res)
1,128,616,932✔
374
            .unwrap();
375

376
        let res = self.backrefs.replace(self.current_node, res);
1,410,771,165✔
377
        let node_id = self.current_node;
564,308,466✔
378
        self.current_node += 1;
282,154,233✔
379
        Some((node_id, unsafe {
564,308,466✔
380
            crate::traits::labels::AssumeSortedIterator::new(res.iter().copied())
564,308,466✔
381
        }))
382
    }
383

384
    fn size_hint(&self) -> (usize, Option<usize>) {
3,581,266✔
385
        let len = self.len();
10,743,798✔
386
        (len, Some(len))
3,581,266✔
387
    }
388
}
389

390
unsafe impl<D: Decode> SortedLender for NodeLabels<D> {}
391

392
impl<D: Decode> ExactSizeLender for NodeLabels<D> {
393
    #[inline(always)]
394
    fn len(&self) -> usize {
3,581,266✔
395
        self.number_of_nodes - self.current_node
3,581,266✔
396
    }
397
}
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