work
[pspp] / rust / src / endian.rs
1 /// The endianness for integer and floating-point numbers in SPSS system files.
2 ///
3 /// SPSS system files can declare IBM 370 and DEC VAX floating-point
4 /// representations, but no file that uses either of these has ever been found
5 /// in the wild, so this code does not handle them.
6 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
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<i64, 8> for Endian {
41     fn to_bytes(self, value: i64) -> [u8; 8] {
42         match self {
43             Endian::Big => i64::to_be_bytes(value),
44             Endian::Little => i64::to_le_bytes(value),
45         }
46     }
47 }
48 impl ToBytes<u32, 4> for Endian {
49     fn to_bytes(self, value: u32) -> [u8; 4] {
50         match self {
51             Endian::Big => u32::to_be_bytes(value),
52             Endian::Little => u32::to_le_bytes(value),
53         }
54     }
55 }
56 impl ToBytes<i32, 4> for Endian {
57     fn to_bytes(self, value: i32) -> [u8; 4] {
58         match self {
59             Endian::Big => i32::to_be_bytes(value),
60             Endian::Little => i32::to_le_bytes(value),
61         }
62     }
63 }
64 impl ToBytes<u16, 2> for Endian {
65     fn to_bytes(self, value: u16) -> [u8; 2] {
66         match self {
67             Endian::Big => u16::to_be_bytes(value),
68             Endian::Little => u16::to_le_bytes(value),
69         }
70     }
71 }
72 impl ToBytes<u8, 1> for Endian {
73     fn to_bytes(self, value: u8) -> [u8; 1] {
74         [value]
75     }
76 }
77 impl ToBytes<f64, 8> for Endian {
78     fn to_bytes(self, value: f64) -> [u8; 8] {
79         match self {
80             Endian::Big => f64::to_be_bytes(value),
81             Endian::Little => f64::to_le_bytes(value),
82         }
83     }
84 }
85
86 /// Parses an `N`-byte slice in one of the supported formats into native format
87 /// as type `T`.
88 pub trait Parse<T, const N: usize> {
89     /// Given 'bytes', returns `T`.
90     fn parse(self, bytes: [u8; N]) -> T;
91 }
92 impl Parse<u64, 8> for Endian {
93     fn parse(self, bytes: [u8; 8]) -> u64 {
94         match self {
95             Endian::Big => u64::from_be_bytes(bytes),
96             Endian::Little => u64::from_le_bytes(bytes),
97         }
98     }
99 }
100 impl Parse<u32, 4> for Endian {
101     fn parse(self, bytes: [u8; 4]) -> u32 {
102         match self {
103             Endian::Big => u32::from_be_bytes(bytes),
104             Endian::Little => u32::from_le_bytes(bytes),
105         }
106     }
107 }
108 impl Parse<u16, 2> for Endian {
109     fn parse(self, bytes: [u8; 2]) -> u16 {
110         match self {
111             Endian::Big => u16::from_be_bytes(bytes),
112             Endian::Little => u16::from_le_bytes(bytes),
113         }
114     }
115 }
116 impl Parse<u8, 1> for Endian {
117     fn parse(self, bytes: [u8; 1]) -> u8 {
118         match self {
119             Endian::Big => u8::from_be_bytes(bytes),
120             Endian::Little => u8::from_le_bytes(bytes),
121         }
122     }
123 }
124 impl Parse<i64, 8> for Endian {
125     fn parse(self, bytes: [u8; 8]) -> i64 {
126         match self {
127             Endian::Big => i64::from_be_bytes(bytes),
128             Endian::Little => i64::from_le_bytes(bytes),
129         }
130     }
131 }
132 impl Parse<i32, 4> for Endian {
133     fn parse(self, bytes: [u8; 4]) -> i32 {
134         match self {
135             Endian::Big => i32::from_be_bytes(bytes),
136             Endian::Little => i32::from_le_bytes(bytes),
137         }
138     }
139 }
140 impl Parse<i16, 2> for Endian {
141     fn parse(self, bytes: [u8; 2]) -> i16 {
142         match self {
143             Endian::Big => i16::from_be_bytes(bytes),
144             Endian::Little => i16::from_le_bytes(bytes),
145         }
146     }
147 }
148 impl Parse<i8, 1> for Endian {
149     fn parse(self, bytes: [u8; 1]) -> i8 {
150         match self {
151             Endian::Big => i8::from_be_bytes(bytes),
152             Endian::Little => i8::from_le_bytes(bytes),
153         }
154     }
155 }
156 impl Parse<f64, 8> for Endian {
157     fn parse(self, bytes: [u8; 8]) -> f64 {
158         match self {
159             Endian::Big => f64::from_be_bytes(bytes),
160             Endian::Little => f64::from_le_bytes(bytes),
161         }
162     }
163 }
164