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

vigna / webgraph-rs / 19278972970

11 Nov 2025 09:25PM UTC coverage: 62.316% (+14.3%) from 48.052%
19278972970

push

github

zommiommy
BatchCodec print stats and encoding time

40 of 53 new or added lines in 4 files covered. (75.47%)

814 existing lines in 46 files now uncovered.

5252 of 8428 relevant lines covered (62.32%)

29580067.92 hits per line

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

84.55
/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::BitSeek;
16
use dsi_bitstream::traits::BE;
17
use lender::*;
18

19
/// A sequential BvGraph that can be read from a `codes_reader_builder`.
20
/// The builder is needed because we should be able to create multiple iterators
21
/// and this allows us to have a single place where to store the mmapped file.
22
#[derive(Debug, Clone)]
23
pub struct BvGraphSeq<F> {
24
    factory: F,
25
    number_of_nodes: usize,
26
    number_of_arcs: Option<u64>,
27
    compression_window: usize,
28
    min_interval_length: usize,
29
}
30

31
impl BvGraphSeq<()> {
32
    pub fn with_basename(
39✔
33
        basename: impl AsRef<std::path::Path>,
34
    ) -> LoadConfig<BE, Sequential, Dynamic, Mmap, Mmap> {
35
        LoadConfig {
36
            basename: PathBuf::from(basename.as_ref()),
156✔
37
            graph_load_flags: Flags::empty(),
78✔
38
            offsets_load_flags: Flags::empty(),
39✔
39
            _marker: std::marker::PhantomData,
40
        }
41
    }
42
}
43

44
impl<F: SequentialDecoderFactory> SplitLabeling for BvGraphSeq<F>
45
where
46
    for<'a> <F as SequentialDecoderFactory>::Decoder<'a>: Clone + Send + Sync,
47
{
48
    type SplitLender<'a>
49
        = split::seq::Lender<'a, BvGraphSeq<F>>
50
    where
51
        Self: 'a;
52
    type IntoIterator<'a>
53
        = split::seq::IntoIterator<'a, BvGraphSeq<F>>
54
    where
55
        Self: 'a;
56

57
    fn split_iter(&self, how_many: usize) -> Self::IntoIterator<'_> {
10✔
58
        split::seq::Iter::new(self.iter(), self.num_nodes(), how_many)
60✔
59
    }
60
}
61

62
impl<F: SequentialDecoderFactory> SequentialLabeling for BvGraphSeq<F> {
63
    type Label = usize;
64
    type Lender<'a>
65
        = Iter<F::Decoder<'a>>
66
    where
67
        Self: 'a;
68

69
    #[inline(always)]
70
    /// Returns the number of nodes in the graph
71
    fn num_nodes(&self) -> usize {
30✔
72
        self.number_of_nodes
30✔
73
    }
74

75
    #[inline(always)]
76
    fn num_arcs_hint(&self) -> Option<u64> {
×
77
        self.number_of_arcs
×
78
    }
79

80
    #[inline(always)]
81
    fn iter_from(&self, from: usize) -> Self::Lender<'_> {
7,001✔
82
        let mut iter = Iter::new(
83
            self.factory.new_decoder().unwrap(),
21,003✔
84
            self.number_of_nodes,
7,001✔
85
            self.compression_window,
7,001✔
86
            self.min_interval_length,
7,001✔
87
        );
88

89
        let _ = iter.advance_by(from);
14,002✔
90

91
        iter
7,001✔
92
    }
93
}
94

95
impl<F: SequentialDecoderFactory> SequentialGraph for BvGraphSeq<F> {}
96

97
/// Convenience implementation that makes it possible to iterate
98
/// over the graph using the [`for_`] macro
99
/// (see the [crate documentation](crate)).
100
impl<'a, F: SequentialDecoderFactory> IntoLender for &'a BvGraphSeq<F> {
101
    type Lender = <BvGraphSeq<F> as SequentialLabeling>::Lender<'a>;
102

103
    #[inline(always)]
104
    fn into_lender(self) -> Self::Lender {
10✔
105
        self.iter()
20✔
106
    }
107
}
108

109
impl<F: SequentialDecoderFactory> BvGraphSeq<F> {
110
    /// Creates a new sequential graph from a codes reader builder
111
    /// and the number of nodes.
112
    pub fn new(
6,991✔
113
        codes_reader_builder: F,
114
        number_of_nodes: usize,
115
        number_of_arcs: Option<u64>,
116
        compression_window: usize,
117
        min_interval_length: usize,
118
    ) -> Self {
119
        Self {
120
            factory: codes_reader_builder,
121
            number_of_nodes,
122
            number_of_arcs,
123
            compression_window,
124
            min_interval_length,
125
        }
126
    }
127

128
    #[inline(always)]
UNCOV
129
    pub fn map_factory<F1, F0>(self, map_func: F0) -> BvGraphSeq<F1>
×
130
    where
131
        F0: FnOnce(F) -> F1,
132
        F1: SequentialDecoderFactory,
133
    {
134
        BvGraphSeq {
135
            factory: map_func(self.factory),
×
136
            number_of_nodes: self.number_of_nodes,
×
UNCOV
137
            number_of_arcs: self.number_of_arcs,
×
UNCOV
138
            compression_window: self.compression_window,
×
UNCOV
139
            min_interval_length: self.min_interval_length,
×
140
        }
141
    }
142

143
    #[inline(always)]
144
    /// Consume self and return the factory
UNCOV
145
    pub fn into_inner(self) -> F {
×
UNCOV
146
        self.factory
×
147
    }
148
}
149

150
impl<F: SequentialDecoderFactory> BvGraphSeq<F>
151
where
152
    for<'a> F::Decoder<'a>: Decode,
153
{
154
    #[inline(always)]
155
    /// Creates an iterator specialized in the degrees of the nodes.
156
    /// This is slightly faster because it can avoid decoding some of the nodes
157
    /// and completely skip the merging step.
158
    pub fn offset_deg_iter(&self) -> OffsetDegIter<F::Decoder<'_>> {
6,961✔
159
        OffsetDegIter::new(
160
            self.factory.new_decoder().unwrap(),
20,883✔
161
            self.number_of_nodes,
6,961✔
162
            self.compression_window,
6,961✔
163
            self.min_interval_length,
6,961✔
164
        )
165
    }
166
}
167

168
/// A fast sequential iterator over the nodes of the graph and their successors.
169
/// This iterator does not require to know the offsets of each node in the graph.
170
#[derive(Debug, Clone)]
171
pub struct Iter<D: Decode> {
172
    pub(crate) number_of_nodes: usize,
173
    pub(crate) compression_window: usize,
174
    pub(crate) min_interval_length: usize,
175
    pub(crate) decoder: D,
176
    pub(crate) backrefs: CircularBuffer<Vec<usize>>,
177
    pub(crate) current_node: usize,
178
}
179

180
impl<D: Decode + BitSeek> Iter<D> {
181
    #[inline(always)]
182
    /// Forward the call of `get_pos` to the inner `codes_reader`.
183
    /// This returns the current bits offset in the bitstream.
184
    pub fn bit_pos(&mut self) -> Result<u64, <D as BitSeek>::Error> {
1,175,880✔
185
        self.decoder.bit_pos()
2,351,760✔
186
    }
187
}
188

189
impl<D: Decode> Iter<D> {
190
    /// Creates a new iterator from a codes reader
191
    pub fn new(
38,214✔
192
        decoder: D,
193
        number_of_nodes: usize,
194
        compression_window: usize,
195
        min_interval_length: usize,
196
    ) -> Self {
197
        Self {
198
            number_of_nodes,
199
            compression_window,
200
            min_interval_length,
201
            decoder,
202
            backrefs: CircularBuffer::new(compression_window + 1),
38,214✔
203
            current_node: 0,
204
        }
205
    }
206

207
    /// Get the successors of the next node in the stream
208
    pub fn next_successors(&mut self) -> Result<&[usize]> {
×
209
        let mut res = self.backrefs.take(self.current_node);
×
210
        res.clear();
×
211
        self.get_successors_iter_priv(self.current_node, &mut res)?;
×
UNCOV
212
        let res = self.backrefs.replace(self.current_node, res);
×
UNCOV
213
        self.current_node += 1;
×
UNCOV
214
        Ok(res)
×
215
    }
216

217
    #[inline(always)]
218
    /// Inner method called by `next_successors` and the iterator `next` method
219
    fn get_successors_iter_priv(&mut self, node_id: usize, results: &mut Vec<usize>) -> Result<()> {
111,615,894✔
220
        let degree = self.decoder.read_outdegree() as usize;
223,231,788✔
221
        // no edges, we are done!
222
        if degree == 0 {
111,615,894✔
223
            return Ok(());
22,973,508✔
224
        }
225

226
        // ensure that we have enough capacity in the vector for not reallocating
227
        results.reserve(degree.saturating_sub(results.capacity()));
531,854,316✔
228
        // read the reference offset
229
        let ref_delta = if self.compression_window != 0 {
177,284,772✔
230
            self.decoder.read_reference_offset() as usize
87,121,987✔
231
        } else {
232
            0
1,520,399✔
233
        };
234
        // if we copy nodes from a previous one
235
        if ref_delta != 0 {
88,642,386✔
236
            // compute the node id of the reference
237
            let reference_node_id = node_id - ref_delta;
124,747,422✔
238
            // retrieve the data
239
            let neighbours = &self.backrefs[reference_node_id];
124,747,422✔
240
            //debug_assert!(!neighbours.is_empty());
241
            // get the info on which destinations to copy
242
            let number_of_blocks = self.decoder.read_block_count() as usize;
124,747,422✔
243
            // no blocks, we copy everything
244
            if number_of_blocks == 0 {
84,851,938✔
245
                results.extend_from_slice(neighbours);
44,956,454✔
246
            } else {
247
                // otherwise we copy only the blocks of even index
248
                // the first block could be zero
249
                let mut idx = self.decoder.read_block() as usize;
79,790,968✔
250
                results.extend_from_slice(&neighbours[..idx]);
119,686,452✔
251

252
                // while the other can't
253
                for block_id in 1..number_of_blocks {
99,756,494✔
254
                    let block = self.decoder.read_block() as usize;
119,722,020✔
255
                    let end = idx + block + 1;
119,722,020✔
256
                    if block_id % 2 == 0 {
77,773,101✔
257
                        results.extend_from_slice(&neighbours[idx..end]);
71,648,364✔
258
                    }
259
                    idx = end;
59,861,010✔
260
                }
261
                if number_of_blocks & 1 == 0 {
63,932,312✔
262
                    results.extend_from_slice(&neighbours[idx..]);
72,110,484✔
263
                }
264
            }
265
        };
266

267
        // if we still have to read nodes
268
        let nodes_left_to_decode = degree - results.len();
265,927,158✔
269
        if nodes_left_to_decode != 0 && self.min_interval_length != 0 {
156,370,040✔
270
            // read the number of intervals
271
            let number_of_intervals = self.decoder.read_interval_count() as usize;
132,080,792✔
272
            if number_of_intervals != 0 {
66,040,396✔
273
                // pre-allocate with capacity for efficiency
274
                let node_id_offset = self.decoder.read_interval_start().to_int();
41,721,744✔
275
                let mut start = (node_id as i64 + node_id_offset) as usize;
20,860,872✔
276
                let mut delta = self.decoder.read_interval_len() as usize;
20,860,872✔
277
                delta += self.min_interval_length;
10,430,436✔
278
                // save the first interval
279
                results.extend(start..(start + delta));
41,721,744✔
280
                start += delta;
10,430,436✔
281
                // decode the intervals
282
                for _ in 1..number_of_intervals {
16,026,588✔
283
                    start += 1 + self.decoder.read_interval_start() as usize;
11,192,304✔
284
                    delta = self.decoder.read_interval_len() as usize;
11,192,304✔
285
                    delta += self.min_interval_length;
11,192,304✔
286

287
                    results.extend(start..(start + delta));
22,384,608✔
288

289
                    start += delta;
5,596,152✔
290
                }
291
            }
292
        }
293

294
        // decode the extra nodes if needed
295
        let nodes_left_to_decode = degree - results.len();
265,927,158✔
296
        if nodes_left_to_decode != 0 {
88,642,386✔
297
            // pre-allocate with capacity for efficiency
298
            let node_id_offset = self.decoder.read_first_residual().to_int();
264,873,300✔
299
            let mut extra = (node_id as i64 + node_id_offset) as usize;
132,436,650✔
300
            results.push(extra);
198,654,975✔
301
            // decode the successive extra nodes
302
            for _ in 1..nodes_left_to_decode {
231,251,016✔
303
                extra += 1 + self.decoder.read_residual() as usize;
330,065,382✔
304
                results.push(extra);
330,065,382✔
305
            }
306
        }
307

308
        results.sort();
88,642,386✔
309
        Ok(())
88,642,386✔
310
    }
311
}
312

313
impl<'succ, D: Decode> NodeLabelsLender<'succ> for Iter<D> {
314
    type Label = usize;
315
    type IntoIterator = crate::traits::labels::AssumeSortedIterator<
316
        std::iter::Copied<std::slice::Iter<'succ, Self::Label>>,
317
    >;
318
}
319

320
impl<'succ, D: Decode> Lending<'succ> for Iter<D> {
321
    type Lend = (usize, <Self as NodeLabelsLender<'succ>>::IntoIterator);
322
}
323

324
impl<D: Decode> Lender for Iter<D> {
325
    fn next(&mut self) -> Option<Lend<'_, Self>> {
111,616,202✔
326
        if self.current_node >= self.number_of_nodes {
111,616,202✔
327
            return None;
308✔
328
        }
329
        let mut res = self.backrefs.take(self.current_node);
446,463,576✔
330
        res.clear();
223,231,788✔
331
        self.get_successors_iter_priv(self.current_node, &mut res)
446,463,576✔
332
            .unwrap();
333

334
        let res = self.backrefs.replace(self.current_node, res);
558,079,470✔
335
        let node_id = self.current_node;
223,231,788✔
336
        self.current_node += 1;
111,615,894✔
337
        Some((node_id, unsafe {
223,231,788✔
338
            crate::traits::labels::AssumeSortedIterator::new(res.iter().copied())
223,231,788✔
339
        }))
340
    }
341

342
    fn size_hint(&self) -> (usize, Option<usize>) {
3,581,263✔
343
        let len = self.len();
10,743,789✔
344
        (len, Some(len))
3,581,263✔
345
    }
346
}
347

348
unsafe impl<D: Decode> SortedLender for Iter<D> {}
349

350
impl<D: Decode> ExactSizeLender for Iter<D> {
351
    fn len(&self) -> usize {
3,906,831✔
352
        self.number_of_nodes - self.current_node
3,906,831✔
353
    }
354
}
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