1use crate::util::md5::*;
21use crate::util::thread::*;
22use implementation::*;
23use std::sync::atomic::{AtomicU32, Ordering};
24
25pub struct Shared {
26 prefix: String,
27 iter: AtomicIter,
28 first: AtomicU32,
29 second: AtomicU32,
30}
31
32pub fn parse(input: &str) -> Shared {
33 let shared = Shared {
34 prefix: input.trim().to_owned(),
35 iter: AtomicIter::new(1000, 1000),
36 first: AtomicU32::new(u32::MAX),
37 second: AtomicU32::new(u32::MAX),
38 };
39
40 for n in 1..1000 {
42 let (mut buffer, size) = format_string(&shared.prefix, n);
43 check_hash(&mut buffer, size, n, &shared);
44 }
45
46 spawn(|| worker(&shared));
48 shared
49}
50
51pub fn part1(input: &Shared) -> u32 {
52 input.first.load(Ordering::Relaxed)
53}
54
55pub fn part2(input: &Shared) -> u32 {
56 input.second.load(Ordering::Relaxed)
57}
58
59fn format_string(prefix: &str, mut n: u32) -> ([u8; 64], usize) {
60 let prefix_len = prefix.len();
61 let digits = n.max(1).ilog10() as usize + 1;
62 let size = prefix_len + digits;
63
64 let mut buffer = [0; 64];
65 buffer[..prefix_len].copy_from_slice(prefix.as_bytes());
66
67 for i in (prefix_len..size).rev() {
68 buffer[i] = b'0' + (n % 10) as u8;
69 n /= 10;
70 }
71
72 (buffer, size)
73}
74
75fn check_hash(buffer: &mut [u8], size: usize, n: u32, shared: &Shared) {
76 let [result, ..] = hash(buffer, size);
77
78 if result & 0xffffff00 == 0 {
79 shared.second.fetch_min(n, Ordering::Relaxed);
80 shared.iter.stop();
81 } else if result & 0xfffff000 == 0 {
82 shared.first.fetch_min(n, Ordering::Relaxed);
83 }
84}
85
86#[cfg(not(feature = "simd"))]
87mod implementation {
88 use super::*;
89
90 pub(super) fn worker(shared: &Shared) {
91 while let Some(offset) = shared.iter.next() {
92 let (mut buffer, size) = format_string(&shared.prefix, offset);
93
94 for n in 0..1000 {
95 buffer[size - 3] = b'0' + (n / 100) as u8;
97 buffer[size - 2] = b'0' + ((n / 10) % 10) as u8;
98 buffer[size - 1] = b'0' + (n % 10) as u8;
99
100 check_hash(&mut buffer, size, offset + n, shared);
101 }
102 }
103 }
104}
105
106#[cfg(feature = "simd")]
107mod implementation {
108 use super::*;
109 use crate::util::bitset::*;
110 use crate::util::md5::simd::hash_fixed;
111 use std::simd::cmp::SimdPartialEq as _;
112 use std::simd::*;
113
114 #[expect(clippy::needless_range_loop)]
115 fn check_hash_simd<const N: usize>(
116 buffers: &mut [[u8; 64]; N],
117 size: usize,
118 start: u32,
119 offset: u32,
120 shared: &Shared,
121 ) {
122 for i in 0..N {
124 let n = offset + i as u32;
125 buffers[i][size - 3] = b'0' + (n / 100) as u8;
126 buffers[i][size - 2] = b'0' + ((n / 10) % 10) as u8;
127 buffers[i][size - 1] = b'0' + (n % 10) as u8;
128 }
129
130 let [result, ..] = hash_fixed(buffers, size);
131 let bitmask = (result & Simd::splat(0xfffff000)).simd_eq(Simd::splat(0)).to_bitmask();
132
133 for i in bitmask.biterator() {
134 if result[i] & 0xffffff00 == 0 {
135 shared.second.fetch_min(start + offset + i as u32, Ordering::Relaxed);
136 shared.iter.stop();
137 } else {
138 shared.first.fetch_min(start + offset + i as u32, Ordering::Relaxed);
139 }
140 }
141 }
142
143 pub(super) fn worker(shared: &Shared) {
144 while let Some(start) = shared.iter.next() {
145 let (prefix, size) = format_string(&shared.prefix, start);
146 let buffers = &mut [prefix; 32];
147
148 for offset in (0..992).step_by(32) {
149 check_hash_simd(buffers, size, start, offset, shared);
150 }
151
152 let buffers = &mut [prefix; 8];
153 check_hash_simd(buffers, size, start, 992, shared);
154 }
155 }
156}