1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#![no_std]
#[derive(Debug)]
pub struct Demon {
pub last_sample: u32,
pub last_mixing_sample: u32,
pub run: u32,
pub key: u32,
pub mix: u32,
pub ops_remaining: u32,
pub samples_remaining: u32,
}
#[derive(Debug)]
pub enum Error {
NeedMoreSamples,
Timeout,
}
pub type Result<T> = core::result::Result<T, Error>;
impl Default for Demon {
fn default() -> Self {
Demon {
key: 0xACACACAC,
last_sample: 0,
last_mixing_sample: 0,
mix: 0xF0F0F0F0,
run: 0,
ops_remaining: 100,
samples_remaining: 100_000,
}
}
}
impl Demon {
pub fn take_sample(&mut self, sample: u32) -> Result<[u8; 4]> {
self.samples_remaining = self.samples_remaining.saturating_sub(1);
if sample == self.last_sample {
self.run = self.run.saturating_add(1);
return Err(Error::NeedMoreSamples);
}
self.last_sample = sample;
if self.run != 0 {
self.mix = self.mix.saturating_add(1 << (self.run & 31));
self.mix = self.mix.rotate_right(self.run);
}
self.run = 0;
let sample_bits = sample & 0b11;
let candidate_bits = (self.last_mixing_sample & 0b11) ^ sample_bits;
let changed_data = candidate_bits != 0;
let new_mix = self.mix.rotate_right(candidate_bits) ^ sample_bits;
match (changed_data, (new_mix != 0)) {
(true, true) => {
self.key = self.key.wrapping_add(new_mix);
self.key ^= self.mix;
self.key = self.key.rotate_left((candidate_bits << 2) | sample_bits);
self.last_mixing_sample = sample;
self.mix = new_mix;
self.ops_remaining = self.ops_remaining.saturating_sub(1);
}
(true, false) => {
self.key = self.key.rotate_left(7);
}
(false, true) => {
self.key = self.key.rotate_right(7);
}
(false, false) => {
self.key = !self.key;
}
}
if self.ops_remaining == 0 {
Ok(self.key.to_ne_bytes())
} else if self.samples_remaining == 0 {
Err(Error::Timeout)
} else {
Err(Error::NeedMoreSamples)
}
}
}