all_strings/src/main.rs

145 lines
5.0 KiB
Rust
Raw Normal View History

use std::hint::black_box;
2024-03-05 05:22:08 +00:00
use std::io::{stderr, Write};
use std::sync::{Arc, RwLock};
2024-03-05 05:22:08 +00:00
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;
2024-03-05 05:22:08 +00:00
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;
2024-03-05 05:22:08 +00:00
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(&copy, &i.to_string(), output_len, counter, buffer);
});
tasks.push(worker);
}
2024-03-05 05:22:08 +00:00
// 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")
}
2024-03-05 05:22:08 +00:00
// output P/s
let _ = out.write(
format!("\rCalculated {}, at {}p/s, {:.5}%",
2024-03-05 05:22:08 +00:00
sofar.to_formatted_string(&Locale::en_NA),
(sofar / elapsed).to_formatted_string(&Locale::en_NA),
sofar as f64 / unsafe { TOTAL as f64 } * 100f64,
2024-03-05 05:22:08 +00:00
).as_bytes());
let _ = out.flush();
sleep(Duration::from_secs(CHECK_IN_DELAY));
};
2024-03-05 05:22:08 +00:00
});
// Block waiting for all the tasks to finish
for i in tasks {i.join().unwrap();}
2024-03-05 05:22:08 +00:00
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());
2024-03-05 05:22:08 +00:00
unsafe { TOTAL = t as usize };
eprintln!(
"Calculating {} combinations...\n(Number may be truncated if too large)\n",
2024-03-05 05:22:08 +00:00
unsafe { TOTAL }.to_formatted_string(&Locale::en_NA)
);
start(ALL_CHARS_95, LEN);
2024-03-05 05:22:08 +00:00
}
/// Recursively create and print every combination of
2024-03-05 05:22:08 +00:00
/// `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.
2024-03-05 05:22:08 +00:00
///
/// * `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<RwLock<u128>>, buffer: Arc<RwLock<Vec<String>>>) {
2024-03-05 05:22:08 +00:00
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;
2024-03-05 05:22:08 +00:00
}
}
// 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());
2024-03-05 05:22:08 +00:00
}
}
}
}