Skip to main content

swh_graph/
arc_iterators.rs

1// Copyright (C) 2023-2026  The Software Heritage developers
2// See the AUTHORS file at the top-level directory of this distribution
3// License: GNU General Public License version 3, or any later version
4// See top-level LICENSE file for more information
5
6//! Structures to manipulate the Software Heritage graph
7//!
8//! In order to load only what is necessary, these structures are initially created
9//! by calling [`SwhUnidirectionalGraph::new`](crate::graph::SwhUnidirectionalGraph::new) or
10//! [`SwhBidirectionalGraph::new`](crate::graph::SwhBidirectionalGraph::new), then calling methods
11//! on them to progressively load additional data (`load_properties`, `load_all_properties`,
12//! `load_labels`)
13
14#![allow(clippy::type_complexity)]
15
16use std::borrow::Borrow;
17use std::iter::Iterator;
18
19use webgraph::prelude::*;
20
21use crate::graph::{NodeId, SwhGraphWithProperties};
22use crate::labels::{EdgeLabel, UntypedEdgeLabel};
23
24pub trait IntoFlattenedLabeledArcsIterator<Label> {
25    type Flattened: IntoIterator<Item = (NodeId, Label)>;
26
27    /// Turns this `Iterator<Item=(succ, Iterator<Item=labels>)>` into an
28    /// `Iterator<Item=(succ, label)>`.
29    fn flatten_labels(self) -> Self::Flattened;
30}
31
32pub struct LabeledSuccessorIterator<Successors: Iterator>
33where
34    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>>>,
35{
36    pub(crate) successors: Successors,
37}
38
39impl<Successors: Iterator> LabeledSuccessorIterator<Successors>
40where
41    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>>>,
42{
43    #[inline(always)]
44    pub fn new(successors: Successors) -> Self {
45        LabeledSuccessorIterator { successors }
46    }
47}
48
49impl<Successors: Iterator> Iterator for LabeledSuccessorIterator<Successors>
50where
51    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>>>,
52{
53    type Item = (
54        NodeId,
55        LabeledArcIterator<
56            <<<Successors as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter,
57        >,
58    );
59
60    fn next(&mut self) -> Option<Self::Item> {
61        self.successors.next().map(|pair| {
62            let (successor, arc_labels) = pair.into_pair();
63            (
64                successor,
65                LabeledArcIterator {
66                    arc_label_ids: arc_labels.into_iter(),
67                },
68            )
69        })
70    }
71}
72
73// SAFETY: if Successors is a sorted iterator of (usize, into sorted iterator),
74// then LabeledSuccessorIterator is sorted because it preserves order
75unsafe impl<Successors: SortedIterator> SortedIterator for LabeledSuccessorIterator<Successors> where
76    <Successors as Iterator>::Item:
77        Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>, IntoIter: SortedIterator>>
78{
79}
80
81impl<Successors: Iterator> IntoFlattenedLabeledArcsIterator<UntypedEdgeLabel>
82    for LabeledSuccessorIterator<Successors>
83where
84    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>>>,
85{
86    type Flattened = FlattenedSuccessorsIterator<Self>;
87
88    #[inline(always)]
89    fn flatten_labels(self) -> Self::Flattened {
90        FlattenedSuccessorsIterator::new(self)
91    }
92}
93
94pub struct FlattenedSuccessorsIterator<Successors: Iterator + ?Sized>
95where
96    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator>,
97{
98    current_node_and_labels: Option<(
99        NodeId,
100        <<<Successors as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter,
101    )>,
102    iter: Successors,
103}
104
105impl<Successors: Iterator> FlattenedSuccessorsIterator<Successors>
106where
107    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator>,
108{
109    #[inline(always)]
110    pub fn new(successors: Successors) -> Self {
111        Self {
112            current_node_and_labels: None,
113            iter: successors,
114        }
115    }
116}
117
118impl<Successors: Iterator + ?Sized> Iterator for FlattenedSuccessorsIterator<Successors>
119where
120    <Successors as Iterator>::Item: Pair<Left = usize, Right: IntoIterator>,
121{
122    type Item = (
123        NodeId,
124        <<<Successors as Iterator>::Item as Pair>::Right as IntoIterator>::Item,
125    );
126
127    fn next(&mut self) -> Option<Self::Item> {
128        if self.current_node_and_labels.is_none() {
129            // first iterator, or exhausted current label list, move to the next one
130            self.current_node_and_labels = self
131                .iter
132                .next()
133                .map(Pair::into_pair)
134                .map(|(succ, labels)| (succ, labels.into_iter()))
135        }
136        let Some((current_node, ref mut current_labels)) = self.current_node_and_labels else {
137            // Reached the end of the iterator
138            return None;
139        };
140        match current_labels.next() {
141            Some(label) => Some((current_node, label)),
142            None => {
143                // exhausted this label list, move to the next one
144                self.current_node_and_labels = None;
145                self.next()
146            }
147        }
148    }
149}
150
151// SAFETY: if Successors is a sorted iterator of (usize, into sorted iterator),
152// then FlattenedSuccessorsIterator is sorted because it preserves order
153unsafe impl<Successors: SortedIterator> SortedIterator for FlattenedSuccessorsIterator<Successors> where
154    <Successors as Iterator>::Item:
155        Pair<Left = usize, Right: IntoIterator<Item: Borrow<u64>, IntoIter: SortedIterator>>
156{
157}
158
159pub struct LabeledArcIterator<T: Iterator<Item: Borrow<u64>>> {
160    arc_label_ids: T,
161}
162
163impl<T: Iterator<Item: Borrow<u64>>> Iterator for LabeledArcIterator<T> {
164    type Item = UntypedEdgeLabel;
165
166    fn next(&mut self) -> Option<Self::Item> {
167        self.arc_label_ids
168            .next()
169            .map(|label| UntypedEdgeLabel::from(*label.borrow()))
170    }
171}
172
173// SAFETY: if T is a sorted iterator of u64,
174// then LabeledArcIterator is sorted because it preserves order
175unsafe impl<T: SortedIterator<Item: Borrow<u64>>> SortedIterator for LabeledArcIterator<T> {}
176
177pub struct LabelTypingSuccessorIterator<'a, G, Successors: Iterator>
178where
179    <Successors as Iterator>::Item:
180        Pair<Left = usize, Right: IntoIterator<Item = UntypedEdgeLabel>>,
181    G: SwhGraphWithProperties + ?Sized,
182    <G as SwhGraphWithProperties>::Maps: crate::properties::Maps,
183{
184    pub(crate) graph: &'a G,
185    pub(crate) is_transposed: bool,
186    pub(crate) successors: Successors,
187    pub(crate) src: NodeId,
188}
189
190impl<'a, G, Successors: Iterator> Iterator for LabelTypingSuccessorIterator<'a, G, Successors>
191where
192    <Successors as Iterator>::Item:
193        Pair<Left = usize, Right: IntoIterator<Item = UntypedEdgeLabel>>,
194    G: SwhGraphWithProperties + ?Sized,
195    <G as SwhGraphWithProperties>::Maps: crate::properties::Maps,
196{
197    type Item = (
198        NodeId,
199        LabelTypingArcIterator<
200            'a,
201            G,
202            <<<Successors as Iterator>::Item as Pair>::Right as IntoIterator>::IntoIter,
203        >,
204    );
205
206    fn next(&mut self) -> Option<Self::Item> {
207        self.successors.next().map(|pair| {
208            let (dst, labels) = pair.into_pair();
209            (
210                dst,
211                LabelTypingArcIterator {
212                    graph: self.graph,
213                    is_transposed: self.is_transposed,
214                    labels: labels.into_iter(),
215                    src: self.src,
216                    dst,
217                },
218            )
219        })
220    }
221}
222
223impl<G, Successors: Iterator> IntoFlattenedLabeledArcsIterator<EdgeLabel>
224    for LabelTypingSuccessorIterator<'_, G, Successors>
225where
226    <Successors as Iterator>::Item:
227        Pair<Left = usize, Right: IntoIterator<Item = UntypedEdgeLabel>>,
228    G: SwhGraphWithProperties + ?Sized,
229    <G as SwhGraphWithProperties>::Maps: crate::properties::Maps,
230{
231    type Flattened = FlattenedSuccessorsIterator<Self>;
232
233    #[inline(always)]
234    fn flatten_labels(self) -> Self::Flattened {
235        FlattenedSuccessorsIterator::new(self)
236    }
237}
238
239pub struct LabelTypingArcIterator<'a, G, Labels: Iterator<Item = UntypedEdgeLabel>>
240where
241    G: SwhGraphWithProperties + ?Sized,
242    <G as SwhGraphWithProperties>::Maps: crate::properties::Maps,
243{
244    graph: &'a G,
245    is_transposed: bool,
246    labels: Labels,
247    src: NodeId,
248    dst: NodeId,
249}
250
251impl<G, Labels: Iterator<Item = UntypedEdgeLabel>> Iterator
252    for LabelTypingArcIterator<'_, G, Labels>
253where
254    G: SwhGraphWithProperties + ?Sized,
255    <G as SwhGraphWithProperties>::Maps: crate::properties::Maps,
256{
257    type Item = EdgeLabel;
258
259    fn next(&mut self) -> Option<Self::Item> {
260        let props = self.graph.properties();
261        self.labels.next().map(move |label| {
262            label
263                .for_edge_type(
264                    props.node_type(self.src),
265                    props.node_type(self.dst),
266                    self.is_transposed,
267                )
268                .unwrap_or_else(|e| {
269                    panic!(
270                        "Unexpected edge from {} ({}) to {} ({}): {}",
271                        props.swhid(self.src),
272                        self.src,
273                        props.swhid(self.dst),
274                        self.dst,
275                        e
276                    )
277                })
278        })
279    }
280}
281
282/// Wraps an iterator of labeled successors, and yields only the successors
283pub struct DelabelingIterator<Successors: Iterator<Item: Pair<Left = usize>>> {
284    pub(crate) successors: Successors,
285}
286impl<Successors: Iterator<Item: Pair<Left = usize>>> Iterator for DelabelingIterator<Successors> {
287    type Item = NodeId;
288
289    fn next(&mut self) -> Option<Self::Item> {
290        self.successors.next().map(|pair| {
291            let (successor, _arc_labels) = pair.into_pair();
292            successor
293        })
294    }
295}
296// SAFETY: DelabelingIterator preserves order
297unsafe impl<Successors: SortedIterator<Item: Pair<Left = usize>>> SortedIterator
298    for DelabelingIterator<Successors>
299{
300}