Compare commits
5 Commits
7a841eac8f
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbb9241869 | ||
|
|
4302adad55 | ||
| 2953b18df7 | |||
| b4ba730818 | |||
| 01da4a6c39 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
45
.vscode/launch.json
vendored
Normal file
45
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'all_the_passwords'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--bin=all_the_passwords",
|
||||||
|
"--package=all_the_passwords"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "all_the_passwords",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'all_the_passwords'",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"--no-run",
|
||||||
|
"--bin=all_the_passwords",
|
||||||
|
"--package=all_the_passwords"
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "all_the_passwords",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
32
Cargo.lock
generated
Normal file
32
Cargo.lock
generated
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "all_the_passwords"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"num-format",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-format"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3"
|
||||||
|
dependencies = [
|
||||||
|
"arrayvec",
|
||||||
|
"itoa",
|
||||||
|
]
|
||||||
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "all_the_passwords"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
num-format = "0.4.4"
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
175
src/main.rs
Normal file
175
src/main.rs
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
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};
|
||||||
|
|
||||||
|
/// The length of the combination
|
||||||
|
const LEN: usize = 5;
|
||||||
|
|
||||||
|
/// How many combinations have we create thus far
|
||||||
|
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
|
||||||
|
///
|
||||||
|
/// 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 = 2;
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
// Try multithreading...
|
||||||
|
let mut tasks = Vec::new();
|
||||||
|
let mut counters = Vec::new();
|
||||||
|
let mut buffers = Vec::new();
|
||||||
|
|
||||||
|
// 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(©, &i.to_string(), output_len, counter, buffer);
|
||||||
|
});
|
||||||
|
tasks.push(worker);
|
||||||
|
}
|
||||||
|
|
||||||
|
// A thread just to report stats
|
||||||
|
let timer = thread::spawn(move || {
|
||||||
|
let mut out = stderr();
|
||||||
|
|
||||||
|
while unsafe { !IS_DONE } {
|
||||||
|
let mut seconds_elapsed = start.elapsed().as_secs() as u128;
|
||||||
|
// prevent div by zero err
|
||||||
|
if seconds_elapsed == 0 { seconds_elapsed = 1; }
|
||||||
|
|
||||||
|
let mut combinations =0;
|
||||||
|
for i in &counters {
|
||||||
|
combinations += *i.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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),
|
||||||
|
unsafe {TOTAL}.to_formatted_string(&Locale::en_NA),
|
||||||
|
char_list.len(),
|
||||||
|
output_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let charset = Charset::AllAlpha.as_str();
|
||||||
|
// 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 = (charset.len() as u128).pow(LEN.try_into().unwrap());
|
||||||
|
unsafe { TOTAL = t as usize };
|
||||||
|
eprintln!(
|
||||||
|
"\nCalculating {} combinations...\n(Number may be truncated if too large)\n",
|
||||||
|
unsafe { TOTAL }.to_formatted_string(&Locale::en_NA)
|
||||||
|
);
|
||||||
|
|
||||||
|
start(&charset, 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<RwLock<u128>>, buffer: Arc<RwLock<Vec<String>>>) {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user