##// END OF EJS Templates
rust-matchers: fix quadratic complexity in `FileMatcher`...
Raphaël Gomès -
r52002:687e192d default
parent child Browse files
Show More
@@ -7,6 +7,9 b''
7
7
8 //! Structs and types for matching files and directories.
8 //! Structs and types for matching files and directories.
9
9
10 use format_bytes::format_bytes;
11 use once_cell::sync::OnceCell;
12
10 use crate::{
13 use crate::{
11 dirstate::dirs_multiset::DirsChildrenMultiset,
14 dirstate::dirs_multiset::DirsChildrenMultiset,
12 filepatterns::{
15 filepatterns::{
@@ -23,11 +26,11 b' use crate::{'
23
26
24 use crate::dirstate::status::IgnoreFnType;
27 use crate::dirstate::status::IgnoreFnType;
25 use crate::filepatterns::normalize_path_bytes;
28 use crate::filepatterns::normalize_path_bytes;
26 use std::borrow::ToOwned;
27 use std::collections::HashSet;
29 use std::collections::HashSet;
28 use std::fmt::{Display, Error, Formatter};
30 use std::fmt::{Display, Error, Formatter};
29 use std::ops::Deref;
31 use std::ops::Deref;
30 use std::path::{Path, PathBuf};
32 use std::path::{Path, PathBuf};
33 use std::{borrow::ToOwned, collections::BTreeSet};
31
34
32 #[derive(Debug, PartialEq)]
35 #[derive(Debug, PartialEq)]
33 pub enum VisitChildrenSet {
36 pub enum VisitChildrenSet {
@@ -173,6 +176,7 b' impl Matcher for NeverMatcher {'
173 pub struct FileMatcher {
176 pub struct FileMatcher {
174 files: HashSet<HgPathBuf>,
177 files: HashSet<HgPathBuf>,
175 dirs: DirsMultiset,
178 dirs: DirsMultiset,
179 sorted_visitchildrenset_candidates: OnceCell<BTreeSet<HgPathBuf>>,
176 }
180 }
177
181
178 impl FileMatcher {
182 impl FileMatcher {
@@ -181,6 +185,7 b' impl FileMatcher {'
181 Ok(Self {
185 Ok(Self {
182 files: HashSet::from_iter(files.into_iter()),
186 files: HashSet::from_iter(files.into_iter()),
183 dirs,
187 dirs,
188 sorted_visitchildrenset_candidates: OnceCell::new(),
184 })
189 })
185 }
190 }
186 fn inner_matches(&self, filename: &HgPath) -> bool {
191 fn inner_matches(&self, filename: &HgPath) -> bool {
@@ -199,30 +204,34 b' impl Matcher for FileMatcher {'
199 self.inner_matches(filename)
204 self.inner_matches(filename)
200 }
205 }
201 fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
206 fn visit_children_set(&self, directory: &HgPath) -> VisitChildrenSet {
202 if self.files.is_empty() || !self.dirs.contains(&directory) {
207 if self.files.is_empty() || !self.dirs.contains(directory) {
203 return VisitChildrenSet::Empty;
208 return VisitChildrenSet::Empty;
204 }
209 }
205 let mut candidates: HashSet<HgPathBuf> =
206 self.dirs.iter().cloned().collect();
207
210
208 candidates.extend(self.files.iter().cloned());
211 let compute_candidates = || -> BTreeSet<HgPathBuf> {
209 candidates.remove(HgPath::new(b""));
212 let mut candidates: BTreeSet<HgPathBuf> =
210
213 self.dirs.iter().cloned().collect();
211 if !directory.as_ref().is_empty() {
214 candidates.extend(self.files.iter().cloned());
212 let directory = [directory.as_ref().as_bytes(), b"/"].concat();
215 candidates.remove(HgPath::new(b""));
213 candidates = candidates
216 candidates
214 .iter()
217 };
215 .filter_map(|c| {
218 let candidates =
216 if c.as_bytes().starts_with(&directory) {
219 if directory.as_ref().is_empty() {
217 Some(HgPathBuf::from_bytes(
220 compute_candidates()
218 &c.as_bytes()[directory.len()..],
221 } else {
219 ))
222 let sorted_candidates = self
220 } else {
223 .sorted_visitchildrenset_candidates
221 None
224 .get_or_init(compute_candidates);
222 }
225 let directory_bytes = directory.as_ref().as_bytes();
223 })
226 let start: HgPathBuf =
224 .collect();
227 format_bytes!(b"{}/", directory_bytes).into();
225 }
228 let start_len = start.len();
229 // `0` sorts after `/`
230 let end = format_bytes!(b"{}0", directory_bytes).into();
231 BTreeSet::from_iter(sorted_candidates.range(start..end).map(
232 |c| HgPathBuf::from_bytes(&c.as_bytes()[start_len..]),
233 ))
234 };
226
235
227 // `self.dirs` includes all of the directories, recursively, so if
236 // `self.dirs` includes all of the directories, recursively, so if
228 // we're attempting to match 'foo/bar/baz.txt', it'll have '', 'foo',
237 // we're attempting to match 'foo/bar/baz.txt', it'll have '', 'foo',
General Comments 0
You need to be logged in to leave comments. Login now