1use crate::{Error, Result};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum DataType {
28 Boolean = 0x01,
30 Integer8 = 0x02,
32 Integer16 = 0x03,
34 Integer32 = 0x04,
36 Unsigned8 = 0x05,
38 Unsigned16 = 0x06,
40 Unsigned32 = 0x07,
42 Real32 = 0x08,
44 Real64 = 0x11,
46 Integer64 = 0x15,
48 Unsigned64 = 0x1B,
50}
51
52impl DataType {
53 pub const fn index(self) -> u16 {
55 self as u16
56 }
57
58 pub const fn from_index(index: u16) -> Option<Self> {
62 Some(match index {
63 0x01 => DataType::Boolean,
64 0x02 => DataType::Integer8,
65 0x03 => DataType::Integer16,
66 0x04 => DataType::Integer32,
67 0x05 => DataType::Unsigned8,
68 0x06 => DataType::Unsigned16,
69 0x07 => DataType::Unsigned32,
70 0x08 => DataType::Real32,
71 0x11 => DataType::Real64,
72 0x15 => DataType::Integer64,
73 0x1B => DataType::Unsigned64,
74 _ => return None,
75 })
76 }
77
78 pub const fn size(self) -> usize {
80 match self {
81 DataType::Boolean | DataType::Integer8 | DataType::Unsigned8 => 1,
82 DataType::Integer16 | DataType::Unsigned16 => 2,
83 DataType::Integer32 | DataType::Unsigned32 | DataType::Real32 => 4,
84 DataType::Integer64 | DataType::Unsigned64 | DataType::Real64 => 8,
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq)]
91#[non_exhaustive]
92pub enum Value {
93 Boolean(bool),
95 Integer8(i8),
97 Integer16(i16),
99 Integer32(i32),
101 Unsigned8(u8),
103 Unsigned16(u16),
105 Unsigned32(u32),
107 Real32(f32),
109 Real64(f64),
111 Integer64(i64),
113 Unsigned64(u64),
115}
116
117impl Value {
118 pub const fn data_type(&self) -> DataType {
120 match self {
121 Value::Boolean(_) => DataType::Boolean,
122 Value::Integer8(_) => DataType::Integer8,
123 Value::Integer16(_) => DataType::Integer16,
124 Value::Integer32(_) => DataType::Integer32,
125 Value::Unsigned8(_) => DataType::Unsigned8,
126 Value::Unsigned16(_) => DataType::Unsigned16,
127 Value::Unsigned32(_) => DataType::Unsigned32,
128 Value::Real32(_) => DataType::Real32,
129 Value::Real64(_) => DataType::Real64,
130 Value::Integer64(_) => DataType::Integer64,
131 Value::Unsigned64(_) => DataType::Unsigned64,
132 }
133 }
134
135 pub const fn size(&self) -> usize {
137 self.data_type().size()
138 }
139
140 pub fn encode_le(&self, buf: &mut [u8]) -> Result<usize> {
145 let n = self.size();
146 if buf.len() < n {
147 return Err(Error::BadLength);
148 }
149 match self {
150 Value::Boolean(v) => buf[0] = *v as u8,
151 Value::Integer8(v) => buf[0] = *v as u8,
152 Value::Unsigned8(v) => buf[0] = *v,
153 Value::Integer16(v) => buf[..2].copy_from_slice(&v.to_le_bytes()),
154 Value::Unsigned16(v) => buf[..2].copy_from_slice(&v.to_le_bytes()),
155 Value::Integer32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
156 Value::Unsigned32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
157 Value::Real32(v) => buf[..4].copy_from_slice(&v.to_le_bytes()),
158 Value::Integer64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
159 Value::Unsigned64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
160 Value::Real64(v) => buf[..8].copy_from_slice(&v.to_le_bytes()),
161 }
162 Ok(n)
163 }
164
165 pub fn decode_le(data_type: DataType, bytes: &[u8]) -> Result<Value> {
170 if bytes.len() != data_type.size() {
171 return Err(Error::BadLength);
172 }
173 let v = match data_type {
174 DataType::Boolean => Value::Boolean(bytes[0] != 0),
175 DataType::Integer8 => Value::Integer8(bytes[0] as i8),
176 DataType::Unsigned8 => Value::Unsigned8(bytes[0]),
177 DataType::Integer16 => Value::Integer16(i16::from_le_bytes([bytes[0], bytes[1]])),
178 DataType::Unsigned16 => Value::Unsigned16(u16::from_le_bytes([bytes[0], bytes[1]])),
179 DataType::Integer32 => {
180 Value::Integer32(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
181 }
182 DataType::Unsigned32 => {
183 Value::Unsigned32(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
184 }
185 DataType::Real32 => {
186 Value::Real32(f32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
187 }
188 DataType::Integer64 => Value::Integer64(i64::from_le_bytes([
189 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
190 ])),
191 DataType::Unsigned64 => Value::Unsigned64(u64::from_le_bytes([
192 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
193 ])),
194 DataType::Real64 => Value::Real64(f64::from_le_bytes([
195 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
196 ])),
197 };
198 Ok(v)
199 }
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn sizes_match_spec() {
208 assert_eq!(DataType::Boolean.size(), 1);
209 assert_eq!(DataType::Unsigned16.size(), 2);
210 assert_eq!(DataType::Unsigned32.size(), 4);
211 assert_eq!(DataType::Real64.size(), 8);
212 }
213
214 #[test]
215 fn data_type_index_roundtrips() {
216 for dt in [
217 DataType::Boolean,
218 DataType::Unsigned32,
219 DataType::Real64,
220 DataType::Integer64,
221 DataType::Unsigned64,
222 ] {
223 assert_eq!(DataType::from_index(dt.index()), Some(dt));
224 }
225 assert_eq!(DataType::Unsigned32.index(), 0x0007);
226 assert_eq!(DataType::from_index(0x09), None);
228 }
229
230 #[test]
231 fn u32_is_little_endian() {
232 let mut buf = [0u8; 8];
233 let n = Value::Unsigned32(0x1234_5678).encode_le(&mut buf).unwrap();
234 assert_eq!(n, 4);
235 assert_eq!(&buf[..4], &[0x78, 0x56, 0x34, 0x12]);
236 }
237
238 #[test]
239 fn decode_roundtrips() {
240 let cases = [
241 Value::Boolean(true),
242 Value::Integer8(-5),
243 Value::Unsigned16(0xBEEF),
244 Value::Integer32(-123456),
245 Value::Unsigned32(0xDEAD_BEEF),
246 Value::Unsigned64(0x0102_0304_0506_0708),
247 ];
248 let mut buf = [0u8; 8];
249 for v in cases {
250 let n = v.encode_le(&mut buf).unwrap();
251 let back = Value::decode_le(v.data_type(), &buf[..n]).unwrap();
252 assert_eq!(v, back);
253 }
254 }
255
256 #[test]
257 fn encode_rejects_short_buffer() {
258 let mut buf = [0u8; 2];
259 assert_eq!(
260 Value::Unsigned32(0).encode_le(&mut buf),
261 Err(Error::BadLength)
262 );
263 }
264
265 #[test]
266 fn decode_rejects_wrong_length() {
267 assert_eq!(
268 Value::decode_le(DataType::Unsigned32, &[0, 0]),
269 Err(Error::BadLength)
270 );
271 }
272}