Compare commits

...

3 Commits

Author SHA1 Message Date
Oliver Atkinson
bbb9241869 messing arround 2024-12-16 11:34:44 -07:00
Oliver Atkinson
4302adad55 cleaning...\ncleaning out my work computer 2024-10-28 10:30:22 -06:00
2953b18df7 readme 2024-03-04 22:30:13 -07:00
2 changed files with 110 additions and 46 deletions

View File

@@ -1,2 +1,8 @@
# all_strings # All Strings
A short bit of code to create every possible string combination of given length, with a given charset. The program outputs the combinations to `stdout` and periodic statistics to `stderr`.
This project uses quite a bit of `unsafe` blocks to acheive the equivilent of java's `public static` in the form of `static mut`. I know some people don't like unsafe rust code, if you are one of those people... too bad.
Since this code is just single-threaded I can only get about 5M permutations a second when not IO limited. (Tested by redirecting `stduout` to `/dev/null`). Later on I might make this multi-threaded to speed up the calculation speed.

View File

@@ -1,15 +1,34 @@
use std::hint::black_box;
use std::io::{stderr, Write}; use std::io::{stderr, Write};
use std::sync::{Arc, RwLock};
use std::thread::{self, sleep}; use std::thread::{self, sleep};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use num_format::{Locale, ToFormattedString}; use num_format::{Locale, ToFormattedString};
const ALL_CHARS: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.<>/?;':\"[]{}\\|-=_+`~!@#$%^&*()"; /// The length of the combination
const LEN: usize = 4; const LEN: usize = 5;
static mut SOFAR: u128 = 0; /// How many combinations have we create thus far
static mut TOTAL: usize = 0; static mut TOTAL: usize = 0;
#[allow(dead_code)]
enum Charset {
AllChars,
AllAlpha,
AllAlphaAndNum
}
impl Charset {
fn as_str(&self) -> &'static str {
match self {
Charset::AllChars => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.<>/?;':\"[]{}\\|-=_+`~!@#$%^&*() ",
Charset::AllAlpha => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
Charset::AllAlphaAndNum => "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
}
}
}
/// Pseudo main /// Pseudo main
/// ///
/// Starts the number crunching (blocking on main thread!) and the /// Starts the number crunching (blocking on main thread!) and the
@@ -19,73 +38,106 @@ fn start(char_list: &str, output_len: usize) {
/// Flag to communicate between main (combination crunching) and /// Flag to communicate between main (combination crunching) and
/// timer threads. /// timer threads.
static mut IS_DONE: bool = false; static mut IS_DONE: bool = false;
/// Seconds between the calculating p/s and percentage done.
const CHECK_IN_DELAY: u64 = 2;
let start = Instant::now(); let start = Instant::now();
// Try multithreading...
let mut tasks = Vec::new();
let mut counters = Vec::new();
let mut buffers = Vec::new();
// Just to report stats // Start generating 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);
}
// A thread just to report stats
let timer = thread::spawn(move || { let timer = thread::spawn(move || {
let mut out = stderr(); let mut out = stderr();
while unsafe { !IS_DONE } { while unsafe { !IS_DONE } {
let mut elapsed = start.elapsed().as_secs() as u128; let mut seconds_elapsed = start.elapsed().as_secs() as u128;
// prevent div by zero err // prevent div by zero err
if elapsed == 0 { elapsed = 1; } if seconds_elapsed == 0 { seconds_elapsed = 1; }
let sofar = unsafe {SOFAR}; let mut combinations =0;
// output P/s for i in &counters {
let _ = out.write( combinations += *i.read().unwrap();
format!("\rCalculated {} possibilities thus far, at {} p/s", };
sofar.to_formatted_string(&Locale::en_NA),
(sofar / elapsed).to_formatted_string(&Locale::en_NA),
).as_bytes());
let _ = out.flush();
sleep(Duration::from_secs(1));
/* // This doesn't free the memory fast enough! // FIXME
// output remaining time for i in &buffers {
// I haven't tested to see if this timer is somewhat accurate... for j in &*i.read().unwrap() {
// This is code I stole of reddit... what am I doing? let x = j;
let remaining = unsafe { TOTAL as u128 / (sofar / elapsed) }; black_box(x);
let sec = remaining % 60;
let min = (remaining / 60) % 60;
let hrs = (remaining / 60) / 60;
let _ = out.write(
// TODO clear the whole line, don't just put cursor to the begining...
format!("\r{}%, {}h:{}m:{}s remaining ",
unsafe { sofar / TOTAL as u128 },
hrs, min, sec
).as_bytes());
let _ = out.flush();
sleep(Duration::from_secs(1));
*/
} }
// prevent memory footprint from getting too big
i.write().unwrap().clear();
}
let time_remaining = {
let seconds_remaining = (unsafe {TOTAL as u128} - combinations) * seconds_elapsed;
// TODO hours in inacuate?
let hours = (seconds_remaining / 60) / 60;
let minutes = (seconds_remaining / 60) % 60;
let seconds = seconds_remaining % 60;
format!("{hours}h:{minutes}m:{seconds}s")
};
// output stats
let _ = out.write(
format!("\rCalculated {}, at {}p/s, {:.5}%, elapsed: {}s {}",
combinations.to_formatted_string(&Locale::en_NA),
(combinations / seconds_elapsed).to_formatted_string(&Locale::en_NA),
combinations as f64 / unsafe { TOTAL as f64 } * 100f64,
seconds_elapsed,
time_remaining
).as_bytes());
let _ = out.flush();
sleep(Duration::from_secs(CHECK_IN_DELAY));
};
}); });
// starting with the blank string is important // Block waiting for all the tasks to finish
calc(char_list, &"", output_len); for i in tasks {i.join().unwrap();}
unsafe { IS_DONE = true; } unsafe { IS_DONE = true; }
timer.join().unwrap(); 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); eprintln!("\r{}ms to calculate the {} permutations (p) for {} chars (n) across {} slots (r).",
start.elapsed().as_millis().to_formatted_string(&Locale::en_NA),
unsafe {TOTAL}.to_formatted_string(&Locale::en_NA),
char_list.len(),
output_len);
} }
fn main() { fn main() {
let charset = Charset::AllAlpha.as_str();
// All the status messages are printed on stderr // All the status messages are printed on stderr
// This way you can pipe the actual permutations into // This way you can pipe the actual permutations into
// a file whilst still getting debug messages. // a file whilst still getting debug messages.
let t = (ALL_CHARS.len() as u128).pow(LEN.try_into().unwrap()); let t = (charset.len() as u128).pow(LEN.try_into().unwrap());
unsafe { TOTAL = t as usize }; unsafe { TOTAL = t as usize };
eprintln!( eprintln!(
"Calculating {} combinations...\n(Number may be trunkated if too large)\n", "\nCalculating {} combinations...\n(Number may be truncated if too large)\n",
unsafe { TOTAL }.to_formatted_string(&Locale::en_NA) unsafe { TOTAL }.to_formatted_string(&Locale::en_NA)
); );
start(ALL_CHARS, LEN); start(&charset, LEN);
} }
/// Recurseively create and print every combination of /// Recursively create and print every combination of
/// `choices` chars inside a string of `len` length. /// `choices` chars inside a string of `len` length.
/// ///
/// ///
@@ -94,23 +146,29 @@ fn main() {
/// ///
/// * `prefix` should be a blank string `""` unless you /// * `prefix` should be a blank string `""` unless you
/// actually want a prefix on every output. This variable is /// actually want a prefix on every output. This variable is
/// mainly used within the recursive nature of the funciton. /// mainly used within the recursive nature of the function.
/// ///
/// * `len` the size that all the output strings wil be. /// * `len` the size that all the output strings wil be.
fn calc(choices: &str, prefix: &str, len: usize) { ///
/// * `counter` what to count into
fn calc(choices: &str, prefix: &str, len: usize, counter: Arc<RwLock<u128>>, buffer: Arc<RwLock<Vec<String>>>) {
let need_to_fill = len - prefix.len(); let need_to_fill = len - prefix.len();
match need_to_fill { match need_to_fill {
// the last char in the sequence, time to print // the last char in the sequence, time to print
1 => { 1 => {
for i in choices.chars() { for i in choices.chars() {
println!("{}{}", prefix, i); // This line causes a 10x slowdown
unsafe { SOFAR = SOFAR + 1 }; 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 // need to recurse farther into the sequence
_ => { _ => {
for i in choices.chars() { for i in choices.chars() {
calc(choices, &(prefix.to_string() + &i.to_string()), len); calc(choices, &(prefix.to_string() + &i.to_string()), len, counter.clone(), buffer.clone());
} }
} }
} }