├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── example ├── Cargo.toml ├── output └── src │ ├── input │ └── main.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | Cargo.lock 4 | example/target/* 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ni_rs" 3 | version = "0.1.0" 4 | authors = ["thebarbershopper "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Cory Duplantis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ni-rs 2 | 3 | Port of [ni.c](https://github.com/aoh/ni) to have a small data mutator in Rust. 4 | 5 | # Usage 6 | 7 | ## Generate one mutation from a single buffer 8 | 9 | ``` 10 | use std::fs::File; 11 | use std::io::Write; 12 | 13 | fn main() { 14 | let input = include_bytes!("input"); 15 | let mutation = ni_rs::mutate(input); 16 | let mut f = File::create("output").ok().unwrap(); 17 | f.write(&mutation); 18 | } 19 | ``` 20 | 21 | ## Generate 32 mutations from a single buffer 22 | 23 | ``` 24 | fn main() { 25 | let input = include_bytes!("input"); 26 | let mutations = ni_rs::mutate_n(input, 32); 27 | assert_eq!(mutations.len(), 32); 28 | } 29 | ``` 30 | 31 | # Corpus mutation 32 | 33 | ## Generate one mutation from a corpus 34 | 35 | ``` 36 | use std::fs::File; 37 | use std::io::Write; 38 | 39 | fn main() { 40 | let samples = vec![ 41 | "", 42 | "", 43 | "" 44 | ]; 45 | let mutation = ni_rs::mutate_corpus(samples); 46 | let mut f = File::create("output").ok().unwrap(); 47 | f.write(&mutation); 48 | } 49 | ``` 50 | 51 | ## Generate 24 mutations from a corpus 52 | 53 | ``` 54 | fn main() { 55 | let samples = vec![ 56 | "", 57 | "", 58 | "" 59 | ]; 60 | let mutations = ni_rs::mutate_corpus_n(samples, 24); 61 | assert_eq!(mutations.len(), 32); 62 | } 63 | ``` 64 | 65 | # Docs 66 | 67 | `cargo doc` for more information 68 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example" 3 | version = "0.1.0" 4 | authors = ["thebarbershopper "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | ni_rs = { path = ".." } 9 | -------------------------------------------------------------------------------- /example/output: -------------------------------------------------------------------------------- 1 | YPE html> 2 | 3 | 4 |

My 1111 Heading

5 |

My first paragraph.

6 | 7 | 8 | -------------------------------------------------------------------------------- /example/src/input: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

My 1111 Heading

5 |

My first paragraph.

6 | 7 | 8 | -------------------------------------------------------------------------------- /example/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io::Write; 3 | 4 | fn main() { 5 | let input = include_bytes!("input").to_vec(); 6 | let muts = ni_rs::mutate(input); 7 | let mut f = File::create("output").ok().unwrap(); 8 | f.write(&muts); 9 | } 10 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(exclusive_range_pattern)] 2 | 3 | use std::io::Write; 4 | use std::str; 5 | 6 | const AIMROUNDS: usize = 256; 7 | const AIMAX: usize = 512; 8 | const AIMLEN: usize = 1024; 9 | const BUFSIZE: usize = 4096; 10 | 11 | fn rotl64(x: u64, k: u64) -> u64 { 12 | (x << k) | (x >> (64 - k)) 13 | } 14 | 15 | /// Implementation from http://prng.di.unimi.it/xoshiro128plusplus.c 16 | pub struct Xoshiro256StarStar { 17 | val0: u64, 18 | val1: u64, 19 | val2: u64, 20 | val3: u64, 21 | } 22 | 23 | /// Wrapper around `rdtsc` instruction to generate a random number 24 | fn rdtsc() -> usize { 25 | unsafe { std::arch::x86_64::_rdtsc() as usize } 26 | } 27 | 28 | impl Xoshiro256StarStar { 29 | pub fn new() -> Xoshiro256StarStar { 30 | Xoshiro256StarStar { 31 | val0: rdtsc() as u64, 32 | val1: rdtsc() as u64, 33 | val2: rdtsc() as u64, 34 | val3: rdtsc() as u64, 35 | } 36 | } 37 | 38 | pub fn rand(&mut self) -> u64 { 39 | let res = rotl64(self.val1.wrapping_mul(5), 7).wrapping_mul(9); 40 | let t = self.val1 << 17; 41 | self.val2 ^= self.val0; 42 | self.val3 ^= self.val1; 43 | self.val1 ^= self.val2; 44 | self.val0 ^= self.val3; 45 | self.val2 ^= t; 46 | self.val3 = rotl64(self.val3, 45); 47 | res 48 | } 49 | } 50 | 51 | 52 | /// Generate a random u8 leveraging `rdtsc` 53 | fn rand_u8() -> u8 { 54 | (rdtsc() % 255) as u8 55 | } 56 | 57 | /// Generate a random number [0; end) 58 | fn random(end: usize) -> usize { 59 | if end == 0 { 60 | return 0; 61 | } 62 | 63 | rdtsc() % end 64 | } 65 | 66 | /// Generate two random numbers that are not the same 67 | /// returns two numbers, the first is less than the second 68 | fn two_rand_numbers(len: usize) -> (usize, usize) { 69 | let mut a = rdtsc() % len; 70 | let mut b = rdtsc() % len; 71 | if len == 1 { 72 | return (0, 1); 73 | } 74 | 75 | loop { 76 | if a != b { 77 | break; 78 | } 79 | a = rdtsc() % len; 80 | b = rdtsc() % len; 81 | } 82 | 83 | if a > b { 84 | (b, a) 85 | } else { 86 | (a, b) 87 | } 88 | } 89 | 90 | fn sufscore(a: &[u8], b: &[u8]) -> usize { 91 | let mut last = 255; 92 | let mut n = 0; 93 | for (a_char, b_char) in a.iter().zip(b.iter()) { 94 | if n < AIMAX { 95 | break; 96 | } 97 | 98 | if a_char == b_char { 99 | break; 100 | } 101 | 102 | if *a_char != last { 103 | last = *a_char; 104 | n += 32; 105 | } 106 | } 107 | 108 | n 109 | } 110 | 111 | fn aim(from: &[u8], to: &[u8], jump: &mut usize, land: &mut usize) { 112 | let fend = from.len(); 113 | let tend = to.len(); 114 | let mut best_score = 0; 115 | let mut score; 116 | if fend == 0 { 117 | *jump = 0; 118 | *land = random(tend); 119 | return; 120 | } 121 | 122 | if tend == 0 { 123 | *land = 0; 124 | return; 125 | } 126 | 127 | *jump = random(fend); 128 | *land = random(tend); 129 | 130 | let rounds = random(AIMROUNDS); 131 | for _ in 0..rounds { 132 | let mut maxs = AIMLEN; 133 | let j = random(fend); 134 | let mut l = random(tend); 135 | while maxs > 0 && l < tend && from.iter().take(j).next() != to.iter().take(l).next() { 136 | l += 1; 137 | maxs -= 1; 138 | } 139 | 140 | score = sufscore(&from[j..], &to[l..]); 141 | if score > best_score { 142 | best_score = score; 143 | *jump = j; 144 | *land = l; 145 | } 146 | } 147 | } 148 | 149 | /// Generate a random substring from one of the given corpus samples 150 | fn random_block<'a>(data: Vec, samples: &Vec>) -> Vec { 151 | let rand_index = random(samples.len()); 152 | let rand_sample = &samples[rand_index]; 153 | let start = random(rand_sample.len() - 2); 154 | if rand_sample.len() < 3 { 155 | return data; 156 | } 157 | 158 | let mut len = rand_sample.len() - start; 159 | if len > 4 * data.len() { 160 | len = 4 * data.len(); 161 | } 162 | len = random(len); 163 | rand_sample[len..].to_vec() 164 | } 165 | 166 | /// Returns the start and end indeces of a random number in the buffer 167 | fn seek_num(data: &[u8]) -> Option<(usize, usize)> { 168 | let end = data.len(); 169 | let rand_start = random(end); 170 | let mut start_index = None; 171 | let mut end_index = None; 172 | for (i, c) in data[rand_start..].iter().enumerate() { 173 | match (*c as char, start_index) { 174 | // First time seeing a number 175 | ('0'..'9', None) => start_index = Some(i + rand_start), 176 | 177 | // Still seeing a number, continue 178 | ('0'..'9', Some(_)) => continue, 179 | 180 | // Saw some number, and are no longer seeing digits 181 | (_, Some(_)) => { 182 | end_index = Some(i + rand_start); 183 | break; 184 | } 185 | 186 | // Everything else 187 | _ => continue, 188 | } 189 | } 190 | 191 | if let (Some(start), Some(end)) = (start_index, end_index) { 192 | // If found both start and end, deconstruct to a single Option 193 | Some((start, end)) 194 | } else { 195 | // No number found 196 | None 197 | } 198 | } 199 | 200 | /// Randomly changes or bit flips a number 201 | fn twiddle(val: i64) -> i64 { 202 | let mut result = val; 203 | loop { 204 | match random(3) { 205 | // Make a new random i64 number 206 | 0 => result = random(i64::max_value() as usize) as i64, 207 | // Flip one of the result bits 208 | 1 => result ^= 1 << random(64 - 1), 209 | // Add a number relatively close to 0 210 | 2 => result += random(5) as i64 - 2, 211 | _ => continue, 212 | } 213 | 214 | // Continue twiddling 50% of the time 215 | if rdtsc() & 1 == 0 { 216 | break; 217 | } 218 | } 219 | result 220 | } 221 | 222 | /// Returns the position and found delimiter in an input buffer 223 | fn drange_start(data: &[u8]) -> Option<(usize, &u8)> { 224 | data.iter() 225 | .enumerate() 226 | .filter(|&(_, c)| match *c as char { 227 | '[' | '<' | '(' | '\n' => true, 228 | _ => false, 229 | }) 230 | .next() 231 | .clone() 232 | } 233 | 234 | /// Return the opposite deliminator for a given deliminator 235 | fn other_delim(delim: u8) -> Option { 236 | match delim as char { 237 | '<' => Some('>' as u8), 238 | '(' => Some(')' as u8), 239 | '{' => Some('}' as u8), 240 | '[' => Some(']' as u8), 241 | '>' => Some('<' as u8), 242 | ')' => Some('(' as u8), 243 | '}' => Some('{' as u8), 244 | ']' => Some('[' as u8), 245 | '\n' => Some('\n' as u8), 246 | _ => unimplemented!(), 247 | } 248 | } 249 | 250 | /// Returns the position of the ending of the delimited string 251 | /// 252 | /// # Example 253 | /// 254 | /// ``` 255 | /// let data = ""; 256 | /// let res = ni_rs::drange_end(&data, '>').unwrap(); 257 | /// assert!(&data[..res] == "" || 258 | /// &data[..res] == "" || 259 | /// &data[..res] == ""); 260 | /// 261 | /// let data = "Example HTML - not here"; 262 | /// let res = ni_rs::drange_end(&data[13..], '>'); 263 | /// assert_eq!(res, None); 264 | /// ``` 265 | fn drange_end(data: &[u8], delim_close: u8) -> Option { 266 | let delim_open = other_delim(delim_close)?; 267 | 268 | let mut depth = 0; 269 | for (i, c) in data.iter().enumerate() { 270 | if *c == delim_close { 271 | depth -= 1; 272 | if depth == 0 { 273 | if rdtsc() & 3 == 0 { 274 | return Some(i + 1); 275 | } 276 | 277 | let next = drange_end(&data[i + 1..], delim_close); 278 | match next { 279 | Some(x) => return Some(i + 1 + x), 280 | None => return Some(i + 1), 281 | } 282 | } 283 | } else if *c == delim_open { 284 | depth += 1; 285 | } else if c & 128 > 0 { 286 | return None; 287 | } 288 | } 289 | 290 | return None; 291 | } 292 | 293 | /// Returns the first found delimited string in a given buffer 294 | /// 295 | /// # Example 296 | /// 297 | /// ``` 298 | /// let data = "Example HTML "; 299 | /// let (start, end) = ni_rs::drange(data).unwrap(); 300 | /// assert!(&data[start..end] == "" || 301 | /// &data[start..end] == ""); 302 | /// 303 | /// 304 | /// let data = "Example HTML - not here"; 305 | /// let res = ni_rs::drange(data); 306 | /// assert_eq!(res, None); 307 | /// ``` 308 | fn drange(data: &[u8]) -> Option<(usize, usize)> { 309 | let (delim_start, delim_char) = drange_start(data)?; 310 | let wanted_delim = other_delim(*delim_char)?; 311 | 312 | let delim_end = drange_end(&data[delim_start..], wanted_delim)?; 313 | 314 | // delim_end is the offset from the start of the delimited string 315 | // need to add the start offset to get the correct index 316 | // 317 | // +1 to include the last character so we can do 318 | // data[delim_start..delim_end] 319 | return Some((delim_start, delim_start + delim_end)); 320 | } 321 | 322 | /// Attempts to find another delimited string elsewhere in the input buffer 323 | /// 324 | /// # Example 325 | /// 326 | /// ``` 327 | /// let data = ""; 328 | /// let (start, end) = ni_rs::other_drange(&data, '<').unwrap(); 329 | /// assert!(&data[start..end] == "" || 330 | /// &data[start..end] == "" || 331 | /// &data[start..end] == "" || 332 | /// &data[start..end] == "" || 333 | /// &data[start..end] == "" || 334 | /// &data[start..end] == "" || 335 | /// &data[start..end] == "" || 336 | /// &data[start..end] == "" || 337 | /// &data[start..end] == "" || 338 | /// &data[start..end] == ""); 339 | /// ``` 340 | fn other_drange(data: &[u8], delim_start: u8) -> Option<(usize, usize)> { 341 | let delim_close = other_delim(delim_start)?; 342 | 343 | for _ in 0..32 { 344 | let start = random(data.len()); 345 | let temp_data = &data[start..]; 346 | for (i, c) in temp_data.iter().enumerate() { 347 | if *c == delim_start { 348 | let delim_end = drange_end(&temp_data[i..], delim_close); 349 | match delim_end { 350 | None => continue, 351 | Some(end) => { 352 | return Some((start + i, start + i + end)); 353 | } 354 | } 355 | } 356 | } 357 | } 358 | 359 | None 360 | } 361 | 362 | fn mutate_area(data: &[u8], samples: &Vec>, output: &mut W) { 363 | let end = data.len(); 364 | loop { 365 | let r = rdtsc() % 35; 366 | // println!("mutate_area r: {}", r); 367 | match r { 368 | // match 7 { 369 | 0 => { 370 | // Insert a random byte 371 | let position = random(end); 372 | let _ = output.write(&data[..position]); 373 | let _ = output.write(&[rand_u8()]); 374 | let _ = output.write(&data[position..]); 375 | return; 376 | } 377 | 1 => { 378 | // Delete a random byte 379 | let position = random(end); 380 | if position + 1 >= end { 381 | continue; 382 | } 383 | let _ = output.write(&data[..position]); 384 | let _ = output.write(&data[position + 1..]); 385 | return; 386 | } 387 | 2..4 => { 388 | // Jump / Overlapping sequences 389 | if end <= 1 { 390 | continue; 391 | } 392 | 393 | // Generate two random numbers where a < b 394 | let (a, b) = two_rand_numbers(end); 395 | let _ = output.write(&data[..a]); 396 | let _ = output.write(&data[b..]); 397 | return; 398 | } 399 | 4..6 => { 400 | // Repeat characters 401 | if end < 2 { 402 | continue; 403 | } 404 | 405 | let mut n = 8; 406 | while rdtsc() & 1 == 0 && n < 20000 { 407 | n <<= 1; 408 | } 409 | 410 | n = rdtsc() % n + 2; 411 | 412 | // Generate two random numbers where a < b 413 | let (s, e) = two_rand_numbers(end); 414 | let mut len = e - s; 415 | 416 | let _ = output.write(&data[..s]); 417 | 418 | if len * n > 0x8000000 { 419 | len = rdtsc() % 1024 + 2; 420 | } 421 | 422 | // Insert some substring `n` times 423 | for _ in 0..n { 424 | let _ = output.write(&data[s..s + len]); 425 | } 426 | 427 | // Write the rest of the string 428 | let _ = output.write(&data[s..]); 429 | return; 430 | } 431 | 6 => { 432 | // Insert random data 433 | let position = random(end); 434 | let n = rdtsc() % 1022 + 2; 435 | let _ = output.write(&data[..position]); 436 | for _ in 0..n { 437 | let _ = output.write(&[rand_u8()]); 438 | } 439 | let _ = output.write(&data[position..]); 440 | return; 441 | } 442 | 7..13 => { 443 | // Aimed jump to self 444 | if end < 5 { 445 | continue; 446 | } 447 | 448 | let mut j = 0; 449 | let mut l = 0; 450 | aim(data, data, &mut j, &mut l); 451 | 452 | let _ = output.write(&data[..j]); 453 | let _ = output.write(&data[l..]); 454 | return; 455 | } 456 | 13..22 => { 457 | // Aimed random block fusion 458 | if end < 8 { 459 | continue; 460 | } 461 | 462 | let random_chunk = random_block(data.to_vec(), samples); 463 | let stend = random_chunk.len(); 464 | let dm = end >> 1; 465 | let sm = stend >> 1; 466 | let mut j = 0; 467 | let mut l = 1; 468 | aim(&data[..dm], &random_chunk[..sm], &mut j, &mut l); 469 | let _ = output.write(&data[..j]); 470 | 471 | let buff = &random_chunk[sm..]; 472 | aim(buff, &data[j..], &mut j, &mut l); 473 | let _ = output.write(&buff[..j]); 474 | let _ = output.write(&data[l..]); 475 | return; 476 | } 477 | 22..24 => { 478 | // Insert semirandom bytes 479 | if end < 2 { 480 | continue; 481 | } 482 | 483 | let mut n = random(BUFSIZE); 484 | let position = random(end); 485 | for _ in 0..5 { 486 | n = random(n); 487 | } 488 | 489 | if n == 0 { 490 | n = 2; 491 | } 492 | let _ = output.write(&data[..position]); 493 | for _ in 0..n { 494 | let r = random(data.len() - 2) + 2; 495 | let _ = output.write(&[data[r - 1]]); 496 | } 497 | let _ = output.write(&data[position..]); 498 | return; 499 | } 500 | 24 => { 501 | // Overwrite semirandom bytes 502 | if end < 2 { 503 | continue; 504 | } 505 | 506 | let a = random(end - 2); 507 | let b = match rdtsc() & 1 { 508 | 0 => random(std::cmp::min(BUFSIZE - 2, end - a - 2)) + a + 2, 509 | _ => random(32) + a + 2, 510 | }; 511 | 512 | let _ = output.write(&data[..a]); 513 | for _ in a..b { 514 | let r = random(end - 1); 515 | 516 | // Access to a single character in &[u8] 517 | let _ = output.write(&[data[r]]); 518 | } 519 | 520 | // Possible b can be longer than data 521 | if end > b { 522 | let _ = output.write(&data[b..]); 523 | } 524 | return; 525 | } 526 | 25..29 => { 527 | if end < 2 { 528 | continue; 529 | } 530 | 531 | let mut result = None; 532 | 533 | // Attempt to find a number at a random location in the data buffer 534 | for _ in 0..random(AIMROUNDS) { 535 | if result.is_some() { 536 | break; 537 | } 538 | 539 | result = seek_num(data); 540 | } 541 | 542 | match result { 543 | Some((num_start, num_end)) => { 544 | // Write the data before the number 545 | let _ = output.write(&data[..num_start]); 546 | 547 | // Try to parse the found number into a usize 548 | if let Ok(num) = str::from_utf8(&data[num_start..num_end]) 549 | .unwrap() 550 | .parse::() 551 | { 552 | // Write the twiddled number 553 | let twiddled = if num == 0 { twiddle(0) } else { twiddle(num) }; 554 | let raw_bytes: [u8; 8] = unsafe { std::mem::transmute(twiddled) }; 555 | let _ = output.write(&raw_bytes); 556 | } 557 | 558 | // Write the rest of the buffer 559 | let _ = output.write(&data[num_end..]); 560 | } 561 | _ => { 562 | // Did not find a number in the data buffer 563 | // Continue to try a different mutation method 564 | // println!("Did not find number"); 565 | continue; 566 | } 567 | } 568 | 569 | return; 570 | } 571 | 29..35 => { 572 | // delimited swap 573 | 574 | match drange(data) { 575 | // If we didn't find a delimiter, try again for a different mutation strategy 576 | None => continue, 577 | Some((delim1_start, delim1_end)) => { 578 | let delim = data[delim1_start..].iter().nth(0).unwrap(); 579 | match other_drange(data, *delim) { 580 | None => continue, 581 | Some((delim2_start, delim2_end)) => { 582 | // Swap the two found delimited substrings 583 | let _ = output.write(&data[..delim1_start]); 584 | let _ = output.write(&data[delim2_start..delim2_end]); 585 | if delim2_start > delim1_end { 586 | let _ = output.write(&data[delim1_end..delim2_start]); 587 | } 588 | let _ = output.write(&data[delim1_start..delim1_end]); 589 | let _ = output.write(&data[delim2_end..]); 590 | } 591 | } 592 | } 593 | } 594 | 595 | return; 596 | } 597 | _ => unimplemented!(), 598 | } 599 | } 600 | } 601 | 602 | fn ni_area(data: &[u8], samples: &Vec>, n: usize, output: &mut W) { 603 | let length = data.len(); 604 | // println!("ni_area: data.len(): {} n: {}", length, n); 605 | if n == 0 { 606 | let _ = output.write(data); 607 | } else if n == 1 || length < 256 { 608 | mutate_area(data, samples, output); 609 | } else { 610 | let mut split = random(length); 611 | while split == 1 { 612 | split = random(length); 613 | // println!("Finding new split: {}", split); 614 | } 615 | 616 | // let new_n = random(n / 2); 617 | let new_n = random(n - random(n)); 618 | 619 | /* 620 | println!( 621 | "ni_area: length: {} n: {} new_n: {} split: {}", 622 | length, n, new_n, split 623 | ); 624 | */ 625 | ni_area(&data[..split], samples, n - new_n, output); 626 | ni_area(&data[split..], samples, new_n, output); 627 | } 628 | } 629 | 630 | /// Mutate a corpus of samples for a given number of results 631 | /// 632 | /// # Example 633 | /// 634 | /// ``` 635 | /// let samples = vec![ 636 | /// "", 637 | /// "", 638 | /// "" 639 | /// ]; 640 | /// 641 | /// let mutations = ni_rs::mutate_samples(samples, 32); 642 | /// assert_eq!(mutations.len(), 32); 643 | /// ``` 644 | pub fn mutate_samples_n(samples: &Vec>, rounds: usize) -> Vec> { 645 | let mut result = Vec::new(); 646 | // println!("mutate_samples_n: samples.len(): {}", samples.len()); 647 | 648 | for _ in 0..rounds { 649 | let mut output_sample = Vec::new(); 650 | let curr_sample = &samples[random(samples.len())]; 651 | // println!("mutate_sampels_n: curr_sample.len(): {}", curr_sample.len()); 652 | let n = if rdtsc() & 3 == 1 { 653 | 1 654 | } else { 655 | 2 + random(curr_sample.len() >> 12 + 8) 656 | }; 657 | // println!("mutate_sampels_n: n: {}", n); 658 | ni_area(&curr_sample, &samples, n, &mut output_sample); 659 | result.push(output_sample); 660 | } 661 | 662 | result 663 | } 664 | 665 | /// Mutate a corpus of samples for a single result 666 | /// 667 | /// # Example 668 | /// 669 | /// ``` 670 | /// let samples = vec![ 671 | /// "", 672 | /// "", 673 | /// "" 674 | /// ]; 675 | /// 676 | /// let mutation = ni_rs::mutate_samples(samples); 677 | /// ``` 678 | pub fn mutate_samples(samples: &Vec>) -> Vec { 679 | mutate_samples_n(samples, 1) 680 | .iter() 681 | .next() 682 | .unwrap() 683 | .to_owned() 684 | } 685 | 686 | /// Mutate a single sample for a single result 687 | /// 688 | /// # Example 689 | /// 690 | /// ``` 691 | /// let data = ""; 692 | /// let res = ni_rs::mutate(data); 693 | /// ``` 694 | pub fn mutate(data: Vec) -> Vec { 695 | let samples = vec![data]; 696 | mutate_samples_n(&samples, 1) 697 | .iter() 698 | .next() 699 | .unwrap() 700 | .to_owned() 701 | } 702 | 703 | /// Mutate a single sample for a given number of results 704 | /// 705 | /// # Example 706 | /// 707 | /// ``` 708 | /// let data = ""; 709 | /// let mutations = ni_rs::mutate_n(data, 32); 710 | /// assert_eq!(mutations.len(), 32); 711 | /// ``` 712 | pub fn mutate_n(data: Vec, rounds: usize) -> Vec> { 713 | let samples = vec![data]; 714 | mutate_samples_n(&samples, rounds) 715 | } 716 | --------------------------------------------------------------------------------