2024-10-28 16:30:22 +00:00
use std ::hint ::black_box ;
2024-03-05 05:22:08 +00:00
use std ::io ::{ stderr , Write } ;
2024-10-28 16:30:22 +00:00
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 } ;
2024-10-28 16:30:22 +00:00
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 ;
2024-10-28 16:30:22 +00:00
/// 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 ( ) ;
2024-10-28 16:30:22 +00:00
// 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 ( 0 u128 ) ) ;
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 ; }
2024-10-28 16:30:22 +00:00
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 (
2024-10-28 16:30:22 +00:00
format! ( " \r Calculated {} , 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 ) ,
2024-10-28 16:30:22 +00:00
sofar as f64 / unsafe { TOTAL as f64 } * 100 f64 ,
2024-03-05 05:22:08 +00:00
) . as_bytes ( ) ) ;
let _ = out . flush ( ) ;
2024-10-28 16:30:22 +00:00
sleep ( Duration ::from_secs ( CHECK_IN_DELAY ) ) ;
} ;
2024-03-05 05:22:08 +00:00
} ) ;
2024-10-28 16:30:22 +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.
2024-10-28 16:30:22 +00:00
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! (
2024-10-28 16:30:22 +00:00
" 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 )
) ;
2024-10-28 16:30:22 +00:00
start ( ALL_CHARS_95 , LEN ) ;
2024-03-05 05:22:08 +00:00
}
2024-10-28 16:30:22 +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
2024-10-28 16:30:22 +00:00
/// 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.
2024-10-28 16:30:22 +00:00
///
/// * `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 ( ) {
2024-10-28 16:30:22 +00:00
// 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 ( ) {
2024-10-28 16:30:22 +00:00
calc ( choices , & ( prefix . to_string ( ) + & i . to_string ( ) ) , len , counter . clone ( ) , buffer . clone ( ) ) ;
2024-03-05 05:22:08 +00:00
}
}
}
}