Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion notify/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub enum TargetMode {
/// If the underlying physical entity (inode/File ID) is replaced
/// (e.g., by a move/rename operation), the watch stops monitoring.
///
/// TODO: fsevents backend and Windows backend does not unwatch on physical entity change yet. <https://github.com/rolldown/notify/issues/33>
/// TODO: fsevents backend and Windows backend and polling backend does not unwatch on physical entity change yet. <https://github.com/rolldown/notify/issues/33>
NoTrack,
}

Expand Down
87 changes: 53 additions & 34 deletions notify/src/consolidating_path_trie.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,17 @@ impl<'a, T> PathTrieIter<'a, T> {
}

pub struct ConsolidatingPathTrie {
children_consolidation: bool,

trie: PathTrie<()>,
}

impl ConsolidatingPathTrie {
const CHILDREN_CONSOLIDATION_THRESHOLD: usize = 10;

pub fn new() -> Self {
pub fn new(children_consolidation: bool) -> Self {
Self {
children_consolidation,
trie: PathTrie::new(),
}
}
Expand All @@ -192,14 +195,16 @@ impl ConsolidatingPathTrie {
let inserted = self.trie.insert(path, ());
inserted.remove_children();

for ancestor_path in path.ancestors().skip(1) {
if let Some(parent_node) = self.trie.get_node_mut(ancestor_path)
&& parent_node.children_len() >= Self::CHILDREN_CONSOLIDATION_THRESHOLD
{
parent_node.remove_children();
parent_node.set_value(());
} else {
break;
if self.children_consolidation {
for ancestor_path in path.ancestors().skip(1) {
if let Some(parent_node) = self.trie.get_node_mut(ancestor_path)
&& parent_node.children_len() >= Self::CHILDREN_CONSOLIDATION_THRESHOLD
{
parent_node.remove_children();
parent_node.set_value(());
} else {
break;
}
}
}
}
Expand Down Expand Up @@ -235,54 +240,68 @@ mod tests {

#[test]
fn consolidate_no_siblings() {
let mut ct = ConsolidatingPathTrie::new();
ct.insert(PathBuf::from("/a/b"));
ct.insert(PathBuf::from("/a/c"));
assert_eq!(
ct.values(),
vec![PathBuf::from("/a/b"), PathBuf::from("/a/c")]
);
for children_consolidation in [true, false] {
let mut ct = ConsolidatingPathTrie::new(children_consolidation);
ct.insert(PathBuf::from("/a/b"));
ct.insert(PathBuf::from("/a/c"));
assert_eq!(
ct.values(),
vec![PathBuf::from("/a/b"), PathBuf::from("/a/c")]
);
}
}

#[test]
fn consolidate_no_siblings2() {
let mut ct = ConsolidatingPathTrie::new();
ct.insert(PathBuf::from("/a/b1"));
ct.insert(PathBuf::from("/a/b2"));
assert_eq!(
ct.values(),
vec![PathBuf::from("/a/b1"), PathBuf::from("/a/b2")]
);
for children_consolidation in [true, false] {
let mut ct = ConsolidatingPathTrie::new(children_consolidation);
ct.insert(PathBuf::from("/a/b1"));
ct.insert(PathBuf::from("/a/b2"));
assert_eq!(
ct.values(),
vec![PathBuf::from("/a/b1"), PathBuf::from("/a/b2")]
);
}
}

#[test]
fn consolidate_children() {
let mut ct = ConsolidatingPathTrie::new();
ct.insert(PathBuf::from("/a/b"));
ct.insert(PathBuf::from("/a/b/c"));
assert_eq!(ct.values(), vec![PathBuf::from("/a/b")]);
for children_consolidation in [true, false] {
let mut ct = ConsolidatingPathTrie::new(children_consolidation);
ct.insert(PathBuf::from("/a/b"));
ct.insert(PathBuf::from("/a/b/c"));
assert_eq!(ct.values(), vec![PathBuf::from("/a/b")]);
}
}

#[test]
fn consolidate_parent() {
let mut ct = ConsolidatingPathTrie::new();
ct.insert(PathBuf::from("/a/b/c"));
ct.insert(PathBuf::from("/a/b"));
assert_eq!(ct.values(), vec![PathBuf::from("/a/b")]);
for children_consolidation in [true, false] {
let mut ct = ConsolidatingPathTrie::new(children_consolidation);
ct.insert(PathBuf::from("/a/b/c"));
ct.insert(PathBuf::from("/a/b"));
assert_eq!(ct.values(), vec![PathBuf::from("/a/b")]);
}
}

#[test]
fn consolidate_to_single_parent() {
let mut cr = ConsolidatingPathTrie::new();
let mut cr = ConsolidatingPathTrie::new(true);
for i in 1..=ConsolidatingPathTrie::CHILDREN_CONSOLIDATION_THRESHOLD {
cr.insert(PathBuf::from(format!("/a/b/c{i}")));
}
assert_eq!(cr.values(), vec![PathBuf::from("/a/b")]);

let mut cr = ConsolidatingPathTrie::new(false);
for i in 1..=ConsolidatingPathTrie::CHILDREN_CONSOLIDATION_THRESHOLD {
cr.insert(PathBuf::from(format!("/a/b/c{i}")));
}
assert!(cr.values().len() > 1);
}

#[test]
fn consolidate_to_single_parent_nested1() {
let mut cr = ConsolidatingPathTrie::new();
let mut cr = ConsolidatingPathTrie::new(true);
for i in 1..ConsolidatingPathTrie::CHILDREN_CONSOLIDATION_THRESHOLD {
cr.insert(PathBuf::from(format!("/a/b/c{i}")));
}
Expand All @@ -294,7 +313,7 @@ mod tests {

#[test]
fn consolidate_to_single_parent_nested2() {
let mut cr = ConsolidatingPathTrie::new();
let mut cr = ConsolidatingPathTrie::new(true);
cr.insert(PathBuf::from("/a/b/c1"));
cr.insert(PathBuf::from("/a/b/c2"));
for i in 1..=ConsolidatingPathTrie::CHILDREN_CONSOLIDATION_THRESHOLD {
Expand Down
2 changes: 1 addition & 1 deletion notify/src/fsevent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ impl FsEventWatcher {

fn update_paths_based_on_watches(&mut self) {
let paths_to_watch = {
let mut trie = ConsolidatingPathTrie::new();
let mut trie = ConsolidatingPathTrie::new(true);
for path in self.watches.keys() {
trie.insert(path.clone());
}
Expand Down
1 change: 0 additions & 1 deletion notify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,6 @@ pub mod poll;

mod bimap;
mod config;
#[cfg(all(target_os = "macos", not(feature = "macos_kqueue")))]
mod consolidating_path_trie;
mod error;

Expand Down
Loading