20
20
use crate :: util:: md5:: hash;
21
21
use std:: sync:: atomic:: Ordering :: Relaxed ;
22
22
use std:: sync:: atomic:: { AtomicBool , AtomicU32 } ;
23
- use std:: sync:: mpsc:: { channel, Sender } ;
24
23
use std:: sync:: Arc ;
25
24
use std:: thread;
26
25
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 > ,
32
33
}
33
34
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
+ } ;
39
43
40
44
// Handle the first 999 numbers specially as the number of digits varies.
41
45
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) ;
53
48
}
54
49
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 ( ) ;
60
57
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 ( ) ;
75
61
}
76
62
77
- ( first , second )
63
+ shared
78
64
}
79
65
80
- pub fn part1 ( input : & Input ) -> u32 {
81
- input. 0
66
+ pub fn part1 ( input : & Shared ) -> u32 {
67
+ input. first . load ( Relaxed )
82
68
}
83
69
84
- pub fn part2 ( input : & Input ) -> u32 {
85
- input. 1
70
+ pub fn part2 ( input : & Shared ) -> u32 {
71
+ input. second . load ( Relaxed )
86
72
}
87
73
88
- fn check_hash ( buffer : & [ u8 ] , n : u32 , tx : & Sender < Found > ) {
74
+ fn check_hash ( buffer : & [ u8 ] , n : u32 , shared : & Shared ) {
89
75
let ( result, ..) = hash ( buffer) ;
90
76
91
77
if result & 0xffffff00 == 0 {
92
- let _ = tx. send ( Found :: Second ( n) ) ;
78
+ shared. done . store ( true , Relaxed ) ;
79
+ shared. second . fetch_min ( n, Relaxed ) ;
93
80
} else if result & 0xfffff000 == 0 {
94
- let _ = tx . send ( Found :: First ( n ) ) ;
81
+ shared . first . fetch_min ( n , Relaxed ) ;
95
82
}
96
83
}
97
84
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 ) ;
102
89
let size = string. len ( ) - 3 ;
103
90
let mut buffer = string. as_bytes ( ) . to_vec ( ) ;
104
91
@@ -107,7 +94,7 @@ fn worker(prefix: &str, done: &Arc<AtomicBool>, counter: &Arc<AtomicU32>, tx: &S
107
94
buffer[ size] = b'0' + ( n / 100 ) as u8 ;
108
95
buffer[ size + 1 ] = b'0' + ( ( n / 10 ) % 10 ) as u8 ;
109
96
buffer[ size + 2 ] = b'0' + ( n % 10 ) as u8 ;
110
- check_hash ( & buffer, start + n, tx ) ;
97
+ check_hash ( & buffer, offset + n, shared ) ;
111
98
}
112
99
}
113
100
}
0 commit comments