1extern crate alloc;
41
42use alloc::vec::Vec;
43use zerodds_foundation::md5;
44
45pub const KEY_HASH_LEN: usize = 16;
47
48#[must_use]
60pub fn compute_key_hash(
61 plain_cdr2_be_bytes: &[u8],
62 key_holder_max_size: usize,
63) -> [u8; KEY_HASH_LEN] {
64 if key_holder_max_size <= KEY_HASH_LEN {
65 let mut out = [0u8; KEY_HASH_LEN];
67 let n = core::cmp::min(plain_cdr2_be_bytes.len(), KEY_HASH_LEN);
68 out[..n].copy_from_slice(&plain_cdr2_be_bytes[..n]);
69 out
70 } else {
71 md5(plain_cdr2_be_bytes)
73 }
74}
75
76#[derive(Debug, Default)]
86pub struct PlainCdr2BeKeyHolder {
87 bytes: Vec<u8>,
88}
89
90impl PlainCdr2BeKeyHolder {
91 #[must_use]
93 pub fn new() -> Self {
94 Self::default()
95 }
96
97 #[must_use]
99 pub fn len(&self) -> usize {
100 self.bytes.len()
101 }
102
103 #[must_use]
105 pub fn is_empty(&self) -> bool {
106 self.bytes.is_empty()
107 }
108
109 #[must_use]
111 pub fn into_bytes(self) -> Vec<u8> {
112 self.bytes
113 }
114
115 #[must_use]
117 pub fn as_bytes(&self) -> &[u8] {
118 &self.bytes
119 }
120
121 fn pad_to(&mut self, align: usize) {
123 let pad = (align - (self.bytes.len() % align)) % align;
124 for _ in 0..pad {
125 self.bytes.push(0);
126 }
127 }
128
129 pub fn write_u8(&mut self, v: u8) {
131 self.bytes.push(v);
132 }
133
134 pub fn write_i8(&mut self, v: i8) {
136 self.bytes.push(v as u8);
137 }
138
139 pub fn write_u16(&mut self, v: u16) {
141 self.pad_to(2);
142 self.bytes.extend_from_slice(&v.to_be_bytes());
143 }
144
145 pub fn write_i16(&mut self, v: i16) {
147 self.pad_to(2);
148 self.bytes.extend_from_slice(&v.to_be_bytes());
149 }
150
151 pub fn write_u32(&mut self, v: u32) {
153 self.pad_to(4);
154 self.bytes.extend_from_slice(&v.to_be_bytes());
155 }
156
157 pub fn write_i32(&mut self, v: i32) {
159 self.pad_to(4);
160 self.bytes.extend_from_slice(&v.to_be_bytes());
161 }
162
163 pub fn write_u64(&mut self, v: u64) {
165 self.pad_to(4);
166 self.bytes.extend_from_slice(&v.to_be_bytes());
167 }
168
169 pub fn write_i64(&mut self, v: i64) {
171 self.pad_to(4);
172 self.bytes.extend_from_slice(&v.to_be_bytes());
173 }
174
175 pub fn write_f32(&mut self, v: f32) {
177 self.pad_to(4);
178 self.bytes.extend_from_slice(&v.to_be_bytes());
179 }
180
181 pub fn write_f64(&mut self, v: f64) {
183 self.pad_to(4);
184 self.bytes.extend_from_slice(&v.to_be_bytes());
185 }
186
187 pub fn write_string(&mut self, s: &str) {
192 self.pad_to(4);
193 let len = u32::try_from(s.len() + 1).unwrap_or(u32::MAX);
194 self.bytes.extend_from_slice(&len.to_be_bytes());
195 self.bytes.extend_from_slice(s.as_bytes());
196 self.bytes.push(0); }
198
199 pub fn write_bytes(&mut self, b: &[u8]) {
201 self.bytes.extend_from_slice(b);
202 }
203}
204
205#[must_use]
220pub fn keyhash_cdr2_be(
221 members: &[(u32, alloc::vec::Vec<u8>)],
222 key_holder_max_size: usize,
223) -> [u8; KEY_HASH_LEN] {
224 let mut sorted: alloc::vec::Vec<&(u32, alloc::vec::Vec<u8>)> = members.iter().collect();
225 sorted.sort_by_key(|(id, _)| *id);
226 let mut concat: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
227 for (_, bytes) in &sorted {
228 concat.extend_from_slice(bytes);
229 }
230 compute_key_hash(&concat, key_holder_max_size)
231}
232
233#[cfg(test)]
234#[allow(clippy::expect_used, clippy::unwrap_used)]
235mod tests {
236 use super::*;
237 use alloc::vec;
238
239 #[test]
242 fn small_keyholder_zero_padded_to_16() {
243 let bytes = 0x1234_5678u32.to_be_bytes();
245 let h = compute_key_hash(&bytes, 4);
246 assert_eq!(h[0..4], [0x12, 0x34, 0x56, 0x78]);
247 assert_eq!(h[4..16], [0u8; 12]);
248 }
249
250 #[test]
251 fn exactly_16_byte_keyholder_no_md5() {
252 let mut bytes = [0u8; 16];
253 for (i, b) in bytes.iter_mut().enumerate() {
254 *b = i as u8;
255 }
256 let h = compute_key_hash(&bytes, 16);
257 assert_eq!(h, bytes);
258 }
259
260 #[test]
261 fn input_shorter_than_max_zero_padded() {
262 let h = compute_key_hash(&[1, 2, 3, 4, 5], 16);
264 assert_eq!(&h[..5], &[1, 2, 3, 4, 5]);
265 assert_eq!(&h[5..], &[0u8; 11]);
266 }
267
268 #[test]
271 fn large_keyholder_md5_hashed() {
272 let bytes = [0u8; 20];
274 let h = compute_key_hash(&bytes, 20);
275 assert_eq!(
277 h,
278 [
279 0x44, 0x10, 0x18, 0x52, 0x52, 0x08, 0x45, 0x77, 0x05, 0xbf, 0x09, 0xa8, 0xee, 0x3c,
280 0x10, 0x93
281 ]
282 );
283 }
284
285 #[test]
286 fn md5_known_vector_empty_string() {
287 let h = compute_key_hash(&[], 17);
289 assert_eq!(
290 h,
291 [
292 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8,
293 0x42, 0x7e
294 ]
295 );
296 }
297
298 #[test]
299 fn md5_known_vector_abc() {
300 let h = compute_key_hash(b"abc", 17);
301 assert_eq!(
303 h,
304 [
305 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1,
306 0x7f, 0x72
307 ]
308 );
309 }
310
311 #[test]
314 fn keyholder_writes_u32_be() {
315 let mut h = PlainCdr2BeKeyHolder::new();
316 h.write_u32(0x1234_5678);
317 assert_eq!(h.into_bytes(), vec![0x12, 0x34, 0x56, 0x78]);
318 }
319
320 #[test]
321 fn keyholder_pads_to_4_for_u32_after_u8() {
322 let mut h = PlainCdr2BeKeyHolder::new();
323 h.write_u8(0xAA);
324 h.write_u32(0x1234_5678);
325 assert_eq!(h.into_bytes(), vec![0xAA, 0, 0, 0, 0x12, 0x34, 0x56, 0x78]);
327 }
328
329 #[test]
330 fn keyholder_u64_aligned_to_4_not_8() {
331 let mut h = PlainCdr2BeKeyHolder::new();
332 h.write_u8(1);
333 h.write_u64(0x1122_3344_5566_7788);
334 assert_eq!(h.len(), 1 + 3 + 8);
337 }
338
339 #[test]
340 fn keyholder_string_length_prefixed_with_nul() {
341 let mut h = PlainCdr2BeKeyHolder::new();
342 h.write_string("hi");
343 assert_eq!(h.into_bytes(), vec![0x00, 0x00, 0x00, 0x03, b'h', b'i', 0]);
345 }
346
347 #[test]
348 fn keyholder_struct_member_order() {
349 let mut h = PlainCdr2BeKeyHolder::new();
351 h.write_u32(42); h.write_string("foo"); let bytes = h.into_bytes();
354 assert_eq!(bytes.len(), 12);
356 assert_eq!(&bytes[0..4], &[0, 0, 0, 42]);
357 assert_eq!(&bytes[4..8], &[0, 0, 0, 4]);
358 assert_eq!(&bytes[8..12], &[b'f', b'o', b'o', 0]);
359 }
360
361 #[test]
362 fn keyholder_full_keyhash_pipeline_zero_pad() {
363 let mut h = PlainCdr2BeKeyHolder::new();
365 h.write_u32(0x1122_3344);
366 let h_bytes = h.into_bytes();
367 let key = compute_key_hash(&h_bytes, 4);
368 assert_eq!(&key[..4], &[0x11, 0x22, 0x33, 0x44]);
369 assert_eq!(&key[4..], &[0u8; 12]);
370 }
371
372 #[test]
373 fn keyholder_full_keyhash_pipeline_md5() {
374 let mut h = PlainCdr2BeKeyHolder::new();
376 h.write_string("hello-world-this-is-a-long-key-name-exceeding-16");
377 let h_bytes = h.into_bytes();
378 let key = compute_key_hash(&h_bytes, usize::MAX);
380 assert_ne!(key, [0u8; 16]);
382 }
383
384 #[test]
387 fn keyhash_cdr2_be_member_order_independent() {
388 let m_3 = (3u32, vec![0xAAu8, 0xBB, 0xCC, 0xDD]);
392 let m_5 = (5u32, vec![0x11u8, 0x22, 0x33, 0x44]);
393 let m_7 = (7u32, vec![0xDEu8, 0xAD, 0xBE, 0xEF]);
394
395 let order_a = vec![m_3.clone(), m_5.clone(), m_7.clone()];
396 let order_b = vec![m_7.clone(), m_3.clone(), m_5.clone()];
397 let order_c = vec![m_5.clone(), m_7.clone(), m_3.clone()];
398
399 let h_a = keyhash_cdr2_be(&order_a, 12);
400 let h_b = keyhash_cdr2_be(&order_b, 12);
401 let h_c = keyhash_cdr2_be(&order_c, 12);
402 assert_eq!(h_a, h_b);
403 assert_eq!(h_a, h_c);
404 let expected = {
406 let mut v = Vec::new();
407 v.extend_from_slice(&m_3.1);
408 v.extend_from_slice(&m_5.1);
409 v.extend_from_slice(&m_7.1);
410 v.resize(16, 0);
411 v
412 };
413 assert_eq!(h_a.as_slice(), expected.as_slice());
414 }
415
416 #[test]
417 fn keyhash_cdr2_be_md5_path_also_order_independent() {
418 let m_1 = (1u32, b"alpha-content-1".to_vec());
420 let m_2 = (2u32, b"beta-content-22".to_vec());
421 let m_3 = (3u32, b"gamma-content-333".to_vec());
422 let m_4 = (4u32, b"delta-content-4444".to_vec());
423
424 let order_a = vec![m_1.clone(), m_2.clone(), m_3.clone(), m_4.clone()];
425 let order_b = vec![m_4.clone(), m_3.clone(), m_2.clone(), m_1.clone()];
426 let h_a = keyhash_cdr2_be(&order_a, 1024);
427 let h_b = keyhash_cdr2_be(&order_b, 1024);
428 assert_eq!(h_a, h_b);
429 assert_ne!(h_a, [0u8; 16]);
430 }
431
432 #[test]
433 fn keyhash_cdr2_be_single_member_matches_compute_key_hash() {
434 let m = (42u32, vec![0xAB, 0xCD, 0xEF, 0x12]);
435 let h = keyhash_cdr2_be(&[m.clone()], 4);
436 let expected = compute_key_hash(&m.1, 4);
437 assert_eq!(h, expected);
438 }
439
440 #[test]
441 fn keyhash_cdr2_be_empty_member_set_yields_zero_padding() {
442 let h = keyhash_cdr2_be(&[], 4);
443 assert_eq!(h, [0u8; 16]);
444 }
445
446 #[test]
447 fn keyhash_cdr2_be_member_id_zero_sorts_first() {
448 let m_0 = (0u32, vec![0xFFu8, 0xEE, 0xDD, 0xCC]);
451 let m_99 = (99u32, vec![0x00u8, 0x11, 0x22, 0x33]);
452
453 let h_natural = keyhash_cdr2_be(&[m_0.clone(), m_99.clone()], 8);
454 let h_swapped = keyhash_cdr2_be(&[m_99.clone(), m_0.clone()], 8);
455 assert_eq!(h_natural, h_swapped);
456 let mut expected = [0u8; 16];
458 expected[..4].copy_from_slice(&m_0.1);
459 expected[4..8].copy_from_slice(&m_99.1);
460 assert_eq!(h_natural, expected);
461 }
462
463 #[test]
467 fn keyholder_is_empty_initially_and_after_write_not_empty() {
468 let mut h = PlainCdr2BeKeyHolder::new();
469 assert!(h.is_empty());
470 assert_eq!(h.len(), 0);
471 h.write_u8(0x01);
472 assert!(!h.is_empty());
473 assert_eq!(h.len(), 1);
474 }
475
476 #[test]
477 fn keyholder_as_bytes_returns_written_content() {
478 let mut h = PlainCdr2BeKeyHolder::new();
479 h.write_u8(0xDE);
480 h.write_u8(0xAD);
481 h.write_u8(0xBE);
482 h.write_u8(0xEF);
483 assert_eq!(h.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
484 assert_eq!(h.as_bytes(), &[0xDE, 0xAD, 0xBE, 0xEF]);
486 }
487
488 #[test]
489 fn keyholder_write_i8_negative_two_complement() {
490 let mut h = PlainCdr2BeKeyHolder::new();
491 h.write_i8(-1);
492 h.write_i8(-128);
493 h.write_i8(127);
494 assert_eq!(h.into_bytes(), vec![0xFF, 0x80, 0x7F]);
495 }
496
497 #[test]
498 fn keyholder_write_u16_be_with_alignment() {
499 let mut h = PlainCdr2BeKeyHolder::new();
500 h.write_u8(0xAA); h.write_u16(0x1234); assert_eq!(h.into_bytes(), vec![0xAA, 0x00, 0x12, 0x34]);
503 }
504
505 #[test]
506 fn keyholder_write_i16_be_with_alignment() {
507 let mut h = PlainCdr2BeKeyHolder::new();
508 h.write_u8(0xAA);
509 h.write_i16(-1);
510 assert_eq!(h.into_bytes(), vec![0xAA, 0x00, 0xFF, 0xFF]);
512 }
513
514 #[test]
515 fn keyholder_write_i32_be_with_alignment() {
516 let mut h = PlainCdr2BeKeyHolder::new();
517 h.write_u8(0xAA);
518 h.write_i32(-1);
519 assert_eq!(h.into_bytes(), vec![0xAA, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF]);
521 }
522
523 #[test]
524 fn keyholder_write_i64_be_with_4byte_alignment() {
525 let mut h = PlainCdr2BeKeyHolder::new();
527 h.write_u8(0xAA);
528 h.write_i64(-1);
529 let mut expected = vec![0xAA, 0, 0, 0];
531 expected.extend_from_slice(&[0xFF; 8]);
532 assert_eq!(h.into_bytes(), expected);
533 }
534
535 #[test]
536 fn keyholder_write_f32_be_known_pattern() {
537 let mut h = PlainCdr2BeKeyHolder::new();
538 h.write_f32(1.0_f32);
539 assert_eq!(h.into_bytes(), vec![0x3F, 0x80, 0x00, 0x00]);
541 }
542
543 #[test]
544 fn keyholder_write_f64_be_known_pattern() {
545 let mut h = PlainCdr2BeKeyHolder::new();
546 h.write_f64(1.0_f64);
547 assert_eq!(
549 h.into_bytes(),
550 vec![0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
551 );
552 }
553
554 #[test]
555 fn keyholder_write_bytes_appends_raw() {
556 let mut h = PlainCdr2BeKeyHolder::new();
557 h.write_u8(0x01);
558 h.write_bytes(&[0xDE, 0xAD, 0xBE, 0xEF]);
559 h.write_bytes(&[]);
560 h.write_bytes(&[0xFF]);
561 assert_eq!(h.into_bytes(), vec![0x01, 0xDE, 0xAD, 0xBE, 0xEF, 0xFF]);
562 }
563}