1//! Helper functions.
23/// Read a buffer smaller than 8 bytes into an integer in little-endian.
4///
5/// This assumes that `buf.len() < 8`. If this is not satisfied, the behavior is unspecified.
6#[inline(always)]
7pub fn read_int(buf: &[u8]) -> u64 {
8// Because we want to make sure that it is register allocated, we fetch this into a variable.
9 // It will likely make no difference anyway, though.
10let ptr = buf.as_ptr();
1112unsafe {
13// Break it down to reads of integers with widths in total spanning the buffer. This minimizes
14 // the number of reads
15match buf.len() {
16// u8.
171 => *ptr as u64,
18// u16.
192 => (ptr as *const u16).read_unaligned().to_le() as u64,
20// u16 + u8.
213 => {
22let a = (ptr as *const u16).read_unaligned().to_le() as u64;
23let b = *ptr.offset(2) as u64;
2425 a | (b << 16)
26 }
27// u32.
284 => (ptr as *const u32).read_unaligned().to_le() as u64,
29// u32 + u8.
305 => {
31let a = (ptr as *const u32).read_unaligned().to_le() as u64;
32let b = *ptr.offset(4) as u64;
3334 a | (b << 32)
35 }
36// u32 + u16.
376 => {
38let a = (ptr as *const u32).read_unaligned().to_le() as u64;
39let b = (ptr.offset(4) as *const u16).read_unaligned().to_le() as u64;
4041 a | (b << 32)
42 }
43// u32 + u16 + u8.
447 => {
45let a = (ptr as *const u32).read_unaligned().to_le() as u64;
46let b = (ptr.offset(4) as *const u16).read_unaligned().to_le() as u64;
47let c = *ptr.offset(6) as u64;
4849 a | (b << 32) | (c << 48)
50 }
51_ => 0,
52 }
53 }
54}
5556/// Read a little-endian 64-bit integer from some buffer.
57#[inline(always)]
58pub unsafe fn read_u64(ptr: *const u8) -> u64 {
59#[cfg(target_pointer_width = "32")]
60{
61// We cannot be sure about the memory layout of a potentially emulated 64-bit integer, so
62 // we read it manually. If possible, the compiler should emit proper instructions.
63let a = (ptr as *const u32).read_unaligned().to_le();
64let b = (ptr.offset(4) as *const u32).read_unaligned().to_le();
6566 a as u64 | ((b as u64) << 32)
67 }
6869#[cfg(target_pointer_width = "64")]
70{
71 (ptr as *const u64).read_unaligned().to_le()
72 }
73}
7475/// The diffusion function.
76///
77/// This is a bijective function emitting chaotic behavior. Such functions are used as building
78/// blocks for hash functions.
79pub const fn diffuse(mut x: u64) -> u64 {
80// These are derived from the PCG RNG's round. Thanks to @Veedrac for proposing this. The basic
81 // idea is that we use dynamic shifts, which are determined by the input itself. The shift is
82 // chosen by the higher bits, which means that changing those flips the lower bits, which
83 // scatters upwards because of the multiplication.
8485x = x.wrapping_mul(0x6eed0e9da4d94a4f);
86let a = x >> 32;
87let b = x >> 60;
88 x ^= a >> b;
89 x = x.wrapping_mul(0x6eed0e9da4d94a4f);
9091 x
92}
9394/// Reverse the `diffuse` function.
95pub const fn undiffuse(mut x: u64) -> u64 {
96// 0x2f72b4215a3d8caf is the modular multiplicative inverse of the constant used in `diffuse`.
9798x = x.wrapping_mul(0x2f72b4215a3d8caf);
99let a = x >> 32;
100let b = x >> 60;
101 x ^= a >> b;
102 x = x.wrapping_mul(0x2f72b4215a3d8caf);
103104 x
105}
106107#[cfg(test)]
108mod tests {
109use super::*;
110111fn diffuse_test(x: u64, y: u64) {
112assert_eq!(diffuse(x), y);
113assert_eq!(x, undiffuse(y));
114assert_eq!(undiffuse(diffuse(x)), x);
115 }
116117#[test]
118fn read_int_() {
119assert_eq!(read_int(&[2, 3]), 770);
120assert_eq!(read_int(&[3, 2]), 515);
121assert_eq!(read_int(&[3, 2, 5]), 328195);
122 }
123124#[test]
125fn read_u64_() {
126unsafe {
127assert_eq!(read_u64([1, 0, 0, 0, 0, 0, 0, 0].as_ptr()), 1);
128assert_eq!(read_u64([2, 1, 0, 0, 0, 0, 0, 0].as_ptr()), 258);
129 }
130 }
131132#[test]
133fn diffuse_test_vectors() {
134 diffuse_test(94203824938, 17289265692384716055);
135 diffuse_test(0xDEADBEEF, 12110756357096144265);
136 diffuse_test(0, 0);
137 diffuse_test(1, 15197155197312260123);
138 diffuse_test(2, 1571904453004118546);
139 diffuse_test(3, 16467633989910088880);
140 }
141}