libpijul_compat/apply/
find_alive.rs

1use backend::*;
2use std::collections::HashSet;
3
4#[derive(Debug)]
5pub struct FindAlive {
6    stack: Vec<Key<PatchId>>,
7    visited: HashSet<Key<PatchId>>,
8}
9
10impl FindAlive {
11    pub fn new() -> Self {
12        FindAlive {
13            stack: Vec::new(),
14            visited: HashSet::new(),
15        }
16    }
17    pub fn clear(&mut self) {
18        self.stack.clear();
19        self.visited.clear();
20    }
21    pub fn push(&mut self, k: Key<PatchId>) {
22        self.stack.push(k)
23    }
24    pub fn pop(&mut self) -> Option<Key<PatchId>> {
25        while let Some(p) = self.stack.pop() {
26            if !self.visited.contains(&p) {
27                self.visited.insert(p.clone());
28                return Some(p);
29            }
30        }
31        None
32    }
33}
34
35impl<U: Transaction, R> GenericTxn<U, R> {
36    /// Recursively find all ancestors by doing a DFS, and collect all
37    /// edges until finding an alive ancestor.
38    ///
39    /// Returns whether or not at least one traversed vertex was dead
40    /// (or otherwise said, returns `false` if and only if there all
41    /// vertices in `find_alive` are alive).
42    pub fn find_alive_ancestors(
43        &self,
44        find_alive: &mut FindAlive,
45        branch: &Branch,
46        alive: &mut Vec<Key<PatchId>>,
47        file_ancestor: &mut Option<Key<PatchId>>,
48        files: &mut Vec<(Key<PatchId>, Edge)>,
49    ) -> bool {
50        let mut first_is_alive = false;
51        let mut file = None;
52        while let Some(a) = find_alive.pop() {
53            if self.is_alive(branch, a) {
54                // This node is alive.
55                alive.push(a);
56            } else {
57                first_is_alive = true;
58                let e = Edge::zero(EdgeFlags::PARENT_EDGE | EdgeFlags::DELETED_EDGE);
59                for (_, v) in self.iter_nodes(&branch, Some((a, Some(&e))))
60                    .take_while(|&(k, _)| k == a)
61                {
62                    debug!("find_alive_ancestors: {:?}", v);
63                    if v.flag.contains(EdgeFlags::FOLDER_EDGE) {
64                        // deleted file.
65                        file = Some(a);
66                        *file_ancestor = Some(a)
67                    } else {
68                        find_alive.push(v.dest)
69                    }
70                }
71            }
72        }
73        debug!("file {:?}", file);
74        if let Some(file) = file {
75            find_alive.clear();
76            find_alive.push(file);
77            while let Some(a) = find_alive.pop() {
78                debug!("file {:?}", a);
79                if !self.is_alive(branch, a) {
80                    debug!("not alive");
81                    first_is_alive = true;
82                    let e = Edge::zero(
83                        EdgeFlags::PARENT_EDGE | EdgeFlags::DELETED_EDGE | EdgeFlags::FOLDER_EDGE,
84                    );
85                    for (_, v) in self.iter_nodes(&branch, Some((a, Some(&e))))
86                        .take_while(|&(k, _)| k == a)
87                    {
88                        debug!("file find_alive_ancestors: {:?}", v);
89                        // deleted file, collect.
90                        files.push((a, *v));
91                        find_alive.push(v.dest)
92                    }
93                }
94            }
95        }
96
97        first_is_alive
98    }
99
100    /// Recursively find all descendants by doing a DFS on deleted
101    /// edges (including folder edges), and collect all edges until
102    /// finding an alive or zombie descendant.
103    ///
104    /// Returns whether or not at least one traversed vertex was dead
105    /// (or otherwise said, returns `false` if and only if there all
106    /// vertices in `find_alive` are alive).
107    pub fn find_alive_descendants(
108        &self,
109        find_alive: &mut FindAlive,
110        branch: &Branch,
111        alive: &mut Vec<Key<PatchId>>,
112    ) -> bool {
113        let mut first_is_alive = false;
114        debug!("begin find_alive_descendants");
115        while let Some(a) = find_alive.pop() {
116            debug!("find_alive_descendants, a = {:?}", a);
117            if self.is_alive(branch, a) {
118                debug!("alive: {:?}", a);
119                alive.push(a);
120            } else {
121                // Else, we need to explore its deleted descendants.
122                first_is_alive = true;
123                let e = Edge::zero(EdgeFlags::empty());
124                for (_, v) in self.iter_nodes(&branch, Some((a, Some(&e))))
125                    .take_while(|&(k, _)| k == a)
126                    .filter(|&(_, v)| !v.flag.contains(EdgeFlags::PARENT_EDGE))
127                {
128                    debug!("v = {:?}", v);
129                    if v.flag.contains(EdgeFlags::DELETED_EDGE) {
130                        debug!("find_alive_descendants: {:?}", v);
131                        find_alive.push(v.dest)
132                    } else {
133                        debug!("alive in for: {:?}", v.dest);
134                        alive.push(v.dest)
135                    }
136                }
137            }
138        }
139        debug!("end find_alive_descendants");
140        first_is_alive
141    }
142
143    fn find_alive(
144        &self,
145        branch: &Branch,
146        find_alive: &mut FindAlive,
147        alive: &mut HashSet<Key<PatchId>>,
148        file: &mut Option<Key<PatchId>>,
149        current: Key<PatchId>,
150        flag: EdgeFlags,
151    ) {
152        find_alive.clear();
153        debug!("find_alive: {:?}", current);
154        find_alive.push(current);
155        while let Some(current) = find_alive.pop() {
156            debug!("find_alive, current = {:?}", current);
157            if self.is_alive(branch, current) {
158                alive.insert(current.clone());
159            } else {
160                let e = Edge::zero(flag);
161                for (_, e) in self.iter_nodes(branch, Some((current, Some(&e))))
162                    .take_while(|&(k, v)| {
163                        k == current
164                            && v.flag | EdgeFlags::FOLDER_EDGE | EdgeFlags::PSEUDO_EDGE
165                                == flag | EdgeFlags::FOLDER_EDGE | EdgeFlags::PSEUDO_EDGE
166                    }) {
167                    debug!("e = {:?}", e);
168                    // e might be FOLDER_EDGE here.
169                    if e.flag.contains(EdgeFlags::FOLDER_EDGE) && file.is_none() {
170                        *file = Some(current.clone())
171                    } else {
172                        find_alive.push(e.dest.clone())
173                    }
174                }
175            }
176        }
177    }
178
179    /// Find the alive descendants of `current`. `cache` is here to
180    /// avoid cycles, and `alive` is an accumulator of the
181    /// result. Since this search stops at files, if the file
182    /// containing these lines is ever hit, it will be put in `file`.
183    pub fn find_alive_nonfolder_descendants(
184        &self,
185        branch: &Branch,
186        find_alive: &mut FindAlive,
187        alive: &mut HashSet<Key<PatchId>>,
188        file: &mut Option<Key<PatchId>>,
189        current: Key<PatchId>,
190    ) {
191        self.find_alive(
192            branch,
193            find_alive,
194            alive,
195            file,
196            current,
197            EdgeFlags::DELETED_EDGE,
198        )
199    }
200
201    /// Find the alive ancestors of `current`. `cache` is here to
202    /// avoid cycles, and `alive` is an accumulator of the
203    /// result. Since this search stops at files, if the file
204    /// containing these lines is ever hit, it will be put in `file`.
205    pub fn find_alive_nonfolder_ancestors(
206        &self,
207        branch: &Branch,
208        find_alive: &mut FindAlive,
209        alive: &mut HashSet<Key<PatchId>>,
210        file: &mut Option<Key<PatchId>>,
211        current: Key<PatchId>,
212    ) {
213        self.find_alive(
214            branch,
215            find_alive,
216            alive,
217            file,
218            current,
219            EdgeFlags::DELETED_EDGE | EdgeFlags::PARENT_EDGE,
220        )
221    }
222}