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

vigna / webgraph-rs / 18095486451

29 Sep 2025 11:31AM UTC coverage: 49.512% (-0.5%) from 49.968%
18095486451

Pull #131

github

web-flow
Merge 5e14ff3c1 into d3762acb7
Pull Request #131: Use dsi-progress-logger in BVComp::parallel_iter

12 of 27 new or added lines in 1 file covered. (44.44%)

696 existing lines in 26 files now uncovered.

3855 of 7786 relevant lines covered (49.51%)

23441674.03 hits per line

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

35.51
/webgraph/src/graphs/bvgraph/load.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 super::*;
9
use crate::prelude::*;
10
use anyhow::{Context, Result};
11
use dsi_bitstream::prelude::*;
12
use dsi_bitstream::{dispatch::code_consts, dispatch::factory::CodesReaderFactoryHelper};
13
use epserde::deser::Owned;
14
use epserde::prelude::*;
15
use sealed::sealed;
16
use std::{
17
    io::BufReader,
18
    path::{Path, PathBuf},
19
};
20

21
/// Sequential or random access.
22
#[doc(hidden)]
23
#[sealed]
24
pub trait Access: 'static {}
25

26
#[derive(Debug, Clone)]
27
pub struct Sequential {}
28
#[sealed]
29
impl Access for Sequential {}
30

31
#[derive(Debug, Clone)]
32
pub struct Random {}
33
#[sealed]
34
impl Access for Random {}
35

36
/// [`Static`] or [`Dynamic`] dispatch.
37
#[sealed]
38
pub trait Dispatch: 'static {}
39

40
/// Static dispatch.
41
///
42
/// You have to specify all codes used of the graph. The defaults
43
/// are the same as the default parameters of the Java version.
44
#[derive(Debug, Clone)]
45
pub struct Static<
46
    const OUTDEGREES: usize = { code_consts::GAMMA },
47
    const REFERENCES: usize = { code_consts::UNARY },
48
    const BLOCKS: usize = { code_consts::GAMMA },
49
    const INTERVALS: usize = { code_consts::GAMMA },
50
    const RESIDUALS: usize = { code_consts::ZETA3 },
51
> {}
52

53
#[sealed]
54
impl<
55
        const OUTDEGREES: usize,
56
        const REFERENCES: usize,
57
        const BLOCKS: usize,
58
        const INTERVALS: usize,
59
        const RESIDUALS: usize,
60
    > Dispatch for Static<OUTDEGREES, REFERENCES, BLOCKS, INTERVALS, RESIDUALS>
61
{
62
}
63

64
/// Dynamic dispatch.
65
///
66
/// Parameters are retrieved from the graph properties.
67
#[derive(Debug, Clone)]
68
pub struct Dynamic {}
69

70
#[sealed]
71
impl Dispatch for Dynamic {}
72

73
/// Load mode.
74
///
75
/// The load mode is the way the graph data is accessed. Each load mode has
76
/// a corresponding strategy to access the graph and the offsets.
77
///
78
/// You can set both modes with [`LoadConfig::mode`], or set them separately with
79
/// [`LoadConfig::graph_mode`] and [`LoadConfig::offsets_mode`].
80
#[sealed]
81
pub trait LoadMode: 'static {
82
    type Factory<E: Endianness>;
83

84
    fn new_factory<E: Endianness, P: AsRef<Path>>(
85
        graph: P,
86
        flags: codecs::MemoryFlags,
87
    ) -> Result<Self::Factory<E>>;
88

89
    type Offsets: Offsets;
90

91
    fn load_offsets<P: AsRef<Path>>(
92
        offsets: P,
93
        flags: MemoryFlags,
94
    ) -> Result<MemCase<Self::Offsets>>;
95
}
96

97
/// A type alias for a buffered reader that reads from a memory buffer a `u32` at a time.
98
pub type MemBufReader<'a, E> = BufBitReader<E, MemWordReader<u32, &'a [u32]>>;
99
/// A type alias for a buffered reader that reads from a file buffer a `u32` at a time.
100
pub type FileBufReader<E> = BufBitReader<E, WordAdapter<u32, BufReader<std::fs::File>>>;
101
/// A type alias for the [`CodesReaderFactory`] associated with a [`LoadMode`].
102
///
103
/// This type can be used in client methods that abstract over endianness to
104
/// impose the necessary trait bounds on the factory associated with the load
105
/// mode: one has just to write, for example, for the [`Mmap`] load mode:
106
/// ```ignore
107
/// LoadModeFactory<E, Mmap>: CodesReaderFactoryHelper<E>
108
/// ```
109
///
110
/// Additional trait bounds on the [`CodesRead`] associated with the factory
111
/// can be imposed by using the [`LoadModeCodesReader`] type alias.
112
pub type LoadModeFactory<E, LM> = <LM as LoadMode>::Factory<E>;
113
/// A type alias for the code reader returned by the [`CodesReaderFactory`]
114
/// associated with a [`LoadMode`].
115
///
116
/// This type can be used in client methods that abstract over endianness to
117
/// impose bounds on the code reader associated to the factory associated with
118
/// the load mode, usually in conjunction with [`LoadModeFactory`]. For example,
119
/// for the [`Mmap`] load mode:
120
/// ```ignore
121
/// LoadModeFactory<E, Mmap>: CodesReaderFactoryHelper<E>
122
/// LoadModeCodesReader<'a, E, Mmap>: BitSeek
123
/// ```
124
pub type LoadModeCodesReader<'a, E, LM> =
125
    <LoadModeFactory<E, LM> as CodesReaderFactory<E>>::CodesReader<'a>;
126

127
/// The graph is read from a file; offsets are fully deserialized in memory.
128
///
129
/// Note that you must guarantee that the graph file is padded with enough
130
/// zeroes so that it can be read one `u32` at a time.
131
#[derive(Debug, Clone)]
132
pub struct File {}
133
#[sealed]
134
impl LoadMode for File {
135
    type Factory<E: Endianness> = FileFactory<E>;
136
    type Offsets = Owned<EF>;
137

138
    fn new_factory<E: Endianness, P: AsRef<Path>>(
×
139
        graph: P,
140
        _flags: MemoryFlags,
141
    ) -> Result<Self::Factory<E>> {
142
        FileFactory::<E>::new(graph)
×
143
    }
144

145
    fn load_offsets<P: AsRef<Path>>(
×
146
        offsets: P,
147
        _flags: MemoryFlags,
148
    ) -> Result<MemCase<Self::Offsets>> {
149
        let path = offsets.as_ref();
×
150
        unsafe {
151
            EF::load_full(path)
×
152
                .with_context(|| format!("Cannot load Elias-Fano pointer list {}", path.display()))
×
UNCOV
153
                .map(Into::into)
×
154
        }
155
    }
156
}
157

158
/// The graph and offsets are memory mapped.
159
///
160
/// This is the default mode. You can [set memory-mapping flags](LoadConfig::flags).
161
#[derive(Debug, Clone)]
162
pub struct Mmap {}
163
#[sealed]
164
impl LoadMode for Mmap {
165
    type Factory<E: Endianness> = MmapHelper<u32>;
166
    type Offsets = EF;
167

168
    fn new_factory<E: Endianness, P: AsRef<Path>>(
75✔
169
        graph: P,
170
        flags: MemoryFlags,
171
    ) -> Result<Self::Factory<E>> {
172
        MmapHelper::mmap(graph, flags.into())
300✔
173
    }
174

175
    fn load_offsets<P: AsRef<Path>>(
36✔
176
        offsets: P,
177
        flags: MemoryFlags,
178
    ) -> Result<MemCase<Self::Offsets>> {
179
        let path = offsets.as_ref();
108✔
180
        unsafe {
181
            EF::mmap(path, flags.into())
144✔
182
                .with_context(|| format!("Cannot map Elias-Fano pointer list {}", path.display()))
36✔
183
        }
184
    }
185
}
186

187
/// The graph and offsets are loaded into allocated memory.
188
#[derive(Debug, Clone)]
189
pub struct LoadMem {}
190
#[sealed]
191
impl LoadMode for LoadMem {
192
    type Factory<E: Endianness> = MemoryFactory<E, Box<[u32]>>;
193
    type Offsets = EF;
194

195
    fn new_factory<E: Endianness, P: AsRef<Path>>(
×
196
        graph: P,
197
        _flags: MemoryFlags,
198
    ) -> Result<Self::Factory<E>> {
UNCOV
199
        MemoryFactory::<E, _>::new_mem(graph)
×
200
    }
201

202
    fn load_offsets<P: AsRef<Path>>(
×
203
        offsets: P,
204
        _flags: MemoryFlags,
205
    ) -> Result<MemCase<Self::Offsets>> {
UNCOV
206
        let path = offsets.as_ref();
×
207
        unsafe {
UNCOV
208
            EF::load_mem(path)
×
UNCOV
209
                .with_context(|| format!("Cannot load Elias-Fano pointer list {}", path.display()))
×
210
        }
211
    }
212
}
213

214
/// The graph and offsets are loaded into memory obtained via `mmap()`.
215
///
216
/// You can [set memory-mapping flags](LoadConfig::flags).
217
#[derive(Debug, Clone)]
218
pub struct LoadMmap {}
219
#[sealed]
220
impl LoadMode for LoadMmap {
221
    type Factory<E: Endianness> = MemoryFactory<E, MmapHelper<u32>>;
222
    type Offsets = EF;
223

224
    fn new_factory<E: Endianness, P: AsRef<Path>>(
8✔
225
        graph: P,
226
        flags: MemoryFlags,
227
    ) -> Result<Self::Factory<E>> {
228
        MemoryFactory::<E, _>::new_mmap(graph, flags)
24✔
229
    }
230

231
    fn load_offsets<P: AsRef<Path>>(
8✔
232
        offsets: P,
233
        flags: MemoryFlags,
234
    ) -> Result<MemCase<Self::Offsets>> {
235
        let path = offsets.as_ref();
24✔
236
        unsafe {
237
            EF::load_mmap(path, flags.into())
32✔
238
                .with_context(|| format!("Cannot load Elias-Fano pointer list {}", path.display()))
8✔
239
        }
240
    }
241
}
242

243
/// A load configuration for a [`BvGraph`]/[`BvGraphSeq`].
244
///
245
/// A basic configuration is returned by
246
/// [`BvGraph::with_basename`]/[`BvGraphSeq::with_basename`]. The configuration
247
/// can then be customized using the methods of this struct.
248
#[derive(Debug, Clone)]
249
pub struct LoadConfig<E: Endianness, A: Access, D: Dispatch, GLM: LoadMode, OLM: LoadMode> {
250
    pub(crate) basename: PathBuf,
251
    pub(crate) graph_load_flags: MemoryFlags,
252
    pub(crate) offsets_load_flags: MemoryFlags,
253
    pub(crate) _marker: std::marker::PhantomData<(E, A, D, GLM, OLM)>,
254
}
255

256
impl<E: Endianness, A: Access, D: Dispatch, GLM: LoadMode, OLM: LoadMode>
257
    LoadConfig<E, A, D, GLM, OLM>
258
{
259
    /// Set the endianness of the graph and offsets file.
260
    pub fn endianness<E2: Endianness>(self) -> LoadConfig<E2, A, D, GLM, OLM>
70✔
261
    where
262
        GLM: LoadMode,
263
        OLM: LoadMode,
264
    {
265
        LoadConfig {
266
            basename: self.basename,
140✔
267
            graph_load_flags: self.graph_load_flags,
140✔
268
            offsets_load_flags: self.offsets_load_flags,
70✔
269
            _marker: std::marker::PhantomData,
270
        }
271
    }
272
}
273

274
impl<E: Endianness, A: Access, D: Dispatch, GLM: LoadMode, OLM: LoadMode>
275
    LoadConfig<E, A, D, GLM, OLM>
276
{
277
    /// Choose between [`Static`] and [`Dynamic`] dispatch.
UNCOV
278
    pub fn dispatch<D2: Dispatch>(self) -> LoadConfig<E, A, D2, GLM, OLM> {
×
279
        LoadConfig {
UNCOV
280
            basename: self.basename,
×
UNCOV
281
            graph_load_flags: self.graph_load_flags,
×
UNCOV
282
            offsets_load_flags: self.offsets_load_flags,
×
283
            _marker: std::marker::PhantomData,
284
        }
285
    }
286
}
287

288
impl<E: Endianness, A: Access, D: Dispatch, GLM: LoadMode, OLM: LoadMode>
289
    LoadConfig<E, A, D, GLM, OLM>
290
{
291
    /// Choose the [`LoadMode`] for the graph and offsets.
292
    pub fn mode<LM: LoadMode>(self) -> LoadConfig<E, A, D, LM, LM> {
8✔
293
        LoadConfig {
294
            basename: self.basename,
16✔
295
            graph_load_flags: self.graph_load_flags,
16✔
296
            offsets_load_flags: self.offsets_load_flags,
8✔
297
            _marker: std::marker::PhantomData,
298
        }
299
    }
300
}
301

302
impl<E: Endianness, A: Access, D: Dispatch> LoadConfig<E, A, D, Mmap, Mmap> {
303
    /// Set flags for memory-mapping (both graph and offsets).
UNCOV
304
    pub fn flags(self, flags: MemoryFlags) -> LoadConfig<E, A, D, Mmap, Mmap> {
×
305
        LoadConfig {
UNCOV
306
            basename: self.basename,
×
307
            graph_load_flags: flags,
308
            offsets_load_flags: flags,
309
            _marker: std::marker::PhantomData,
310
        }
311
    }
312
}
313

314
impl<E: Endianness, A: Access, D: Dispatch> LoadConfig<E, A, D, LoadMmap, LoadMmap> {
315
    /// Set flags for memory obtained from `mmap()` (both graph and offsets).
316
    pub fn flags(self, flags: MemoryFlags) -> LoadConfig<E, A, D, LoadMmap, LoadMmap> {
8✔
317
        LoadConfig {
318
            basename: self.basename,
16✔
319
            graph_load_flags: flags,
320
            offsets_load_flags: flags,
321
            _marker: std::marker::PhantomData,
322
        }
323
    }
324
}
325

326
impl<E: Endianness, A: Access, D: Dispatch, GLM: LoadMode, OLM: LoadMode>
327
    LoadConfig<E, A, D, GLM, OLM>
328
{
329
    /// Choose the [`LoadMode`] for the graph only.
UNCOV
330
    pub fn graph_mode<NGLM: LoadMode>(self) -> LoadConfig<E, A, D, NGLM, OLM> {
×
331
        LoadConfig {
UNCOV
332
            basename: self.basename,
×
UNCOV
333
            graph_load_flags: self.graph_load_flags,
×
334
            offsets_load_flags: self.offsets_load_flags,
×
335
            _marker: std::marker::PhantomData,
336
        }
337
    }
338
}
339

340
impl<E: Endianness, A: Access, D: Dispatch, OLM: LoadMode> LoadConfig<E, A, D, Mmap, OLM> {
341
    /// Set flags for memory-mapping the graph.
UNCOV
342
    pub fn graph_flags(self, flags: MemoryFlags) -> LoadConfig<E, A, D, Mmap, OLM> {
×
343
        LoadConfig {
UNCOV
344
            basename: self.basename,
×
345
            graph_load_flags: flags,
346
            offsets_load_flags: self.offsets_load_flags,
×
347
            _marker: std::marker::PhantomData,
348
        }
349
    }
350
}
351

352
impl<E: Endianness, A: Access, D: Dispatch, OLM: LoadMode> LoadConfig<E, A, D, LoadMmap, OLM> {
353
    /// Set flags for memory obtained from `mmap()` for the graph.
UNCOV
354
    pub fn graph_flags(self, flags: MemoryFlags) -> LoadConfig<E, A, D, LoadMmap, OLM> {
×
355
        LoadConfig {
UNCOV
356
            basename: self.basename,
×
357
            graph_load_flags: flags,
358
            offsets_load_flags: self.offsets_load_flags,
×
359
            _marker: std::marker::PhantomData,
360
        }
361
    }
362
}
363

364
impl<E: Endianness, D: Dispatch, GLM: LoadMode, OLM: LoadMode> LoadConfig<E, Random, D, GLM, OLM> {
365
    /// Choose the [`LoadMode`] for the graph only.
UNCOV
366
    pub fn offsets_mode<NOLM: LoadMode>(self) -> LoadConfig<E, Random, D, GLM, NOLM> {
×
367
        LoadConfig {
UNCOV
368
            basename: self.basename,
×
UNCOV
369
            graph_load_flags: self.graph_load_flags,
×
370
            offsets_load_flags: self.offsets_load_flags,
×
371
            _marker: std::marker::PhantomData,
372
        }
373
    }
374
}
375

376
impl<E: Endianness, D: Dispatch, GLM: LoadMode> LoadConfig<E, Random, D, GLM, Mmap> {
377
    /// Set flags for memory-mapping the offsets.
UNCOV
378
    pub fn offsets_flags(self, flags: MemoryFlags) -> LoadConfig<E, Random, D, GLM, Mmap> {
×
379
        LoadConfig {
UNCOV
380
            basename: self.basename,
×
UNCOV
381
            graph_load_flags: self.graph_load_flags,
×
382
            offsets_load_flags: flags,
383
            _marker: std::marker::PhantomData,
384
        }
385
    }
386
}
387

388
impl<E: Endianness, D: Dispatch, GLM: LoadMode> LoadConfig<E, Random, D, GLM, LoadMmap> {
389
    /// Set flags for memory obtained from `mmap()` for the graph.
UNCOV
390
    pub fn offsets_flags(self, flags: MemoryFlags) -> LoadConfig<E, Random, D, GLM, LoadMmap> {
×
391
        LoadConfig {
UNCOV
392
            basename: self.basename,
×
UNCOV
393
            graph_load_flags: self.graph_load_flags,
×
394
            offsets_load_flags: flags,
395
            _marker: std::marker::PhantomData,
396
        }
397
    }
398
}
399

400
impl<E: Endianness, GLM: LoadMode, OLM: LoadMode> LoadConfig<E, Random, Dynamic, GLM, OLM> {
401
    /// Load a random-access graph with dynamic dispatch.
402
    #[allow(clippy::type_complexity)]
403
    pub fn load(
44✔
404
        mut self,
405
    ) -> anyhow::Result<BvGraph<DynCodesDecoderFactory<E, GLM::Factory<E>, OLM::Offsets>>>
406
    where
407
        <GLM as LoadMode>::Factory<E>: CodesReaderFactoryHelper<E>,
408
        for<'a> LoadModeCodesReader<'a, E, GLM>: CodesRead<E> + BitSeek,
409
    {
410
        self.basename.set_extension(PROPERTIES_EXTENSION);
88✔
411
        let (num_nodes, num_arcs, comp_flags) = parse_properties::<E>(&self.basename)
132✔
412
            .with_context(|| {
44✔
UNCOV
413
                format!("Could not load properties file {}", self.basename.display())
×
414
            })?;
415
        self.basename.set_extension(GRAPH_EXTENSION);
×
416
        let factory = GLM::new_factory(&self.basename, self.graph_load_flags)
44✔
417
            .with_context(|| format!("Could not graph file {}", self.basename.display()))?;
×
418
        self.basename.set_extension(EF_EXTENSION);
×
419
        let offsets = OLM::load_offsets(&self.basename, self.offsets_load_flags)
44✔
UNCOV
420
            .with_context(|| format!("Could not offsets file {}", self.basename.display()))?;
×
421

422
        Ok(BvGraph::new(
44✔
UNCOV
423
            DynCodesDecoderFactory::new(factory, offsets, comp_flags)?,
×
UNCOV
424
            num_nodes,
×
UNCOV
425
            num_arcs,
×
UNCOV
426
            comp_flags.compression_window,
×
UNCOV
427
            comp_flags.min_interval_length,
×
428
        ))
429
    }
430
}
431

432
impl<E: Endianness, GLM: LoadMode, OLM: LoadMode> LoadConfig<E, Sequential, Dynamic, GLM, OLM> {
433
    /// Load a sequential graph with dynamic dispatch.
434
    #[allow(clippy::type_complexity)]
435
    pub fn load(
39✔
436
        mut self,
437
    ) -> anyhow::Result<
438
        BvGraphSeq<DynCodesDecoderFactory<E, GLM::Factory<E>, Owned<EmptyDict<usize, usize>>>>,
439
    >
440
    where
441
        <GLM as LoadMode>::Factory<E>: CodesReaderFactoryHelper<E>,
442
        for<'a> LoadModeCodesReader<'a, E, GLM>: CodesRead<E>,
443
    {
444
        self.basename.set_extension(PROPERTIES_EXTENSION);
78✔
445
        let (num_nodes, num_arcs, comp_flags) = parse_properties::<E>(&self.basename)?;
117✔
446
        self.basename.set_extension(GRAPH_EXTENSION);
×
447
        let factory = GLM::new_factory(&self.basename, self.graph_load_flags)?;
39✔
448

449
        Ok(BvGraphSeq::new(
39✔
UNCOV
450
            DynCodesDecoderFactory::new(factory, EmptyDict::default().into(), comp_flags)?,
×
UNCOV
451
            num_nodes,
×
UNCOV
452
            Some(num_arcs),
×
UNCOV
453
            comp_flags.compression_window,
×
UNCOV
454
            comp_flags.min_interval_length,
×
455
        ))
456
    }
457
}
458

459
impl<
460
        E: Endianness,
461
        GLM: LoadMode,
462
        OLM: LoadMode,
463
        const OUTDEGREES: usize,
464
        const REFERENCES: usize,
465
        const BLOCKS: usize,
466
        const INTERVALS: usize,
467
        const RESIDUALS: usize,
468
    >
469
    LoadConfig<E, Random, Static<OUTDEGREES, REFERENCES, BLOCKS, INTERVALS, RESIDUALS>, GLM, OLM>
470
{
471
    /// Load a random-access graph with static dispatch.
472
    #[allow(clippy::type_complexity)]
UNCOV
473
    pub fn load(
×
474
        mut self,
475
    ) -> anyhow::Result<
476
        BvGraph<
477
            ConstCodesDecoderFactory<
478
                E,
479
                GLM::Factory<E>,
480
                OLM::Offsets,
481
                OUTDEGREES,
482
                REFERENCES,
483
                BLOCKS,
484
                INTERVALS,
485
                RESIDUALS,
486
            >,
487
        >,
488
    >
489
    where
490
        <GLM as LoadMode>::Factory<E>: CodesReaderFactoryHelper<E>,
491
        for<'a> LoadModeCodesReader<'a, E, GLM>: CodesRead<E> + BitSeek,
492
    {
493
        self.basename.set_extension(PROPERTIES_EXTENSION);
×
494
        let (num_nodes, num_arcs, comp_flags) = parse_properties::<E>(&self.basename)?;
×
495
        self.basename.set_extension(GRAPH_EXTENSION);
×
496
        let factory = GLM::new_factory(&self.basename, self.graph_load_flags)?;
×
497
        self.basename.set_extension(EF_EXTENSION);
×
UNCOV
498
        let offsets = OLM::load_offsets(&self.basename, self.offsets_load_flags)?;
×
499

UNCOV
500
        Ok(BvGraph::new(
×
UNCOV
501
            ConstCodesDecoderFactory::new(factory, offsets, comp_flags)?,
×
UNCOV
502
            num_nodes,
×
UNCOV
503
            num_arcs,
×
UNCOV
504
            comp_flags.compression_window,
×
UNCOV
505
            comp_flags.min_interval_length,
×
506
        ))
507
    }
508
}
509

510
impl<
511
        E: Endianness,
512
        GLM: LoadMode,
513
        OLM: LoadMode,
514
        const OUTDEGREES: usize,
515
        const REFERENCES: usize,
516
        const BLOCKS: usize,
517
        const INTERVALS: usize,
518
        const RESIDUALS: usize,
519
    >
520
    LoadConfig<
521
        E,
522
        Sequential,
523
        Static<OUTDEGREES, REFERENCES, BLOCKS, INTERVALS, RESIDUALS>,
524
        GLM,
525
        OLM,
526
    >
527
{
528
    /// Load a sequential graph with static dispatch.
529
    #[allow(clippy::type_complexity)]
UNCOV
530
    pub fn load(
×
531
        mut self,
532
    ) -> anyhow::Result<
533
        BvGraphSeq<
534
            ConstCodesDecoderFactory<
535
                E,
536
                GLM::Factory<E>,
537
                Owned<EmptyDict<usize, usize>>,
538
                OUTDEGREES,
539
                REFERENCES,
540
                BLOCKS,
541
                INTERVALS,
542
                RESIDUALS,
543
            >,
544
        >,
545
    >
546
    where
547
        <GLM as LoadMode>::Factory<E>: CodesReaderFactoryHelper<E>,
548
        for<'a> LoadModeCodesReader<'a, E, GLM>: CodesRead<E>,
549
    {
550
        self.basename.set_extension(PROPERTIES_EXTENSION);
×
551
        let (num_nodes, num_arcs, comp_flags) = parse_properties::<E>(&self.basename)?;
×
UNCOV
552
        self.basename.set_extension(GRAPH_EXTENSION);
×
553
        let factory = GLM::new_factory(&self.basename, self.graph_load_flags)?;
×
554

555
        Ok(BvGraphSeq::new(
×
556
            ConstCodesDecoderFactory::new(factory, EmptyDict::default().into(), comp_flags)?,
×
UNCOV
557
            num_nodes,
×
UNCOV
558
            Some(num_arcs),
×
UNCOV
559
            comp_flags.compression_window,
×
UNCOV
560
            comp_flags.min_interval_length,
×
561
        ))
562
    }
563
}
564

565
/// Read the .properties file and return the endianness
566
pub fn get_endianness<P: AsRef<Path>>(basename: P) -> Result<String> {
44✔
567
    let path = basename.as_ref().with_extension(PROPERTIES_EXTENSION);
132✔
568
    let f = std::fs::File::open(&path)
132✔
569
        .with_context(|| format!("Cannot open property file {}", path.display()))?;
44✔
570
    let map = java_properties::read(BufReader::new(f))
44✔
UNCOV
571
        .with_context(|| format!("cannot parse {} as a java properties file", path.display()))?;
×
572

UNCOV
573
    let endianness = map
×
574
        .get("endianness")
575
        .map(|x| x.to_string())
48✔
576
        .unwrap_or_else(|| BigEndian::NAME.to_string());
40✔
577

UNCOV
578
    Ok(endianness)
×
579
}
580

581
/// Read the .properties file and return the number of nodes, number of arcs and compression flags
582
/// for the graph. The endianness is checked against the expected one.
583
pub fn parse_properties<E: Endianness>(path: impl AsRef<Path>) -> Result<(usize, u64, CompFlags)> {
83✔
584
    let name = path.as_ref().display();
249✔
585
    let f =
83✔
586
        std::fs::File::open(&path).with_context(|| format!("Cannot open property file {name}"))?;
249✔
587
    let map = java_properties::read(BufReader::new(f))
83✔
588
        .with_context(|| format!("cannot parse {name} as a java properties file"))?;
×
589

590
    let num_nodes = map
83✔
591
        .get("nodes")
UNCOV
592
        .with_context(|| format!("Missing 'nodes' property in {name}"))?
×
593
        .parse::<usize>()
UNCOV
594
        .with_context(|| format!("Cannot parse 'nodes' as usize in {name}"))?;
×
595
    let num_arcs = map
83✔
596
        .get("arcs")
UNCOV
597
        .with_context(|| format!("Missing 'arcs' property in {name}"))?
×
598
        .parse::<u64>()
599
        .with_context(|| format!("Cannot parse arcs as usize in {name}"))?;
×
600

601
    let comp_flags = CompFlags::from_properties::<E>(&map)
83✔
UNCOV
602
        .with_context(|| format!("Cannot parse compression flags from {name}"))?;
×
UNCOV
603
    Ok((num_nodes, num_arcs, comp_flags))
×
604
}
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