use std::hint::black_box; use std::io::{stderr, Write}; use std::sync::{Arc, RwLock}; use std::thread::{self, sleep}; use std::time::{Duration, Instant}; use num_format::{Locale, ToFormattedString}; const ALL_CHARS_95: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.<>/?;':\"[]{}\\|-=_+`~!@#$%^&*() "; // const ALL_ALPHA: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // const ALL_ALPHA_NUM: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; const LEN: usize = 8; static mut TOTAL: usize = 0; /// Pseudo main /// /// Starts the number crunching (blocking on main thread!) and the /// timer thread which watches the main thread while /// reporting statistics about it. fn start(char_list: &str, output_len: usize) { /// Flag to communicate between main (combination crunching) and /// timer threads. static mut IS_DONE: bool = false; /// Seconds between the calculating p/s and percentage done. const CHECK_IN_DELAY: u64 = 1; let start = Instant::now(); // Try multithreading... let mut tasks = Vec::new(); let mut counters = Vec::new(); let mut buffers = Vec::new(); // Start threads for i in char_list.chars() { let counter = Arc::new(RwLock::new(0u128)); let buffer = Arc::new(RwLock::new(Vec::new())); counters.push(counter.clone()); buffers.push(buffer.clone()); let copy = char_list.to_owned(); let worker = thread::spawn(move || { calc(©, &i.to_string(), output_len, counter, buffer); }); tasks.push(worker); } // Just to report stats let timer = thread::spawn(move || { let mut out = stderr(); while unsafe { !IS_DONE } { let mut elapsed = start.elapsed().as_secs() as u128; // prevent div by zero err if elapsed == 0 { elapsed = 1; } let mut sofar =0; for ele in &counters { sofar += *ele.read().unwrap(); }; // This doesn't free the memory fast enough! // FIXME for i in &buffers { for j in &*i.read().unwrap() { let x = j; black_box(x); } // prevent memory footprint from getting too big i.write().unwrap().clear(); println!("clear") } // output P/s let _ = out.write( format!("\rCalculated {}, at {}p/s, {:.5}%", sofar.to_formatted_string(&Locale::en_NA), (sofar / elapsed).to_formatted_string(&Locale::en_NA), sofar as f64 / unsafe { TOTAL as f64 } * 100f64, ).as_bytes()); let _ = out.flush(); sleep(Duration::from_secs(CHECK_IN_DELAY)); }; }); // Block waiting for all the tasks to finish for i in tasks {i.join().unwrap();} unsafe { IS_DONE = true; } timer.join().unwrap(); eprintln!("\r{}ms to calculate the permutations (p) for {} chars (n) across {} slots (r).", start.elapsed().as_millis().to_formatted_string(&Locale::en_NA), char_list.len(), output_len); } fn main() { // All the status messages are printed on stderr // This way you can pipe the actual permutations into // a file whilst still getting debug messages. let t = (ALL_CHARS_95.len() as u128).pow(LEN.try_into().unwrap()); unsafe { TOTAL = t as usize }; eprintln!( "Calculating {} combinations...\n(Number may be truncated if too large)\n", unsafe { TOTAL }.to_formatted_string(&Locale::en_NA) ); start(ALL_CHARS_95, LEN); } /// Recursively create and print every combination of /// `choices` chars inside a string of `len` length. /// /// /// * `choices` string containing all the chars you wish /// to build random strings with. /// /// * `prefix` should be a blank string `""` unless you /// actually want a prefix on every output. This variable is /// mainly used within the recursive nature of the function. /// /// * `len` the size that all the output strings wil be. /// /// * `counter` what to count into fn calc(choices: &str, prefix: &str, len: usize, counter: Arc>, buffer: Arc>>) { let need_to_fill = len - prefix.len(); match need_to_fill { // the last char in the sequence, time to print 1 => { for i in choices.chars() { // This line causes a 10x slowdown buffer.write().unwrap().push(format!("{}{}", prefix, i)); // Needs to be in two lines like this, otherwise the write blocks on the read. let temp = *counter.read().unwrap(); *counter.write().unwrap() = temp+1; } } // need to recurse farther into the sequence _ => { for i in choices.chars() { calc(choices, &(prefix.to_string() + &i.to_string()), len, counter.clone(), buffer.clone()); } } } }