From: Felix S. Klock II Date: Fri, 9 Oct 2015 16:02:37 +0000 (+0200) Subject: Major revision to the dropck_legal_cycles test. X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=098a7a07ee6d11cf6d2b9d18918f26be95ee2f66;p=rust.git Major revision to the dropck_legal_cycles test. 1. Added big comment block explaining the test framework. 2. Added tests exericising Rc and Arc. This was inspired by a comment from eefriedman on PR #28861. 3. Made the cycle-detection not issue false-positives on acyclic dags. Doing this efficiently required revising the framework; instead of visiting all children (i.e. doing a traversal), now each test is responsible for supplying the path that will act as a witness to the cycle. Luckily for me, all of the pre-existing tests worked with a trivial path built from "always tke your first left", but new tests I added did require other input paths (i.e., "first turn right, then left". (The path representation is a bit-string and its branches are n-ary, not word phrases and binary branches as you might think from the outline above.) --- diff --git a/src/test/run-pass/dropck_legal_cycles.rs b/src/test/run-pass/dropck_legal_cycles.rs index 2770260a10e..a31df0fd93e 100644 --- a/src/test/run-pass/dropck_legal_cycles.rs +++ b/src/test/run-pass/dropck_legal_cycles.rs @@ -24,9 +24,92 @@ // through the collection, for every collection type that supports // this. -#![feature(vecmap)] +// HIGH LEVEL DESCRIPTION OF THE TEST ARCHITECTURE +// ----------------------------------------------- +// +// We pick a data structure and want to make a cyclic construction +// from it. Each test of interest is labelled starting with "Cycle N: +// { ... }" where N is the test number and the "..."`is filled in with +// a graphviz-style description of the graph structure that the +// author believes is being made. So "{ a -> b, b -> (c,d), (c,d) -> e }" +// describes a line connected to a diamond: +// +// c +// / \ +// a - b e +// \ / +// d +// +// (Note that the above directed graph is actually acyclic.) +// +// The different graph structures are often composed of different data +// types. Some may be built atop `Vec`, others atop `HashMap`, etc. +// +// For each graph structure, we actually *confirm* that a cycle exists +// (as a safe-guard against a test author accidentally leaving it out) +// by traversing each graph and "proving" that a cycle exists within it. +// +// To do this, while trying to keep the code uniform (despite working +// with different underlying collection and smart-pointer types), we +// have a standard traversal API: +// +// 1. every node in the graph carries a `mark` (a u32, init'ed to 0). +// +// 2. every node provides a method to visit its children +// +// 3. a traversal attmepts to visit the nodes of the graph and prove that +// it sees the same node twice. It does this by setting the mark of each +// node to a fresh non-zero value, and if it sees the current mark, it +// "knows" that it must have found a cycle, and stops attempting further +// traversal. +// +// 4. each traversal is controlled by a bit-string that tells it which child +// it visit when it can take different paths. As a simple example, +// in a binary tree, 0 could mean "left" (and 1, "right"), so that +// "00010" means "left, left, left, right, left". (In general it will +// read as many bits as it needs to choose one child.) +// +// The graphs in this test are all meant to be very small, and thus +// short bitstrings of less than 64 bits should always suffice. +// +// (An earlier version of this test infrastructure simply had any +// given traversal visit all children it encountered, in a +// depth-first manner; one problem with this approach is that an +// acyclic graph can still have sharing, which would then be treated +// as a repeat mark and reported as a detected cycle.) +// +// The travseral code is a little more complicated because it has been +// programmed in a somewhat defensive manner. For example it also has +// a max threshold for the number of nodes it will visit, to guard +// against scenarios where the nodes are not correctly setting their +// mark when asked. There are various other methods not discussed here +// that are for aiding debugging the test when it runs, such as the +// `name` method that all nodes provide. +// +// So each test: +// +// 1. allocates the nodes in the graph, +// +// 2. sets up the links in the graph, +// +// 3. clones the "ContextData" +// +// 4. chooses a new current mark value for this test +// +// 5. initiates a traversal, potentially from multiple starting points +// (aka "roots"), with a given control-string (potentially a +// different string for each root). if it does start from a +// distinct root, then such a test should also increment the +// current mark value, so that this traversal is considered +// distinct from the prior one on this graph structure. +// +// Note that most of the tests work with the default control string +// of all-zeroes. +// +// 6. assert that the context confirms that it actually saw a cycle (since a traversal +// might have terminated, e.g. on a tree structure that contained no cycles). -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::BinaryHeap; use std::collections::HashMap; @@ -35,6 +118,8 @@ use std::collections::btree_map::BTreeMap; use std::collections::btree_set::BTreeSet; use std::hash::{Hash, Hasher}; +use std::rc::Rc; +use std::sync::{Arc, RwLock, Mutex}; const PRINT: bool = false; @@ -47,8 +132,28 @@ pub fn main() { skipped: 0, curr_mark: 0, saw_prev_marked: false, + control_bits: 0, }; + // SANITY CHECK FOR TEST SUITE (thus unnumbered) + // Not a cycle: { v[0] -> (v[1], v[2]), v[1] -> v[3], v[2] -> v[3] }; + let v: Vec = vec![Named::new("s0"), + Named::new("s1"), + Named::new("s2"), + Named::new("s3")]; + v[0].next.set((Some(&v[1]), Some(&v[2]))); + v[1].next.set((Some(&v[3]), None)); + v[2].next.set((Some(&v[3]), None)); + v[3].next.set((None, None)); + + let mut c = c_orig.clone(); + c.curr_mark = 10; + assert!(!c.saw_prev_marked); + v[0].descend_into_self(&mut c); + assert!(!c.saw_prev_marked); // <-- different from below, b/c acyclic above + + if PRINT { println!(""); } + // Cycle 1: { v[0] -> v[1], v[1] -> v[0] }; // does not exercise `v` itself let v: Vec = vec![Named::new("s0"), @@ -59,7 +164,7 @@ pub fn main() { let mut c = c_orig.clone(); c.curr_mark = 10; assert!(!c.saw_prev_marked); - v[0].for_each_child(&mut c); + v[0].descend_into_self(&mut c); assert!(c.saw_prev_marked); if PRINT { println!(""); } @@ -72,7 +177,7 @@ pub fn main() { let mut c = c_orig.clone(); c.curr_mark = 20; assert!(!c.saw_prev_marked); - v.for_each_child(&mut c); + v.descend_into_self(&mut c); assert!(c.saw_prev_marked); if PRINT { println!(""); } @@ -93,7 +198,7 @@ pub fn main() { for (key, _) in h.iter() { c.curr_mark += 1; c.saw_prev_marked = false; - key.for_each_child(&mut c); + key.descend_into_self(&mut c); assert!(c.saw_prev_marked); } @@ -115,7 +220,7 @@ pub fn main() { for (key, _) in h.iter() { c.curr_mark += 1; c.saw_prev_marked = false; - key.for_each_child(&mut c); + key.descend_into_self(&mut c); assert!(c.saw_prev_marked); // break; } @@ -133,7 +238,7 @@ pub fn main() { let mut c = c_orig.clone(); c.curr_mark = 50; assert!(!c.saw_prev_marked); - vd[0].for_each_child(&mut c); + vd[0].descend_into_self(&mut c); assert!(c.saw_prev_marked); if PRINT { println!(""); } @@ -148,7 +253,7 @@ pub fn main() { let mut c = c_orig.clone(); c.curr_mark = 60; assert!(!c.saw_prev_marked); - vd[0].for_each_child(&mut c); + vd[0].descend_into_self(&mut c); assert!(c.saw_prev_marked); if PRINT { println!(""); } @@ -163,7 +268,7 @@ pub fn main() { let mut c = c_orig.clone(); c.curr_mark = 70; assert!(!c.saw_prev_marked); - vm[&0].for_each_child(&mut c); + vm[&0].descend_into_self(&mut c); assert!(c.saw_prev_marked); if PRINT { println!(""); } @@ -181,7 +286,7 @@ pub fn main() { for e in &ll { c.curr_mark += 1; c.saw_prev_marked = false; - e.for_each_child(&mut c); + e.descend_into_self(&mut c); assert!(c.saw_prev_marked); // break; } @@ -201,7 +306,7 @@ pub fn main() { for b in &bh { c.curr_mark += 1; c.saw_prev_marked = false; - b.for_each_child(&mut c); + b.descend_into_self(&mut c); assert!(c.saw_prev_marked); // break; } @@ -222,7 +327,7 @@ pub fn main() { for (k, _) in &btm { c.curr_mark += 1; c.saw_prev_marked = false; - k.for_each_child(&mut c); + k.descend_into_self(&mut c); assert!(c.saw_prev_marked); // break; } @@ -242,10 +347,98 @@ pub fn main() { for b in &bts { c.curr_mark += 1; c.saw_prev_marked = false; - b.for_each_child(&mut c); + b.descend_into_self(&mut c); assert!(c.saw_prev_marked); // break; } + + if PRINT { println!(""); } + + // Cycle 11: { rc0 -> (rc1, rc2), rc1 -> (), rc2 -> rc0 } + let (rc0, rc1, rc2): (RCRC, RCRC, RCRC); + rc0 = RCRC::new("rcrc0"); + rc1 = RCRC::new("rcrc1"); + rc2 = RCRC::new("rcrc2"); + rc0.0.borrow_mut().children.0 = Some(&rc1); + rc0.0.borrow_mut().children.1 = Some(&rc2); + rc2.0.borrow_mut().children.0 = Some(&rc0); + + let mut c = c_orig.clone(); + c.control_bits = 0b1; + c.curr_mark = 110; + assert!(!c.saw_prev_marked); + rc0.descend_into_self(&mut c); + assert!(c.saw_prev_marked); + + if PRINT { println!(""); } + + // We want to take the previous Rc case and generalize it to Arc. + // + // We can use refcells if we're single-threaded (as this test is). + // If one were to generalize these constructions to a + // multi-threaded context, then it might seem like we could choose + // between either a RwLock or a Mutex to hold the owned arcs on + // each node. + // + // Part of the point of this test is to actually confirm that the + // cycle exists by traversing it. We can do that just fine with an + // RwLock (since we can grab the child pointers in read-only + // mode), but we cannot lock a std::sync::Mutex to guard reading + // from each node via the same pattern, since once you hit the + // cycle, you'll be trying to acquring the same lock twice. + // (We deal with this by exiting the traversal early if try_lock fails.) + + // Cycle 12: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, refcells + let (arc0, arc1, arc2): (ARCRC, ARCRC, ARCRC); + arc0 = ARCRC::new("arcrc0"); + arc1 = ARCRC::new("arcrc1"); + arc2 = ARCRC::new("arcrc2"); + arc0.0.borrow_mut().children.0 = Some(&arc1); + arc0.0.borrow_mut().children.1 = Some(&arc2); + arc2.0.borrow_mut().children.0 = Some(&arc0); + + let mut c = c_orig.clone(); + c.control_bits = 0b1; + c.curr_mark = 110; + assert!(!c.saw_prev_marked); + arc0.descend_into_self(&mut c); + assert!(c.saw_prev_marked); + + if PRINT { println!(""); } + + // Cycle 13: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, rwlocks + let (arc0, arc1, arc2): (ARCRW, ARCRW, ARCRW); + arc0 = ARCRW::new("arcrw0"); + arc1 = ARCRW::new("arcrw1"); + arc2 = ARCRW::new("arcrw2"); + arc0.0.write().unwrap().children.0 = Some(&arc1); + arc0.0.write().unwrap().children.1 = Some(&arc2); + arc2.0.write().unwrap().children.0 = Some(&arc0); + + let mut c = c_orig.clone(); + c.control_bits = 0b1; + c.curr_mark = 110; + assert!(!c.saw_prev_marked); + arc0.descend_into_self(&mut c); + assert!(c.saw_prev_marked); + + if PRINT { println!(""); } + + // Cycle 14: { arc0 -> (arc1, arc2), arc1 -> (), arc2 -> arc0 }, mutexs + let (arc0, arc1, arc2): (ARCM, ARCM, ARCM); + arc0 = ARCM::new("arcm0"); + arc1 = ARCM::new("arcm1"); + arc2 = ARCM::new("arcm2"); + arc0.1.lock().unwrap().children.0 = Some(&arc1); + arc0.1.lock().unwrap().children.1 = Some(&arc2); + arc2.1.lock().unwrap().children.0 = Some(&arc0); + + let mut c = c_orig.clone(); + c.control_bits = 0b1; + c.curr_mark = 110; + assert!(!c.saw_prev_marked); + arc0.descend_into_self(&mut c); + assert!(c.saw_prev_marked); } trait Named { @@ -276,6 +469,26 @@ fn mark(&self) -> u32 { self.mark.get() } fn set_mark(&self, mark: u32) { self.mark.set(mark); } } +struct S2<'a> { + name: &'static str, + mark: Cell, + next: Cell<(Option<&'a S2<'a>>, Option<&'a S2<'a>>)>, +} + +impl<'a> Named for S2<'a> { + fn new<'b>(name: &'static str) -> S2<'b> { + S2 { name: name, mark: Cell::new(0), next: Cell::new((None, None)) } + } + fn name(&self) -> &str { self.name } +} + +impl<'a> Marked for S2<'a> { + fn mark(&self) -> u32 { self.mark.get() } + fn set_mark(&self, mark: u32) { + self.mark.set(mark); + } +} + struct V<'a> { name: &'static str, mark: Cell, @@ -549,8 +762,168 @@ fn cmp(&self, rhs: &BTS<'a>) -> Ordering { } } +#[derive(Clone)] +struct RCRCData<'a> { + name: &'static str, + mark: Cell, + children: (Option<&'a RCRC<'a>>, Option<&'a RCRC<'a>>), +} +#[derive(Clone)] +struct RCRC<'a>(Rc>>); + +impl<'a> Named for RCRC<'a> { + fn new(name: &'static str) -> Self { + RCRC(Rc::new(RefCell::new(RCRCData { + name: name, mark: Cell::new(0), children: (None, None), }))) + } + fn name(&self) -> &str { self.0.borrow().name } +} + +impl<'a> Marked for RCRC<'a> { + fn mark(&self) -> u32 { self.0.borrow().mark.get() } + fn set_mark(&self, mark: u32) { self.0.borrow().mark.set(mark); } +} + +impl<'a> Children<'a> for RCRC<'a> { + fn count_children(&self) -> usize { 2 } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized + { + let children = &self.0.borrow().children; + let child = match index { + 0 => if let Some(child) = children.0 { child } else { return; }, + 1 => if let Some(child) = children.1 { child } else { return; }, + _ => panic!("bad children"), + }; + // println!("S2 {} descending into child {} at index {}", self.name, child.name, index); + child.descend_into_self(context); + } +} +#[derive(Clone)] +struct ARCRCData<'a> { + name: &'static str, + mark: Cell, + children: (Option<&'a ARCRC<'a>>, Option<&'a ARCRC<'a>>), +} +#[derive(Clone)] +struct ARCRC<'a>(Arc>>); + +impl<'a> Named for ARCRC<'a> { + fn new(name: &'static str) -> Self { + ARCRC(Arc::new(RefCell::new(ARCRCData { + name: name, mark: Cell::new(0), children: (None, None), }))) + } + fn name(&self) -> &str { self.0.borrow().name } +} + +impl<'a> Marked for ARCRC<'a> { + fn mark(&self) -> u32 { self.0.borrow().mark.get() } + fn set_mark(&self, mark: u32) { self.0.borrow().mark.set(mark); } +} + +impl<'a> Children<'a> for ARCRC<'a> { + fn count_children(&self) -> usize { 2 } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized + { + let children = &self.0.borrow().children; + match index { + 0 => if let Some(ref child) = children.0 { + child.descend_into_self(context); + }, + 1 => if let Some(ref child) = children.1 { + child.descend_into_self(context); + }, + _ => panic!("bad children!"), + } + } +} + +#[derive(Clone)] +struct ARCMData<'a> { + mark: Cell, + children: (Option<&'a ARCM<'a>>, Option<&'a ARCM<'a>>), +} + +#[derive(Clone)] +struct ARCM<'a>(&'static str, Arc>>); + +impl<'a> Named for ARCM<'a> { + fn new(name: &'static str) -> Self { + ARCM(name, Arc::new(Mutex::new(ARCMData { + mark: Cell::new(0), children: (None, None), }))) + } + fn name(&self) -> &str { self.0 } +} + +impl<'a> Marked for ARCM<'a> { + fn mark(&self) -> u32 { self.1.lock().unwrap().mark.get() } + fn set_mark(&self, mark: u32) { self.1.lock().unwrap().mark.set(mark); } +} + +impl<'a> Children<'a> for ARCM<'a> { + fn count_children(&self) -> usize { 2 } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized + { + let ref children = if let Ok(data) = self.1.try_lock() { + data.children + } else { return; }; + match index { + 0 => if let Some(ref child) = children.0 { + child.descend_into_self(context); + }, + 1 => if let Some(ref child) = children.1 { + child.descend_into_self(context); + }, + _ => panic!("bad children!"), + } + } +} + +#[derive(Clone)] +struct ARCRWData<'a> { + name: &'static str, + mark: Cell, + children: (Option<&'a ARCRW<'a>>, Option<&'a ARCRW<'a>>), +} + +#[derive(Clone)] +struct ARCRW<'a>(Arc>>); + +impl<'a> Named for ARCRW<'a> { + fn new(name: &'static str) -> Self { + ARCRW(Arc::new(RwLock::new(ARCRWData { + name: name, mark: Cell::new(0), children: (None, None), }))) + } + fn name(&self) -> &str { self.0.read().unwrap().name } +} + +impl<'a> Marked for ARCRW<'a> { + fn mark(&self) -> u32 { self.0.read().unwrap().mark.get() } + fn set_mark(&self, mark: u32) { self.0.read().unwrap().mark.set(mark); } +} + +impl<'a> Children<'a> for ARCRW<'a> { + fn count_children(&self) -> usize { 2 } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized + { + let children = &self.0.read().unwrap().children; + match index { + 0 => if let Some(ref child) = children.0 { + child.descend_into_self(context); + }, + 1 => if let Some(ref child) = children.1 { + child.descend_into_self(context); + }, + _ => panic!("bad children!"), + } + } +} trait Context { + fn next_index(&mut self, len: usize) -> usize; fn should_act(&self) -> bool; fn increase_visited(&mut self); fn increase_skipped(&mut self); @@ -565,9 +938,17 @@ trait PrePost { } trait Children<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize; + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost, Self: Sized; + fn next_child(&self, context: &mut C) + where C: Context + PrePost, Self: Sized + { + let index = context.next_index(self.count_children()); + self.descend_one_child(context, index); + } + fn descend_into_self(&self, context: &mut C) where C: Context + PrePost, Self: Sized { @@ -575,7 +956,7 @@ fn descend_into_self(&self, context: &mut C) if context.should_act() { context.increase_visited(); context.increase_depth(); - self.for_each_child(context); + self.next_child(context); context.decrease_depth(); } else { context.hit_limit(self); @@ -594,51 +975,73 @@ fn descend<'b, C>(&self, c: &Cell>, context: &mut C) } impl<'a> Children<'a> for S<'a> { - fn for_each_child(&self, context: &mut C) - where C: Context + PrePost> + fn count_children(&self) -> usize { 1 } + fn descend_one_child(&self, context: &mut C, _: usize) + where C: Context + PrePost, Self: Sized { + self.descend(&self.next, context); + } +} + +impl<'a> Children<'a> for S2<'a> { + fn count_children(&self) -> usize { 2 } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized { - self.descend(&self.next, context); + let children = self.next.get(); + let child = match index { + 0 => if let Some(child) = children.0 { child } else { return; }, + 1 => if let Some(child) = children.1 { child } else { return; }, + _ => panic!("bad children"), + }; + // println!("S2 {} descending into child {} at index {}", self.name, child.name, index); + child.descend_into_self(context); } } impl<'a> Children<'a> for V<'a> { - fn for_each_child(&self, context: &mut C) - where C: Context + PrePost> + fn count_children(&self) -> usize { self.contents.len() } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized { - for r in &self.contents { - self.descend(r, context); + if let Some(child) = self.contents[index].get() { + child.descend_into_self(context); } } } impl<'a> Children<'a> for H<'a> { - fn for_each_child(&self, context: &mut C) - where C: Context + PrePost> + fn count_children(&self) -> usize { 1 } + fn descend_one_child(&self, context: &mut C, _: usize) + where C: Context + PrePost, Self: Sized { self.descend(&self.next, context); } } impl<'a> Children<'a> for HM<'a> { - fn for_each_child(&self, context: &mut C) - where C: Context + PrePost> + fn count_children(&self) -> usize { + if let Some(m) = self.contents.get() { 2 * m.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized { if let Some(ref hm) = self.contents.get() { - for (k, v) in hm.iter() { - for r in &[k, v] { - r.descend_into_self(context); - } + for (k, v) in hm.iter().nth(index / 2) { + [k, v][index % 2].descend_into_self(context); } } } } impl<'a> Children<'a> for VD<'a> { - fn for_each_child(&self, context: &mut C) - where C: Context + PrePost> + fn count_children(&self) -> usize { + if let Some(d) = self.contents.get() { d.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) + where C: Context + PrePost, Self: Sized { if let Some(ref vd) = self.contents.get() { - for r in vd.iter() { + for r in vd.iter().nth(index) { r.descend_into_self(context); } } @@ -646,11 +1049,14 @@ fn for_each_child(&self, context: &mut C) } impl<'a> Children<'a> for VM<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize { + if let Some(m) = self.contents.get() { m.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost> { if let Some(ref vd) = self.contents.get() { - for (_idx, r) in vd.iter() { + for (_idx, r) in vd.iter().nth(index) { r.descend_into_self(context); } } @@ -658,11 +1064,14 @@ fn for_each_child(&self, context: &mut C) } impl<'a> Children<'a> for LL<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize { + if let Some(l) = self.contents.get() { l.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost> { if let Some(ref ll) = self.contents.get() { - for r in ll.iter() { + for r in ll.iter().nth(index) { r.descend_into_self(context); } } @@ -670,11 +1079,14 @@ fn for_each_child(&self, context: &mut C) } impl<'a> Children<'a> for BH<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize { + if let Some(h) = self.contents.get() { h.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost> { if let Some(ref bh) = self.contents.get() { - for r in bh.iter() { + for r in bh.iter().nth(index) { r.descend_into_self(context); } } @@ -682,25 +1094,29 @@ fn for_each_child(&self, context: &mut C) } impl<'a> Children<'a> for BTM<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize { + if let Some(m) = self.contents.get() { 2 * m.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost> { if let Some(ref bh) = self.contents.get() { - for (k, v) in bh.iter() { - for r in &[k, v] { - r.descend_into_self(context); - } + for (k, v) in bh.iter().nth(index / 2) { + [k, v][index % 2].descend_into_self(context); } } } } impl<'a> Children<'a> for BTS<'a> { - fn for_each_child(&self, context: &mut C) + fn count_children(&self) -> usize { + if let Some(s) = self.contents.get() { s.iter().count() } else { 0 } + } + fn descend_one_child(&self, context: &mut C, index: usize) where C: Context + PrePost> { if let Some(ref bh) = self.contents.get() { - for r in bh.iter() { + for r in bh.iter().nth(index) { r.descend_into_self(context); } } @@ -716,9 +1132,27 @@ struct ContextData { skipped: usize, curr_mark: u32, saw_prev_marked: bool, + control_bits: u64, } impl Context for ContextData { + fn next_index(&mut self, len: usize) -> usize { + if len < 2 { return 0; } + let mut pow2 = len.next_power_of_two(); + let _pow2_orig = pow2; + let mut idx = 0; + let mut bits = self.control_bits; + while pow2 > 1 { + idx = (idx << 1) | (bits & 1) as usize; + bits = bits >> 1; + pow2 = pow2 >> 1; + } + idx = idx % len; + // println!("next_index({} [{:b}]) says {}, pre(bits): {:b} post(bits): {:b}", + // len, _pow2_orig, idx, self.control_bits, bits); + self.control_bits = bits; + return idx; + } fn should_act(&self) -> bool { self.curr_depth < self.max_depth && self.visited < self.max_visits }