1use crate::{FourWordError, Result};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23#[repr(u8)]
24pub enum IPv6PatternId {
25 Loopback = 0,
26 Unspecified = 1,
27 LinkLocal = 2,
28 Documentation = 3,
29 UniqueLocal = 4,
30 GlobalUnicast = 5,
31 Multicast = 6,
32 Other = 7, }
34
35impl IPv6PatternId {
36 pub fn to_bits(self) -> u8 {
38 self as u8 & 0x7 }
40
41 pub fn from_bits(bits: u8) -> Self {
43 match bits & 0x7 {
44 0 => IPv6PatternId::Loopback,
45 1 => IPv6PatternId::Unspecified,
46 2 => IPv6PatternId::LinkLocal,
47 3 => IPv6PatternId::Documentation,
48 4 => IPv6PatternId::UniqueLocal,
49 5 => IPv6PatternId::GlobalUnicast,
50 6 => IPv6PatternId::Multicast,
51 7 => IPv6PatternId::Other,
52 _ => unreachable!(),
53 }
54 }
55
56 pub fn from_ipv6_pattern(pattern: &crate::ipv6_perfect_patterns::IPv6Pattern) -> Self {
58 use crate::ipv6_perfect_patterns::IPv6Pattern;
59 match pattern {
60 IPv6Pattern::Loopback => IPv6PatternId::Loopback,
61 IPv6Pattern::Unspecified => IPv6PatternId::Unspecified,
62 IPv6Pattern::LinkLocal(_) => IPv6PatternId::LinkLocal,
63 IPv6Pattern::Documentation(_) => IPv6PatternId::Documentation,
64 IPv6Pattern::UniqueLocal(_) => IPv6PatternId::UniqueLocal,
65 IPv6Pattern::GlobalUnicast(_) => IPv6PatternId::GlobalUnicast,
66 IPv6Pattern::Multicast(_) => IPv6PatternId::Multicast,
67 IPv6Pattern::CloudProvider(_)
68 | IPv6Pattern::CommonProvider(_)
69 | IPv6Pattern::Unstructured => IPv6PatternId::Other,
70 }
71 }
72}
73
74pub struct IPv6PatternFeistel;
76
77impl IPv6PatternFeistel {
78 pub fn encode(pattern_id: IPv6PatternId, data: u64, data_bits: usize) -> Result<u64> {
83 if data_bits > 61 {
84 return Err(FourWordError::InvalidInput(
85 "Data too large for Feistel encoding (max 61 bits)".to_string(),
86 ));
87 }
88
89 let pattern_bits = pattern_id.to_bits() as u64;
90
91 if data_bits <= 32 {
93 Self::encode_small(pattern_bits, data, data_bits)
94 } else {
95 Self::encode_large(pattern_bits, data, data_bits)
96 }
97 }
98
99 pub fn decode(mixed_data: u64, original_data_bits: usize) -> Result<(IPv6PatternId, u64)> {
103 if original_data_bits > 61 {
104 return Err(FourWordError::InvalidInput(
105 "Data too large for Feistel decoding (max 61 bits)".to_string(),
106 ));
107 }
108
109 if original_data_bits <= 32 {
111 Self::decode_small(mixed_data, original_data_bits)
112 } else {
113 Self::decode_large(mixed_data, original_data_bits)
114 }
115 }
116
117 fn encode_small(pattern_bits: u64, data: u64, data_bits: usize) -> Result<u64> {
119 let mut result = 0u64;
125 let pattern_mask = pattern_bits;
126 let data_mask = data & ((1u64 << data_bits) - 1);
127
128 result |= pattern_mask & 1; result |= ((pattern_mask >> 1) & 1) << 8; result |= ((pattern_mask >> 2) & 1) << 16; let mut data_pos = 0;
135 let mut result_pos = 1;
136
137 while data_pos < data_bits && result_pos < 64 {
138 if result_pos == 8 || result_pos == 16 {
139 result_pos += 1; continue;
141 }
142
143 let bit = (data_mask >> data_pos) & 1;
144 result |= bit << result_pos;
145 data_pos += 1;
146 result_pos += 1;
147 }
148
149 Ok(result)
150 }
151
152 fn decode_small(mixed_data: u64, original_data_bits: usize) -> Result<(IPv6PatternId, u64)> {
154 let pattern_a = mixed_data & 1;
156 let pattern_b = (mixed_data >> 8) & 1;
157 let pattern_c = (mixed_data >> 16) & 1;
158 let pattern_bits = pattern_a | (pattern_b << 1) | (pattern_c << 2);
159
160 let mut data = 0u64;
162 let mut data_pos = 0;
163 let mut mixed_pos = 1;
164
165 while data_pos < original_data_bits && mixed_pos < 64 {
166 if mixed_pos == 8 || mixed_pos == 16 {
167 mixed_pos += 1; continue;
169 }
170
171 let bit = (mixed_data >> mixed_pos) & 1;
172 data |= bit << data_pos;
173 data_pos += 1;
174 mixed_pos += 1;
175 }
176
177 let pattern_id = IPv6PatternId::from_bits(pattern_bits as u8);
178 Ok((pattern_id, data))
179 }
180
181 fn encode_large(pattern_bits: u64, data: u64, data_bits: usize) -> Result<u64> {
183 if data_bits > 61 {
188 return Err(FourWordError::InvalidInput(
189 "Data too large for pattern encoding (max 61 bits)".to_string(),
190 ));
191 }
192
193 let data_mask = (1u64 << data_bits) - 1;
194 let masked_data = data & data_mask;
195
196 let result = (pattern_bits << 61) | masked_data;
198 Ok(result)
199 }
200
201 fn decode_large(mixed_data: u64, original_data_bits: usize) -> Result<(IPv6PatternId, u64)> {
203 let pattern_bits = (mixed_data >> 61) & 0x7;
205 let pattern_id = IPv6PatternId::from_bits(pattern_bits as u8);
206
207 let data_mask = (1u64 << original_data_bits) - 1;
209 let data = mixed_data & data_mask;
210
211 Ok((pattern_id, data))
212 }
213}
214
215#[cfg(test)]
216mod tests {
217 use super::*;
218
219 #[test]
220 fn test_pattern_id_conversion() {
221 for id in 0..8 {
222 let pattern = IPv6PatternId::from_bits(id);
223 assert_eq!(pattern.to_bits(), id);
224 }
225 }
226
227 #[test]
228 fn test_small_data_roundtrip() {
229 let test_cases = [
230 (IPv6PatternId::Loopback, 0x1234, 16),
231 (IPv6PatternId::Documentation, 0xABCDEF, 24),
232 (IPv6PatternId::LinkLocal, 0x12345678, 32),
233 ];
234
235 for (pattern_id, data, bits) in test_cases {
236 let encoded = IPv6PatternFeistel::encode(pattern_id, data, bits).unwrap();
237 let (decoded_pattern, decoded_data) =
238 IPv6PatternFeistel::decode(encoded, bits).unwrap();
239
240 assert_eq!(decoded_pattern, pattern_id);
241 assert_eq!(decoded_data, data);
242 }
243 }
244
245 #[test]
246 fn test_large_data_roundtrip() {
247 let test_cases = [
248 (IPv6PatternId::Loopback, 0x123456789ABC, 48),
249 (IPv6PatternId::Documentation, 0x1FFFFFFFFFFFF, 53),
250 (IPv6PatternId::LinkLocal, 0x1FFFFFFFFFFFFFFF, 61),
251 ];
252
253 for (pattern_id, data, bits) in test_cases {
254 println!("Testing pattern {pattern_id:?} with data 0x{data:X} ({bits} bits)");
255
256 let encoded = IPv6PatternFeistel::encode(pattern_id, data, bits).unwrap();
257 println!(" Encoded: 0x{encoded:X}");
258
259 let (decoded_pattern, decoded_data) =
260 IPv6PatternFeistel::decode(encoded, bits).unwrap();
261 println!(" Decoded pattern: {decoded_pattern:?}, data: 0x{decoded_data:X}");
262
263 assert_eq!(
264 decoded_pattern, pattern_id,
265 "Pattern mismatch for {pattern_id:?}"
266 );
267 assert_eq!(decoded_data, data, "Data mismatch for {pattern_id:?}");
268 }
269 }
270
271 #[test]
272 fn test_different_patterns_produce_different_output() {
273 let data = 0x12345678;
274 let bits = 32;
275
276 let encoded1 = IPv6PatternFeistel::encode(IPv6PatternId::Loopback, data, bits).unwrap();
277 let encoded2 =
278 IPv6PatternFeistel::encode(IPv6PatternId::Documentation, data, bits).unwrap();
279
280 assert_ne!(
281 encoded1, encoded2,
282 "Different patterns should produce different encoded data"
283 );
284 }
285
286 #[test]
287 fn test_deterministic_encoding() {
288 let pattern_id = IPv6PatternId::Documentation;
289 let data = 0xABCDEF;
290 let bits = 24;
291
292 let encoded1 = IPv6PatternFeistel::encode(pattern_id, data, bits).unwrap();
293 let encoded2 = IPv6PatternFeistel::encode(pattern_id, data, bits).unwrap();
294
295 assert_eq!(encoded1, encoded2, "Encoding should be deterministic");
296 }
297}