1fb6c1f39Sopenharmony_ciuse std::iter::repeat; 2fb6c1f39Sopenharmony_ci 3fb6c1f39Sopenharmony_ci/// Create a sequence of tests that should be run by memchr implementations. 4fb6c1f39Sopenharmony_cipub fn memchr_tests() -> Vec<MemchrTest> { 5fb6c1f39Sopenharmony_ci let mut tests = Vec::new(); 6fb6c1f39Sopenharmony_ci for statict in MEMCHR_TESTS { 7fb6c1f39Sopenharmony_ci assert!(!statict.corpus.contains("%"), "% is not allowed in corpora"); 8fb6c1f39Sopenharmony_ci assert!(!statict.corpus.contains("#"), "# is not allowed in corpora"); 9fb6c1f39Sopenharmony_ci assert!(!statict.needles.contains(&b'%'), "% is an invalid needle"); 10fb6c1f39Sopenharmony_ci assert!(!statict.needles.contains(&b'#'), "# is an invalid needle"); 11fb6c1f39Sopenharmony_ci 12fb6c1f39Sopenharmony_ci let t = MemchrTest { 13fb6c1f39Sopenharmony_ci corpus: statict.corpus.to_string(), 14fb6c1f39Sopenharmony_ci needles: statict.needles.to_vec(), 15fb6c1f39Sopenharmony_ci positions: statict.positions.to_vec(), 16fb6c1f39Sopenharmony_ci }; 17fb6c1f39Sopenharmony_ci tests.push(t.clone()); 18fb6c1f39Sopenharmony_ci tests.extend(t.expand()); 19fb6c1f39Sopenharmony_ci } 20fb6c1f39Sopenharmony_ci tests 21fb6c1f39Sopenharmony_ci} 22fb6c1f39Sopenharmony_ci 23fb6c1f39Sopenharmony_ci/// A set of tests for memchr-like functions. 24fb6c1f39Sopenharmony_ci/// 25fb6c1f39Sopenharmony_ci/// These tests mostly try to cover the short string cases. We cover the longer 26fb6c1f39Sopenharmony_ci/// string cases via the benchmarks (which are tests themselves), via 27fb6c1f39Sopenharmony_ci/// quickcheck tests and via automatic expansion of each test case (by 28fb6c1f39Sopenharmony_ci/// increasing the corpus size). Finally, we cover different alignment cases 29fb6c1f39Sopenharmony_ci/// in the tests by varying the starting point of the slice. 30fb6c1f39Sopenharmony_ciconst MEMCHR_TESTS: &[MemchrTestStatic] = &[ 31fb6c1f39Sopenharmony_ci // one needle (applied to memchr + memchr2 + memchr3) 32fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "a", needles: &[b'a'], positions: &[0] }, 33fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "aa", needles: &[b'a'], positions: &[0, 1] }, 34fb6c1f39Sopenharmony_ci MemchrTestStatic { 35fb6c1f39Sopenharmony_ci corpus: "aaa", 36fb6c1f39Sopenharmony_ci needles: &[b'a'], 37fb6c1f39Sopenharmony_ci positions: &[0, 1, 2], 38fb6c1f39Sopenharmony_ci }, 39fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "", needles: &[b'a'], positions: &[] }, 40fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "z", needles: &[b'a'], positions: &[] }, 41fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "zz", needles: &[b'a'], positions: &[] }, 42fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "zza", needles: &[b'a'], positions: &[2] }, 43fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "zaza", needles: &[b'a'], positions: &[1, 3] }, 44fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "zzza", needles: &[b'a'], positions: &[3] }, 45fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "\x00a", needles: &[b'a'], positions: &[1] }, 46fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "\x00", needles: &[b'\x00'], positions: &[0] }, 47fb6c1f39Sopenharmony_ci MemchrTestStatic { 48fb6c1f39Sopenharmony_ci corpus: "\x00\x00", 49fb6c1f39Sopenharmony_ci needles: &[b'\x00'], 50fb6c1f39Sopenharmony_ci positions: &[0, 1], 51fb6c1f39Sopenharmony_ci }, 52fb6c1f39Sopenharmony_ci MemchrTestStatic { 53fb6c1f39Sopenharmony_ci corpus: "\x00a\x00", 54fb6c1f39Sopenharmony_ci needles: &[b'\x00'], 55fb6c1f39Sopenharmony_ci positions: &[0, 2], 56fb6c1f39Sopenharmony_ci }, 57fb6c1f39Sopenharmony_ci MemchrTestStatic { 58fb6c1f39Sopenharmony_ci corpus: "zzzzzzzzzzzzzzzza", 59fb6c1f39Sopenharmony_ci needles: &[b'a'], 60fb6c1f39Sopenharmony_ci positions: &[16], 61fb6c1f39Sopenharmony_ci }, 62fb6c1f39Sopenharmony_ci MemchrTestStatic { 63fb6c1f39Sopenharmony_ci corpus: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzza", 64fb6c1f39Sopenharmony_ci needles: &[b'a'], 65fb6c1f39Sopenharmony_ci positions: &[32], 66fb6c1f39Sopenharmony_ci }, 67fb6c1f39Sopenharmony_ci // two needles (applied to memchr2 + memchr3) 68fb6c1f39Sopenharmony_ci MemchrTestStatic { 69fb6c1f39Sopenharmony_ci corpus: "az", 70fb6c1f39Sopenharmony_ci needles: &[b'a', b'z'], 71fb6c1f39Sopenharmony_ci positions: &[0, 1], 72fb6c1f39Sopenharmony_ci }, 73fb6c1f39Sopenharmony_ci MemchrTestStatic { 74fb6c1f39Sopenharmony_ci corpus: "az", 75fb6c1f39Sopenharmony_ci needles: &[b'a', b'z'], 76fb6c1f39Sopenharmony_ci positions: &[0, 1], 77fb6c1f39Sopenharmony_ci }, 78fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "az", needles: &[b'x', b'y'], positions: &[] }, 79fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "az", needles: &[b'a', b'y'], positions: &[0] }, 80fb6c1f39Sopenharmony_ci MemchrTestStatic { corpus: "az", needles: &[b'x', b'z'], positions: &[1] }, 81fb6c1f39Sopenharmony_ci MemchrTestStatic { 82fb6c1f39Sopenharmony_ci corpus: "yyyyaz", 83fb6c1f39Sopenharmony_ci needles: &[b'a', b'z'], 84fb6c1f39Sopenharmony_ci positions: &[4, 5], 85fb6c1f39Sopenharmony_ci }, 86fb6c1f39Sopenharmony_ci MemchrTestStatic { 87fb6c1f39Sopenharmony_ci corpus: "yyyyaz", 88fb6c1f39Sopenharmony_ci needles: &[b'z', b'a'], 89fb6c1f39Sopenharmony_ci positions: &[4, 5], 90fb6c1f39Sopenharmony_ci }, 91fb6c1f39Sopenharmony_ci // three needles (applied to memchr3) 92fb6c1f39Sopenharmony_ci MemchrTestStatic { 93fb6c1f39Sopenharmony_ci corpus: "xyz", 94fb6c1f39Sopenharmony_ci needles: &[b'x', b'y', b'z'], 95fb6c1f39Sopenharmony_ci positions: &[0, 1, 2], 96fb6c1f39Sopenharmony_ci }, 97fb6c1f39Sopenharmony_ci MemchrTestStatic { 98fb6c1f39Sopenharmony_ci corpus: "zxy", 99fb6c1f39Sopenharmony_ci needles: &[b'x', b'y', b'z'], 100fb6c1f39Sopenharmony_ci positions: &[0, 1, 2], 101fb6c1f39Sopenharmony_ci }, 102fb6c1f39Sopenharmony_ci MemchrTestStatic { 103fb6c1f39Sopenharmony_ci corpus: "zxy", 104fb6c1f39Sopenharmony_ci needles: &[b'x', b'a', b'z'], 105fb6c1f39Sopenharmony_ci positions: &[0, 1], 106fb6c1f39Sopenharmony_ci }, 107fb6c1f39Sopenharmony_ci MemchrTestStatic { 108fb6c1f39Sopenharmony_ci corpus: "zxy", 109fb6c1f39Sopenharmony_ci needles: &[b't', b'a', b'z'], 110fb6c1f39Sopenharmony_ci positions: &[0], 111fb6c1f39Sopenharmony_ci }, 112fb6c1f39Sopenharmony_ci MemchrTestStatic { 113fb6c1f39Sopenharmony_ci corpus: "yxz", 114fb6c1f39Sopenharmony_ci needles: &[b't', b'a', b'z'], 115fb6c1f39Sopenharmony_ci positions: &[2], 116fb6c1f39Sopenharmony_ci }, 117fb6c1f39Sopenharmony_ci]; 118fb6c1f39Sopenharmony_ci 119fb6c1f39Sopenharmony_ci/// A description of a test on a memchr like function. 120fb6c1f39Sopenharmony_ci#[derive(Clone, Debug)] 121fb6c1f39Sopenharmony_cipub struct MemchrTest { 122fb6c1f39Sopenharmony_ci /// The thing to search. We use `&str` instead of `&[u8]` because they 123fb6c1f39Sopenharmony_ci /// are nicer to write in tests, and we don't miss much since memchr 124fb6c1f39Sopenharmony_ci /// doesn't care about UTF-8. 125fb6c1f39Sopenharmony_ci /// 126fb6c1f39Sopenharmony_ci /// Corpora cannot contain either '%' or '#'. We use these bytes when 127fb6c1f39Sopenharmony_ci /// expanding test cases into many test cases, and we assume they are not 128fb6c1f39Sopenharmony_ci /// used. If they are used, `memchr_tests` will panic. 129fb6c1f39Sopenharmony_ci corpus: String, 130fb6c1f39Sopenharmony_ci /// The needles to search for. This is intended to be an "alternation" of 131fb6c1f39Sopenharmony_ci /// needles. The number of needles may cause this test to be skipped for 132fb6c1f39Sopenharmony_ci /// some memchr variants. For example, a test with 2 needles cannot be used 133fb6c1f39Sopenharmony_ci /// to test `memchr`, but can be used to test `memchr2` and `memchr3`. 134fb6c1f39Sopenharmony_ci /// However, a test with only 1 needle can be used to test all of `memchr`, 135fb6c1f39Sopenharmony_ci /// `memchr2` and `memchr3`. We achieve this by filling in the needles with 136fb6c1f39Sopenharmony_ci /// bytes that we never used in the corpus (such as '#'). 137fb6c1f39Sopenharmony_ci needles: Vec<u8>, 138fb6c1f39Sopenharmony_ci /// The positions expected to match for all of the needles. 139fb6c1f39Sopenharmony_ci positions: Vec<usize>, 140fb6c1f39Sopenharmony_ci} 141fb6c1f39Sopenharmony_ci 142fb6c1f39Sopenharmony_ci/// Like MemchrTest, but easier to define as a constant. 143fb6c1f39Sopenharmony_ci#[derive(Clone, Debug)] 144fb6c1f39Sopenharmony_cipub struct MemchrTestStatic { 145fb6c1f39Sopenharmony_ci corpus: &'static str, 146fb6c1f39Sopenharmony_ci needles: &'static [u8], 147fb6c1f39Sopenharmony_ci positions: &'static [usize], 148fb6c1f39Sopenharmony_ci} 149fb6c1f39Sopenharmony_ci 150fb6c1f39Sopenharmony_ciimpl MemchrTest { 151fb6c1f39Sopenharmony_ci pub fn one<F: Fn(u8, &[u8]) -> Option<usize>>(&self, reverse: bool, f: F) { 152fb6c1f39Sopenharmony_ci let needles = match self.needles(1) { 153fb6c1f39Sopenharmony_ci None => return, 154fb6c1f39Sopenharmony_ci Some(needles) => needles, 155fb6c1f39Sopenharmony_ci }; 156fb6c1f39Sopenharmony_ci // We test different alignments here. Since some implementations use 157fb6c1f39Sopenharmony_ci // AVX2, which can read 32 bytes at a time, we test at least that. 158fb6c1f39Sopenharmony_ci // Moreover, with loop unrolling, we sometimes process 64 (sse2) or 128 159fb6c1f39Sopenharmony_ci // (avx) bytes at a time, so we include that in our offsets as well. 160fb6c1f39Sopenharmony_ci // 161fb6c1f39Sopenharmony_ci // You might think this would cause most needles to not be found, but 162fb6c1f39Sopenharmony_ci // we actually expand our tests to include corpus sizes all the way up 163fb6c1f39Sopenharmony_ci // to >500 bytes, so we should exercise most branches. 164fb6c1f39Sopenharmony_ci for align in 0..130 { 165fb6c1f39Sopenharmony_ci let corpus = self.corpus(align); 166fb6c1f39Sopenharmony_ci assert_eq!( 167fb6c1f39Sopenharmony_ci self.positions(align, reverse).get(0).cloned(), 168fb6c1f39Sopenharmony_ci f(needles[0], corpus.as_bytes()), 169fb6c1f39Sopenharmony_ci "search for {:?} failed in: {:?} (len: {}, alignment: {})", 170fb6c1f39Sopenharmony_ci needles[0] as char, 171fb6c1f39Sopenharmony_ci corpus, 172fb6c1f39Sopenharmony_ci corpus.len(), 173fb6c1f39Sopenharmony_ci align 174fb6c1f39Sopenharmony_ci ); 175fb6c1f39Sopenharmony_ci } 176fb6c1f39Sopenharmony_ci } 177fb6c1f39Sopenharmony_ci 178fb6c1f39Sopenharmony_ci pub fn two<F: Fn(u8, u8, &[u8]) -> Option<usize>>( 179fb6c1f39Sopenharmony_ci &self, 180fb6c1f39Sopenharmony_ci reverse: bool, 181fb6c1f39Sopenharmony_ci f: F, 182fb6c1f39Sopenharmony_ci ) { 183fb6c1f39Sopenharmony_ci let needles = match self.needles(2) { 184fb6c1f39Sopenharmony_ci None => return, 185fb6c1f39Sopenharmony_ci Some(needles) => needles, 186fb6c1f39Sopenharmony_ci }; 187fb6c1f39Sopenharmony_ci for align in 0..130 { 188fb6c1f39Sopenharmony_ci let corpus = self.corpus(align); 189fb6c1f39Sopenharmony_ci assert_eq!( 190fb6c1f39Sopenharmony_ci self.positions(align, reverse).get(0).cloned(), 191fb6c1f39Sopenharmony_ci f(needles[0], needles[1], corpus.as_bytes()), 192fb6c1f39Sopenharmony_ci "search for {:?}|{:?} failed in: {:?} \ 193fb6c1f39Sopenharmony_ci (len: {}, alignment: {})", 194fb6c1f39Sopenharmony_ci needles[0] as char, 195fb6c1f39Sopenharmony_ci needles[1] as char, 196fb6c1f39Sopenharmony_ci corpus, 197fb6c1f39Sopenharmony_ci corpus.len(), 198fb6c1f39Sopenharmony_ci align 199fb6c1f39Sopenharmony_ci ); 200fb6c1f39Sopenharmony_ci } 201fb6c1f39Sopenharmony_ci } 202fb6c1f39Sopenharmony_ci 203fb6c1f39Sopenharmony_ci pub fn three<F: Fn(u8, u8, u8, &[u8]) -> Option<usize>>( 204fb6c1f39Sopenharmony_ci &self, 205fb6c1f39Sopenharmony_ci reverse: bool, 206fb6c1f39Sopenharmony_ci f: F, 207fb6c1f39Sopenharmony_ci ) { 208fb6c1f39Sopenharmony_ci let needles = match self.needles(3) { 209fb6c1f39Sopenharmony_ci None => return, 210fb6c1f39Sopenharmony_ci Some(needles) => needles, 211fb6c1f39Sopenharmony_ci }; 212fb6c1f39Sopenharmony_ci for align in 0..130 { 213fb6c1f39Sopenharmony_ci let corpus = self.corpus(align); 214fb6c1f39Sopenharmony_ci assert_eq!( 215fb6c1f39Sopenharmony_ci self.positions(align, reverse).get(0).cloned(), 216fb6c1f39Sopenharmony_ci f(needles[0], needles[1], needles[2], corpus.as_bytes()), 217fb6c1f39Sopenharmony_ci "search for {:?}|{:?}|{:?} failed in: {:?} \ 218fb6c1f39Sopenharmony_ci (len: {}, alignment: {})", 219fb6c1f39Sopenharmony_ci needles[0] as char, 220fb6c1f39Sopenharmony_ci needles[1] as char, 221fb6c1f39Sopenharmony_ci needles[2] as char, 222fb6c1f39Sopenharmony_ci corpus, 223fb6c1f39Sopenharmony_ci corpus.len(), 224fb6c1f39Sopenharmony_ci align 225fb6c1f39Sopenharmony_ci ); 226fb6c1f39Sopenharmony_ci } 227fb6c1f39Sopenharmony_ci } 228fb6c1f39Sopenharmony_ci 229fb6c1f39Sopenharmony_ci pub fn iter_one<'a, I, F>(&'a self, reverse: bool, f: F) 230fb6c1f39Sopenharmony_ci where 231fb6c1f39Sopenharmony_ci F: FnOnce(u8, &'a [u8]) -> I, 232fb6c1f39Sopenharmony_ci I: Iterator<Item = usize>, 233fb6c1f39Sopenharmony_ci { 234fb6c1f39Sopenharmony_ci if let Some(ns) = self.needles(1) { 235fb6c1f39Sopenharmony_ci self.iter(reverse, f(ns[0], self.corpus.as_bytes())); 236fb6c1f39Sopenharmony_ci } 237fb6c1f39Sopenharmony_ci } 238fb6c1f39Sopenharmony_ci 239fb6c1f39Sopenharmony_ci pub fn iter_two<'a, I, F>(&'a self, reverse: bool, f: F) 240fb6c1f39Sopenharmony_ci where 241fb6c1f39Sopenharmony_ci F: FnOnce(u8, u8, &'a [u8]) -> I, 242fb6c1f39Sopenharmony_ci I: Iterator<Item = usize>, 243fb6c1f39Sopenharmony_ci { 244fb6c1f39Sopenharmony_ci if let Some(ns) = self.needles(2) { 245fb6c1f39Sopenharmony_ci self.iter(reverse, f(ns[0], ns[1], self.corpus.as_bytes())); 246fb6c1f39Sopenharmony_ci } 247fb6c1f39Sopenharmony_ci } 248fb6c1f39Sopenharmony_ci 249fb6c1f39Sopenharmony_ci pub fn iter_three<'a, I, F>(&'a self, reverse: bool, f: F) 250fb6c1f39Sopenharmony_ci where 251fb6c1f39Sopenharmony_ci F: FnOnce(u8, u8, u8, &'a [u8]) -> I, 252fb6c1f39Sopenharmony_ci I: Iterator<Item = usize>, 253fb6c1f39Sopenharmony_ci { 254fb6c1f39Sopenharmony_ci if let Some(ns) = self.needles(3) { 255fb6c1f39Sopenharmony_ci self.iter(reverse, f(ns[0], ns[1], ns[2], self.corpus.as_bytes())); 256fb6c1f39Sopenharmony_ci } 257fb6c1f39Sopenharmony_ci } 258fb6c1f39Sopenharmony_ci 259fb6c1f39Sopenharmony_ci /// Test that the positions yielded by the given iterator match the 260fb6c1f39Sopenharmony_ci /// positions in this test. If reverse is true, then reverse the positions 261fb6c1f39Sopenharmony_ci /// before comparing them. 262fb6c1f39Sopenharmony_ci fn iter<I: Iterator<Item = usize>>(&self, reverse: bool, it: I) { 263fb6c1f39Sopenharmony_ci assert_eq!( 264fb6c1f39Sopenharmony_ci self.positions(0, reverse), 265fb6c1f39Sopenharmony_ci it.collect::<Vec<usize>>(), 266fb6c1f39Sopenharmony_ci r"search for {:?} failed in: {:?}", 267fb6c1f39Sopenharmony_ci self.needles.iter().map(|&b| b as char).collect::<Vec<char>>(), 268fb6c1f39Sopenharmony_ci self.corpus 269fb6c1f39Sopenharmony_ci ); 270fb6c1f39Sopenharmony_ci } 271fb6c1f39Sopenharmony_ci 272fb6c1f39Sopenharmony_ci /// Expand this test into many variations of the same test. 273fb6c1f39Sopenharmony_ci /// 274fb6c1f39Sopenharmony_ci /// In particular, this will generate more tests with larger corpus sizes. 275fb6c1f39Sopenharmony_ci /// The expected positions are updated to maintain the integrity of the 276fb6c1f39Sopenharmony_ci /// test. 277fb6c1f39Sopenharmony_ci /// 278fb6c1f39Sopenharmony_ci /// This is important in testing a memchr implementation, because there are 279fb6c1f39Sopenharmony_ci /// often different cases depending on the length of the corpus. 280fb6c1f39Sopenharmony_ci /// 281fb6c1f39Sopenharmony_ci /// Note that we extend the corpus by adding `%` bytes, which we 282fb6c1f39Sopenharmony_ci /// don't otherwise use as a needle. 283fb6c1f39Sopenharmony_ci fn expand(&self) -> Vec<MemchrTest> { 284fb6c1f39Sopenharmony_ci let mut more = Vec::new(); 285fb6c1f39Sopenharmony_ci 286fb6c1f39Sopenharmony_ci // Add bytes to the start of the corpus. 287fb6c1f39Sopenharmony_ci for i in 1..515 { 288fb6c1f39Sopenharmony_ci let mut t = self.clone(); 289fb6c1f39Sopenharmony_ci let mut new_corpus: String = repeat('%').take(i).collect(); 290fb6c1f39Sopenharmony_ci new_corpus.push_str(&t.corpus); 291fb6c1f39Sopenharmony_ci t.corpus = new_corpus; 292fb6c1f39Sopenharmony_ci t.positions = t.positions.into_iter().map(|p| p + i).collect(); 293fb6c1f39Sopenharmony_ci more.push(t); 294fb6c1f39Sopenharmony_ci } 295fb6c1f39Sopenharmony_ci // Add bytes to the end of the corpus. 296fb6c1f39Sopenharmony_ci for i in 1..515 { 297fb6c1f39Sopenharmony_ci let mut t = self.clone(); 298fb6c1f39Sopenharmony_ci let padding: String = repeat('%').take(i).collect(); 299fb6c1f39Sopenharmony_ci t.corpus.push_str(&padding); 300fb6c1f39Sopenharmony_ci more.push(t); 301fb6c1f39Sopenharmony_ci } 302fb6c1f39Sopenharmony_ci 303fb6c1f39Sopenharmony_ci more 304fb6c1f39Sopenharmony_ci } 305fb6c1f39Sopenharmony_ci 306fb6c1f39Sopenharmony_ci /// Return the corpus at the given alignment. 307fb6c1f39Sopenharmony_ci /// 308fb6c1f39Sopenharmony_ci /// If the alignment exceeds the length of the corpus, then this returns 309fb6c1f39Sopenharmony_ci /// an empty slice. 310fb6c1f39Sopenharmony_ci fn corpus(&self, align: usize) -> &str { 311fb6c1f39Sopenharmony_ci self.corpus.get(align..).unwrap_or("") 312fb6c1f39Sopenharmony_ci } 313fb6c1f39Sopenharmony_ci 314fb6c1f39Sopenharmony_ci /// Return exactly `count` needles from this test. If this test has less 315fb6c1f39Sopenharmony_ci /// than `count` needles, then add `#` until the number of needles 316fb6c1f39Sopenharmony_ci /// matches `count`. If this test has more than `count` needles, then 317fb6c1f39Sopenharmony_ci /// return `None` (because there is no way to use this test data for a 318fb6c1f39Sopenharmony_ci /// search using fewer needles). 319fb6c1f39Sopenharmony_ci fn needles(&self, count: usize) -> Option<Vec<u8>> { 320fb6c1f39Sopenharmony_ci if self.needles.len() > count { 321fb6c1f39Sopenharmony_ci return None; 322fb6c1f39Sopenharmony_ci } 323fb6c1f39Sopenharmony_ci 324fb6c1f39Sopenharmony_ci let mut needles = self.needles.to_vec(); 325fb6c1f39Sopenharmony_ci for _ in needles.len()..count { 326fb6c1f39Sopenharmony_ci // we assume # is never used in tests. 327fb6c1f39Sopenharmony_ci needles.push(b'#'); 328fb6c1f39Sopenharmony_ci } 329fb6c1f39Sopenharmony_ci Some(needles) 330fb6c1f39Sopenharmony_ci } 331fb6c1f39Sopenharmony_ci 332fb6c1f39Sopenharmony_ci /// Return the positions in this test, reversed if `reverse` is true. 333fb6c1f39Sopenharmony_ci /// 334fb6c1f39Sopenharmony_ci /// If alignment is given, then all positions greater than or equal to that 335fb6c1f39Sopenharmony_ci /// alignment are offset by the alignment. Positions less than the 336fb6c1f39Sopenharmony_ci /// alignment are dropped. 337fb6c1f39Sopenharmony_ci fn positions(&self, align: usize, reverse: bool) -> Vec<usize> { 338fb6c1f39Sopenharmony_ci let positions = if reverse { 339fb6c1f39Sopenharmony_ci let mut positions = self.positions.to_vec(); 340fb6c1f39Sopenharmony_ci positions.reverse(); 341fb6c1f39Sopenharmony_ci positions 342fb6c1f39Sopenharmony_ci } else { 343fb6c1f39Sopenharmony_ci self.positions.to_vec() 344fb6c1f39Sopenharmony_ci }; 345fb6c1f39Sopenharmony_ci positions 346fb6c1f39Sopenharmony_ci .into_iter() 347fb6c1f39Sopenharmony_ci .filter(|&p| p >= align) 348fb6c1f39Sopenharmony_ci .map(|p| p - align) 349fb6c1f39Sopenharmony_ci .collect() 350fb6c1f39Sopenharmony_ci } 351fb6c1f39Sopenharmony_ci} 352