2020use crate :: util:: md5:: hash;
2121use std:: sync:: atomic:: Ordering :: Relaxed ;
2222use std:: sync:: atomic:: { AtomicBool , AtomicU32 } ;
23- use std:: sync:: mpsc:: { channel, Sender } ;
2423use std:: sync:: Arc ;
2524use std:: thread;
2625
27- type Input = ( u32 , u32 ) ;
28-
29- enum Found {
30- First ( u32 ) ,
31- Second ( u32 ) ,
26+ #[ derive( Clone ) ]
27+ pub struct Shared {
28+ prefix : String ,
29+ done : Arc < AtomicBool > ,
30+ counter : Arc < AtomicU32 > ,
31+ first : Arc < AtomicU32 > ,
32+ second : Arc < AtomicU32 > ,
3233}
3334
34- pub fn parse ( input : & str ) -> Input {
35- let prefix = input. trim ( ) . to_string ( ) ;
36- let done = Arc :: new ( AtomicBool :: new ( false ) ) ;
37- let counter = Arc :: new ( AtomicU32 :: new ( 1000 ) ) ;
38- let ( tx, rx) = channel :: < Found > ( ) ;
35+ pub fn parse ( input : & str ) -> Shared {
36+ let shared = Shared {
37+ prefix : input. trim ( ) . to_string ( ) ,
38+ done : Arc :: new ( AtomicBool :: new ( false ) ) ,
39+ counter : Arc :: new ( AtomicU32 :: new ( 1000 ) ) ,
40+ first : Arc :: new ( AtomicU32 :: new ( u32:: MAX ) ) ,
41+ second : Arc :: new ( AtomicU32 :: new ( u32:: MAX ) ) ,
42+ } ;
3943
4044 // Handle the first 999 numbers specially as the number of digits varies.
4145 for n in 1 ..1000 {
42- let string = format ! ( "{prefix}{n}" ) ;
43- check_hash ( string. as_bytes ( ) , n, & tx) ;
44- }
45-
46- // Use as many cores as possible to parallelize the search.
47- for _ in 0 ..thread:: available_parallelism ( ) . unwrap ( ) . get ( ) {
48- let prefix = prefix. clone ( ) ;
49- let done = done. clone ( ) ;
50- let counter = counter. clone ( ) ;
51- let tx = tx. clone ( ) ;
52- thread:: spawn ( move || worker ( & prefix, & done, & counter, & tx) ) ;
46+ let string = format ! ( "{}{}" , shared. prefix, n) ;
47+ check_hash ( string. as_bytes ( ) , n, & shared) ;
5348 }
5449
55- // Explicitly drop the reference to the sender object so that when all search threads finish,
56- // there will be no remaining references. When this happens `rx.recv` will return
57- // `Error` and exit the loop below. This ensures we wait to receive results from all threads,
58- // to handle the edge case where two values could be close together and found out of order.
59- drop ( tx) ;
50+ // Use as many cores as possible to parallelize the remaining search.
51+ let handles: Vec < _ > = ( 0 ..thread:: available_parallelism ( ) . unwrap ( ) . get ( ) )
52+ . map ( |_| {
53+ let shared_clone = shared. clone ( ) ;
54+ thread:: spawn ( move || worker ( & shared_clone) )
55+ } )
56+ . collect ( ) ;
6057
61- // We could potentially find multiple values, keep only the first occurence of each one.
62- let mut first = u32:: MAX ;
63- let mut second = u32:: MAX ;
64-
65- while let Ok ( message) = rx. recv ( ) {
66- match message {
67- Found :: First ( value) => {
68- first = first. min ( value) ;
69- }
70- Found :: Second ( value) => {
71- second = second. min ( value) ;
72- done. store ( true , Relaxed ) ;
73- }
74- }
58+ // Wait for threads to finish
59+ for handle in handles {
60+ let _ = handle. join ( ) ;
7561 }
7662
77- ( first , second )
63+ shared
7864}
7965
80- pub fn part1 ( input : & Input ) -> u32 {
81- input. 0
66+ pub fn part1 ( input : & Shared ) -> u32 {
67+ input. first . load ( Relaxed )
8268}
8369
84- pub fn part2 ( input : & Input ) -> u32 {
85- input. 1
70+ pub fn part2 ( input : & Shared ) -> u32 {
71+ input. second . load ( Relaxed )
8672}
8773
88- fn check_hash ( buffer : & [ u8 ] , n : u32 , tx : & Sender < Found > ) {
74+ fn check_hash ( buffer : & [ u8 ] , n : u32 , shared : & Shared ) {
8975 let ( result, ..) = hash ( buffer) ;
9076
9177 if result & 0xffffff00 == 0 {
92- let _ = tx. send ( Found :: Second ( n) ) ;
78+ shared. done . store ( true , Relaxed ) ;
79+ shared. second . fetch_min ( n, Relaxed ) ;
9380 } else if result & 0xfffff000 == 0 {
94- let _ = tx . send ( Found :: First ( n ) ) ;
81+ shared . first . fetch_min ( n , Relaxed ) ;
9582 }
9683}
9784
98- fn worker ( prefix : & str , done : & Arc < AtomicBool > , counter : & Arc < AtomicU32 > , tx : & Sender < Found > ) {
99- while !done. load ( Relaxed ) {
100- let start = counter. fetch_add ( 1000 , Relaxed ) ;
101- let string = format ! ( "{prefix}{start}" ) ;
85+ fn worker ( shared : & Shared ) {
86+ while !shared . done . load ( Relaxed ) {
87+ let offset = shared . counter . fetch_add ( 1000 , Relaxed ) ;
88+ let string = format ! ( "{}{}" , shared . prefix , offset ) ;
10289 let size = string. len ( ) - 3 ;
10390 let mut buffer = string. as_bytes ( ) . to_vec ( ) ;
10491
@@ -107,7 +94,7 @@ fn worker(prefix: &str, done: &Arc<AtomicBool>, counter: &Arc<AtomicU32>, tx: &S
10794 buffer[ size] = b'0' + ( n / 100 ) as u8 ;
10895 buffer[ size + 1 ] = b'0' + ( ( n / 10 ) % 10 ) as u8 ;
10996 buffer[ size + 2 ] = b'0' + ( n % 10 ) as u8 ;
110- check_hash ( & buffer, start + n, tx ) ;
97+ check_hash ( & buffer, offset + n, shared ) ;
11198 }
11299 }
113100}
0 commit comments