6bd25ab95ac0408793983a34ed18ca8ad68f0db6
[pspp] / rust / src / endian.rs
1 #[derive(Copy, Clone, Debug)]
2 /// The endianness for integer and floating-point numbers in SPSS system files.
3 ///
4 /// SPSS system files can declare IBM 370 and DEC VAX floating-point
5 /// representations, but no file that uses either of these has ever been found
6 /// in the wild, so this code does not handle them.
7 pub enum Endian {
8     /// Big-endian: MSB at lowest address.
9     Big,
10
11     /// Little-endian: LSB at lowest address.
12     Little,
13 }
14
15 impl Endian {
16     pub fn identify_u32(expected_value: u32, bytes: [u8; 4]) -> Option<Self> {
17         let as_big: u32 = Endian::Big.parse(bytes);
18         let as_little: u32 = Endian::Little.parse(bytes);
19         match (as_big == expected_value, as_little == expected_value) {
20             (true, false) => Some(Endian::Big),
21             (false, true) => Some(Endian::Little),
22             _ => None
23         }
24     }
25
26     pub fn identify_f64(expected_value: f64, bytes: [u8; 8]) -> Option<Self> {
27         let as_big: f64 = Endian::Big.parse(bytes);
28         let as_little: f64 = Endian::Little.parse(bytes);
29         match (as_big == expected_value, as_little == expected_value) {
30             (true, false) => Some(Endian::Big),
31             (false, true) => Some(Endian::Little),
32             _ => None
33         }
34     }
35 }
36
37 pub trait ToBytes<T, const N: usize> {
38     fn to_bytes(self, value: T) -> [u8; N];
39 }
40 impl ToBytes<f64, 8> for Endian {
41     fn to_bytes(self, value: f64) -> [u8; 8] {
42         match self {
43             Endian::Big => f64::to_be_bytes(value),
44             Endian::Little => f64::to_le_bytes(value),
45         }
46     }
47 }
48
49 /// Parses an `N`-byte slice in one of the supported formats into native format
50 /// as type `T`.
51 pub trait Parse<T, const N: usize> {
52     /// Given 'bytes', returns `T`.
53     fn parse(self, bytes: [u8; N]) -> T;
54 }
55 impl Parse<u64, 8> for Endian {
56     fn parse(self, bytes: [u8; 8]) -> u64 {
57         match self {
58             Endian::Big => u64::from_be_bytes(bytes),
59             Endian::Little => u64::from_le_bytes(bytes),
60         }
61     }
62 }
63 impl Parse<u32, 4> for Endian {
64     fn parse(self, bytes: [u8; 4]) -> u32 {
65         match self {
66             Endian::Big => u32::from_be_bytes(bytes),
67             Endian::Little => u32::from_le_bytes(bytes),
68         }
69     }
70 }
71 impl Parse<u16, 2> for Endian {
72     fn parse(self, bytes: [u8; 2]) -> u16 {
73         match self {
74             Endian::Big => u16::from_be_bytes(bytes),
75             Endian::Little => u16::from_le_bytes(bytes),
76         }
77     }
78 }
79 impl Parse<u8, 1> for Endian {
80     fn parse(self, bytes: [u8; 1]) -> u8 {
81         match self {
82             Endian::Big => u8::from_be_bytes(bytes),
83             Endian::Little => u8::from_le_bytes(bytes),
84         }
85     }
86 }
87 impl Parse<i64, 8> for Endian {
88     fn parse(self, bytes: [u8; 8]) -> i64 {
89         match self {
90             Endian::Big => i64::from_be_bytes(bytes),
91             Endian::Little => i64::from_le_bytes(bytes),
92         }
93     }
94 }
95 impl Parse<i32, 4> for Endian {
96     fn parse(self, bytes: [u8; 4]) -> i32 {
97         match self {
98             Endian::Big => i32::from_be_bytes(bytes),
99             Endian::Little => i32::from_le_bytes(bytes),
100         }
101     }
102 }
103 impl Parse<i16, 2> for Endian {
104     fn parse(self, bytes: [u8; 2]) -> i16 {
105         match self {
106             Endian::Big => i16::from_be_bytes(bytes),
107             Endian::Little => i16::from_le_bytes(bytes),
108         }
109     }
110 }
111 impl Parse<i8, 1> for Endian {
112     fn parse(self, bytes: [u8; 1]) -> i8 {
113         match self {
114             Endian::Big => i8::from_be_bytes(bytes),
115             Endian::Little => i8::from_le_bytes(bytes),
116         }
117     }
118 }
119 impl Parse<f64, 8> for Endian {
120     fn parse(self, bytes: [u8; 8]) -> f64 {
121         match self {
122             Endian::Big => f64::from_be_bytes(bytes),
123             Endian::Little => f64::from_le_bytes(bytes),
124         }
125     }
126 }
127