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

vigna / webgraph-rs / 17735521500

15 Sep 2025 01:54PM UTC coverage: 49.961% (-0.03%) from 49.987%
17735521500

push

github

web-flow
Merge pull request #137 from progval/fix-compil

bench_unit_transpose: Fix compilation

3888 of 7782 relevant lines covered (49.96%)

23960578.28 hits per line

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

80.38
/webgraph/src/graphs/bvgraph/random_access.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
#![allow(clippy::type_complexity)]
9

10
use crate::prelude::*;
11
use bitflags::Flags;
12
use dsi_bitstream::codes::ToInt;
13
use dsi_bitstream::dispatch::factory::CodesReaderFactoryHelper;
14
use dsi_bitstream::traits::{Endianness, BE};
15
use lender::IntoLender;
16
use std::path::PathBuf;
17
use sux::traits::IndexedSeq;
18

19
use self::sequential::Iter;
20

21
#[derive(Debug, Clone)]
22
pub struct BvGraph<F> {
23
    factory: F,
24
    number_of_nodes: usize,
25
    number_of_arcs: u64,
26
    compression_window: usize,
27
    min_interval_length: usize,
28
}
29

30
impl BvGraph<()> {
31
    /// Returns a load configuration that can be customized.
32
    pub fn with_basename(
38✔
33
        basename: impl AsRef<std::path::Path>,
34
    ) -> LoadConfig<BE, Random, Dynamic, Mmap, Mmap> {
35
        LoadConfig {
36
            basename: PathBuf::from(basename.as_ref()),
152✔
37
            graph_load_flags: Flags::empty(),
76✔
38
            offsets_load_flags: Flags::empty(),
38✔
39
            _marker: std::marker::PhantomData,
40
        }
41
    }
42
}
43

44
impl<
45
        E: Endianness,
46
        F: CodesReaderFactoryHelper<E>,
47
        OFF: IndexedSeq<Input = usize, Output = usize>,
48
    > BvGraph<DynCodesDecoderFactory<E, F, OFF>>
49
where
50
    for<'a> &'a OFF: IntoIterator<Item = usize>,
51
{
52
    /// Remaps the offsets in a slice of `usize`.
53
    ///
54
    /// This method is mainly useful for benchmarking and testing purposes, as
55
    /// representing the offsets as a slice increasing significantly the
56
    /// memory footprint. It just replaces current decoder factory with
57
    /// the result of [`DynCodesDecoderFactory::offsets_to_slice`].
58
    pub fn offsets_to_slice(
1✔
59
        self,
60
    ) -> BvGraph<DynCodesDecoderFactory<E, F, SliceSeq<usize, Box<[usize]>>>> {
61
        BvGraph {
62
            factory: self.factory.offsets_to_slice(),
3✔
63
            number_of_nodes: self.number_of_nodes,
2✔
64
            number_of_arcs: self.number_of_arcs,
2✔
65
            compression_window: self.compression_window,
1✔
66
            min_interval_length: self.min_interval_length,
1✔
67
        }
68
    }
69
}
70

71
impl<
72
        E: Endianness,
73
        F: CodesReaderFactoryHelper<E>,
74
        OFF: IndexedSeq<Input = usize, Output = usize>,
75
    > BvGraph<ConstCodesDecoderFactory<E, F, OFF>>
76
where
77
    for<'a> &'a OFF: IntoIterator<Item = usize>,
78
{
79
    /// Remaps the offsets in a slice of `usize`.
80
    ///
81
    /// This method is mainly useful for benchmarking and testing purposes, as
82
    /// representing the offsets as a slice increasing significantly the
83
    /// memory footprint. It just replaces current decoder factory with
84
    /// the result of [`ConstCodesDecoderFactory::offsets_to_slice`].
85
    pub fn offsets_to_slice(
×
86
        self,
87
    ) -> BvGraph<ConstCodesDecoderFactory<E, F, SliceSeq<usize, Box<[usize]>>>> {
88
        BvGraph {
89
            factory: self.factory.offsets_to_slice(),
×
90
            number_of_nodes: self.number_of_nodes,
×
91
            number_of_arcs: self.number_of_arcs,
×
92
            compression_window: self.compression_window,
×
93
            min_interval_length: self.min_interval_length,
×
94
        }
95
    }
96
}
97

98
impl<F: RandomAccessDecoderFactory> SplitLabeling for BvGraph<F>
99
where
100
    for<'a> <F as RandomAccessDecoderFactory>::Decoder<'a>: Send + Sync,
101
{
102
    type SplitLender<'a>
103
        = split::ra::Lender<'a, BvGraph<F>>
104
    where
105
        Self: 'a;
106
    type IntoIterator<'a>
107
        = split::ra::IntoIterator<'a, BvGraph<F>>
108
    where
109
        Self: 'a;
110

111
    fn split_iter(&self, how_many: usize) -> Self::IntoIterator<'_> {
12✔
112
        split::ra::Iter::new(self, how_many)
36✔
113
    }
114
}
115

116
impl<F> BvGraph<F>
117
where
118
    F: RandomAccessDecoderFactory,
119
{
120
    /// Creates a new BvGraph from the given parameters.
121
    ///
122
    /// # Arguments
123
    /// - `reader_factory`: backend that can create objects that allows
124
    ///   us to read the bitstream of the graph to decode the edges.
125
    /// - `offsets`: the bit offset at which we will have to start for decoding
126
    ///   the edges of each node. (This is needed for the random accesses,
127
    ///   [`BvGraphSeq`] does not need them)
128
    /// - `min_interval_length`: the minimum size of the intervals we are going
129
    ///   to decode.
130
    /// - `compression_window`: the maximum distance between two nodes that
131
    ///   reference each other.
132
    /// - `number_of_nodes`: the number of nodes in the graph.
133
    /// - `number_of_arcs`: the number of arcs in the graph.
134
    ///
135
    pub fn new(
6,996✔
136
        factory: F,
137
        number_of_nodes: usize,
138
        number_of_arcs: u64,
139
        compression_window: usize,
140
        min_interval_length: usize,
141
    ) -> Self {
142
        Self {
143
            factory,
144
            number_of_nodes,
145
            number_of_arcs,
146
            compression_window,
147
            min_interval_length,
148
        }
149
    }
150

151
    #[inline(always)]
152
    /// Consume self and return the factory
153
    pub fn into_inner(self) -> F {
×
154
        self.factory
×
155
    }
156
}
157

158
impl<F> SequentialLabeling for BvGraph<F>
159
where
160
    F: RandomAccessDecoderFactory,
161
{
162
    type Label = usize;
163
    type Lender<'b>
164
        = Iter<F::Decoder<'b>>
165
    where
166
        Self: 'b,
167
        F: 'b;
168

169
    #[inline(always)]
170
    fn num_nodes(&self) -> usize {
332,868✔
171
        self.number_of_nodes
332,868✔
172
    }
173

174
    #[inline(always)]
175
    fn num_arcs_hint(&self) -> Option<u64> {
×
176
        Some(self.number_of_arcs)
×
177
    }
178

179
    /// Returns a fast sequential iterator over the nodes of the graph and their successors.
180
    fn iter_from(&self, start_node: usize) -> Self::Lender<'_> {
14,045✔
181
        let codes_reader = self.factory.new_decoder(start_node).unwrap();
70,225✔
182
        // we have to pre-fill the buffer
183
        let mut backrefs = CircularBuffer::new(self.compression_window + 1);
42,135✔
184

185
        for node_id in start_node.saturating_sub(self.compression_window)..start_node {
153,978✔
186
            backrefs.replace(node_id, self.successors(node_id).collect());
×
187
        }
188

189
        Iter {
190
            decoder: codes_reader,
191
            backrefs,
192
            compression_window: self.compression_window,
28,090✔
193
            min_interval_length: self.min_interval_length,
28,090✔
194
            number_of_nodes: self.number_of_nodes,
14,045✔
195
            current_node: start_node,
196
        }
197
    }
198
}
199

200
impl<F> SequentialGraph for BvGraph<F> where F: RandomAccessDecoderFactory {}
201

202
impl<F> RandomAccessLabeling for BvGraph<F>
203
where
204
    F: RandomAccessDecoderFactory,
205
{
206
    type Labels<'a>
207
        = Succ<F::Decoder<'a>>
208
    where
209
        Self: 'a,
210
        F: 'a;
211

212
    fn num_arcs(&self) -> u64 {
8,020✔
213
        self.number_of_arcs
8,020✔
214
    }
215

216
    /// Returns the outdegree of a node.
217
    fn outdegree(&self, node_id: usize) -> usize {
72,417,023✔
218
        let mut codes_reader = self
144,834,046✔
219
            .factory
72,417,023✔
220
            .new_decoder(node_id)
144,834,046✔
221
            .expect("Cannot create reader");
222
        codes_reader.read_outdegree() as usize
72,417,023✔
223
    }
224

225
    #[inline(always)]
226
    /// Returns a random access iterator over the successors of a node.
227
    fn labels(&self, node_id: usize) -> Succ<F::Decoder<'_>> {
66,375,153✔
228
        let codes_reader = self
132,750,306✔
229
            .factory
66,375,153✔
230
            .new_decoder(node_id)
132,750,306✔
231
            .expect("Cannot create reader");
232

233
        let mut result = Succ::new(codes_reader);
199,125,459✔
234
        let degree = result.reader.read_outdegree() as usize;
132,750,306✔
235
        // no edges, we are done!
236
        if degree == 0 {
66,375,153✔
237
            return result;
7,292,005✔
238
        }
239
        result.size = degree;
×
240
        let mut nodes_left_to_decode = degree;
×
241
        // read the reference offset
242
        let ref_delta = if self.compression_window != 0 {
×
243
            result.reader.read_reference_offset() as usize
59,082,852✔
244
        } else {
245
            0
296✔
246
        };
247
        // if we copy nodes from a previous one
248
        if ref_delta != 0 {
×
249
            // compute the node id of the reference
250
            let reference_node_id = node_id - ref_delta;
74,738,242✔
251
            // retrieve the data
252
            let neighbours = self.successors(reference_node_id);
149,476,484✔
253
            debug_assert!(neighbours.len() != 0);
74,738,242✔
254
            // get the info on which destinations to copy
255
            let number_of_blocks = result.reader.read_block_count() as usize;
74,738,242✔
256
            // add +1 if the number of blocks is even, so we have capacity for
257
            // the block that will be added in the masked iterator
258
            let alloc_len = 1 + number_of_blocks - (number_of_blocks & 1);
74,738,242✔
259
            let mut blocks = Vec::with_capacity(alloc_len);
112,107,363✔
260
            if number_of_blocks != 0 {
37,369,121✔
261
                // the first block could be zero
262
                blocks.push(result.reader.read_block() as usize);
73,526,400✔
263
                // while the other can't
264
                for _ in 1..number_of_blocks {
62,696,796✔
265
                    blocks.push(result.reader.read_block() as usize + 1);
38,187,996✔
266
                }
267
            }
268
            // create the masked iterator
269
            let res = MaskedIterator::new(neighbours, blocks);
149,476,484✔
270
            nodes_left_to_decode -= res.len();
37,369,121✔
271

272
            result.copied_nodes_iter = Some(res);
74,738,242✔
273
        };
274

275
        // if we still have to read nodes
276
        if nodes_left_to_decode != 0 && self.min_interval_length != 0 {
105,437,170✔
277
            // read the number of intervals
278
            let number_of_intervals = result.reader.read_interval_count() as usize;
92,707,100✔
279
            if number_of_intervals != 0 {
46,353,550✔
280
                // pre-allocate with capacity for efficiency
281
                result.intervals = Vec::with_capacity(number_of_intervals + 1);
34,874,370✔
282
                let node_id_offset = (result.reader.read_interval_start()).to_int();
46,499,160✔
283

284
                debug_assert!((node_id as i64 + node_id_offset) >= 0);
23,249,580✔
285
                let mut start = (node_id as i64 + node_id_offset) as usize;
23,249,580✔
286
                let mut delta = result.reader.read_interval_len() as usize;
23,249,580✔
287
                delta += self.min_interval_length;
11,624,790✔
288
                // save the first interval
289
                result.intervals.push((start, delta));
34,874,370✔
290
                start += delta;
11,624,790✔
291
                nodes_left_to_decode -= delta;
11,624,790✔
292
                // decode the intervals
293
                for _ in 1..number_of_intervals {
17,427,995✔
294
                    start += 1 + result.reader.read_interval_start() as usize;
5,803,205✔
295
                    delta = result.reader.read_interval_len() as usize;
5,803,205✔
296
                    delta += self.min_interval_length;
5,803,205✔
297

298
                    result.intervals.push((start, delta));
5,803,205✔
299
                    start += delta;
5,803,205✔
300
                    nodes_left_to_decode -= delta;
5,803,205✔
301
                }
302
                // fake final interval to avoid checks in the implementation of
303
                // `next`
304
                result.intervals.push((usize::MAX - 1, 1));
34,874,370✔
305
            }
306
        }
307

308
        // decode just the first extra, if present (the others will be decoded on demand)
309
        if nodes_left_to_decode != 0 {
104,326,698✔
310
            let node_id_offset = result.reader.read_first_residual().to_int();
226,217,750✔
311
            result.next_residual_node = (node_id as i64 + node_id_offset) as usize;
45,243,550✔
312
            result.residuals_to_go = nodes_left_to_decode - 1;
45,243,550✔
313
        }
314

315
        // setup the first interval node so we can decode without branches
316
        if !result.intervals.is_empty() {
11,624,790✔
317
            let (start, len) = &mut result.intervals[0];
11,624,790✔
318
            *len -= 1;
11,624,790✔
319
            result.next_interval_node = *start;
11,624,790✔
320
            *start += 1;
11,624,790✔
321
            result.intervals_idx += (*len == 0) as usize;
11,624,790✔
322
        };
323

324
        // cache the first copied node so we don't have to check if the iter
325
        // ended at every call of `next`
326
        result.next_copied_node = result
×
327
            .copied_nodes_iter
×
328
            .as_mut()
×
329
            .and_then(|iter| iter.next())
328,458,244✔
330
            .unwrap_or(usize::MAX);
×
331

332
        result
×
333
    }
334
}
335

336
impl<F: SequentialDecoderFactory> BvGraph<F>
337
where
338
    for<'a> F::Decoder<'a>: Decode,
339
{
340
    #[inline(always)]
341
    /// Creates an iterator specialized in the degrees of the nodes.
342
    /// This is slightly faster because it can avoid decoding some of the nodes
343
    /// and completely skip the merging step.
344
    pub fn offset_deg_iter(&self) -> OffsetDegIter<F::Decoder<'_>> {
1✔
345
        OffsetDegIter::new(
346
            self.factory.new_decoder().unwrap(),
3✔
347
            self.number_of_nodes,
1✔
348
            self.compression_window,
1✔
349
            self.min_interval_length,
1✔
350
        )
351
    }
352
}
353

354
impl<F: RandomAccessDecoderFactory> BvGraph<F>
355
where
356
    for<'a> F::Decoder<'a>: Decode,
357
{
358
    #[inline(always)]
359
    /// Creates an iterator specialized in the degrees of the nodes starting
360
    /// from a given node.
361
    pub fn offset_deg_iter_from(&self, node: usize) -> OffsetDegIter<F::Decoder<'_>> {
100✔
362
        let mut backrefs = vec![0; self.compression_window];
300✔
363
        for node_id in node.saturating_sub(self.compression_window)..node {
1,072✔
364
            backrefs[node_id % self.compression_window] = self.outdegree(node_id);
×
365
        }
366
        OffsetDegIter::new_from(
367
            self.factory.new_decoder(node).unwrap(),
400✔
368
            self.number_of_nodes,
100✔
369
            self.compression_window,
100✔
370
            self.min_interval_length,
100✔
371
            node,
100✔
372
            backrefs,
100✔
373
        )
374
    }
375
}
376
impl<F> RandomAccessGraph for BvGraph<F> where F: RandomAccessDecoderFactory {}
377

378
/// The iterator returned from [`BvGraph`] that returns the successors of a
379
/// node in sorted order.
380
#[derive(Debug, Clone)]
381
pub struct Succ<D: Decode> {
382
    reader: D,
383
    /// The number of values left
384
    size: usize,
385
    /// Iterator over the destinations that we are going to copy
386
    /// from another node
387
    copied_nodes_iter: Option<MaskedIterator<Succ<D>>>,
388

389
    /// Intervals of extra nodes
390
    intervals: Vec<(usize, usize)>,
391
    /// The index of interval to return
392
    intervals_idx: usize,
393
    /// Remaining residual nodes
394
    residuals_to_go: usize,
395
    /// The next residual node
396
    next_residual_node: usize,
397
    /// The next residual node
398
    next_copied_node: usize,
399
    /// The next interval node
400
    next_interval_node: usize,
401
}
402

403
impl<D: Decode> ExactSizeIterator for Succ<D> {
404
    #[inline(always)]
405
    fn len(&self) -> usize {
355,078,777✔
406
        self.size
355,078,777✔
407
    }
408
}
409

410
unsafe impl<D: Decode> SortedIterator for Succ<D> {}
411

412
impl<D: Decode> Succ<D> {
413
    /// Creates an empty iterator
414
    fn new(reader: D) -> Self {
529,110,460✔
415
        Self {
416
            reader,
417
            size: 0,
418
            copied_nodes_iter: None,
419
            intervals: vec![],
529,110,460✔
420
            intervals_idx: 0,
421
            residuals_to_go: 0,
422
            next_residual_node: usize::MAX,
423
            next_copied_node: usize::MAX,
424
            next_interval_node: usize::MAX,
425
        }
426
    }
427
}
428

429
impl<D: Decode> Iterator for Succ<D> {
430
    type Item = usize;
431

432
    fn next(&mut self) -> Option<Self::Item> {
2,147,483,647✔
433
        // check if we should stop iterating
434
        if self.size == 0 {
2,147,483,647✔
435
            return None;
211,400,708✔
436
        }
437

438
        self.size -= 1;
×
439
        debug_assert!(
×
440
            self.next_copied_node != usize::MAX
×
441
                || self.next_residual_node != usize::MAX
2,147,483,647✔
442
                || self.next_interval_node != usize::MAX,
1,032,945,144✔
443
            "At least one of the nodes must present, this should be a problem with the degree.",
×
444
        );
445

446
        // find the smallest of the values
447
        let min = self.next_residual_node.min(self.next_interval_node);
2,147,483,647✔
448

449
        // depending on from where the node was, forward it
450
        if min >= self.next_copied_node {
×
451
            let res = self.next_copied_node;
2,147,483,647✔
452
            self.next_copied_node = self
2,147,483,647✔
453
                .copied_nodes_iter
2,147,483,647✔
454
                .as_mut()
2,147,483,647✔
455
                .and_then(|iter| iter.next())
2,147,483,647✔
456
                .unwrap_or(usize::MAX);
2,147,483,647✔
457
            return Some(res);
2,147,483,647✔
458
        } else if min == self.next_residual_node {
2,147,483,647✔
459
            if self.residuals_to_go == 0 {
1,539,213,060✔
460
                self.next_residual_node = usize::MAX;
289,851,168✔
461
            } else {
462
                self.residuals_to_go -= 1;
959,510,724✔
463
                // NOTE: here we cannot propagate the error
464
                self.next_residual_node += 1 + self.reader.read_residual() as usize;
959,510,724✔
465
            }
466
        } else {
467
            let (start, len) = &mut self.intervals[self.intervals_idx];
2,147,483,647✔
468
            debug_assert_ne!(
×
469
                *len, 0,
×
470
                "there should never be an interval with length zero here"
×
471
            );
472
            // if the interval has other values, just reduce the interval
473
            *len -= 1;
2,147,483,647✔
474
            self.next_interval_node = *start;
2,147,483,647✔
475
            *start += 1;
2,147,483,647✔
476
            self.intervals_idx += (*len == 0) as usize;
2,147,483,647✔
477
        }
478

479
        Some(min)
2,147,483,647✔
480
    }
481

482
    fn size_hint(&self) -> (usize, Option<usize>) {
4,064,316✔
483
        (self.size, Some(self.size))
4,064,316✔
484
    }
485
}
486

487
impl<'a, F: RandomAccessDecoderFactory> IntoLender for &'a BvGraph<F> {
488
    type Lender = <BvGraph<F> as SequentialLabeling>::Lender<'a>;
489

490
    #[inline(always)]
491
    fn into_lender(self) -> Self::Lender {
×
492
        self.iter()
×
493
    }
494
}
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