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