hff_core/
ecc.rs

1use crate::Endian;
2use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
3use std::{
4    fmt::Debug,
5    io::{Error, Read, Write},
6    ops::{Deref, DerefMut},
7};
8
9/// 8 character code.
10#[repr(C)]
11#[derive(Copy, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
12pub struct Ecc(
13    /// The code for the identifier.
14    u64,
15);
16
17impl Debug for Ecc {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        write!(f, "Ecc(\"{}\")", self.to_string())
20    }
21}
22
23impl Default for Ecc {
24    fn default() -> Self {
25        Self::INVALID
26    }
27}
28
29impl Ecc {
30    /// Constant representing the size of the structure.
31    pub const SIZE: usize = std::mem::size_of::<Self>();
32    /// Constant representing an invalid Ecc.
33    pub const INVALID: Ecc = Self(0);
34    /// The file header magic value.
35    pub const HFF_MAGIC: Ecc = Self::new("HFF-2023");
36
37    /// Create a new identifier.
38    /// Panics if the provided string is empty or if the number of
39    /// 'bytes' which represent the string won't fit into a u64.
40    /// i.e. the utf8 representation must be less than 9 bytes.
41    pub const fn new(name: &str) -> Self {
42        let bytes = name.as_bytes();
43        let count = bytes.len();
44
45        if count == 0 || count > 8 {
46            panic!("Invalid ECC string.");
47        }
48
49        // This is rather cheesy but it is desirable for 'new' to
50        // be const and this seemed like the way to make that happen.
51        union BytesU64 {
52            value: u64,
53            bytes: [u8; 8],
54        }
55
56        let mut converter = BytesU64 { value: 0 };
57        unsafe {
58            converter.bytes[0] = bytes[0];
59            converter.bytes[1] = if count > 1 { bytes[1] } else { 0 };
60            converter.bytes[2] = if count > 2 { bytes[2] } else { 0 };
61            converter.bytes[3] = if count > 3 { bytes[3] } else { 0 };
62            converter.bytes[4] = if count > 4 { bytes[4] } else { 0 };
63            converter.bytes[5] = if count > 5 { bytes[5] } else { 0 };
64            converter.bytes[6] = if count > 6 { bytes[6] } else { 0 };
65            converter.bytes[7] = if count > 7 { bytes[7] } else { 0 };
66        }
67
68        Self(unsafe { converter.value })
69    }
70
71    /// Check if the Ecc is valid or not.
72    pub fn is_valid(&self) -> bool {
73        *self != Self::INVALID
74    }
75
76    /// Compare the Ecc's in two ways, native and opposing endians.
77    /// If equivalent, returns Some with the endianess otherwise None.
78    /// NOTE: Will panic if rhs is a symetric id where endian can not
79    /// be detected.
80    pub fn endian(&self, rhs: Self) -> Option<Endian> {
81        if rhs.swap_bytes() == rhs {
82            panic!("Does not work for symetric ID's.")
83        }
84        if self.0 == rhs.0 {
85            Some(crate::NATIVE_ENDIAN)
86        } else if self.0 == rhs.0.swap_bytes() {
87            Some(crate::OPPOSING_ENDIAN)
88        } else {
89            None
90        }
91    }
92
93    /// Swap the endianess of the code.
94    pub const fn swap_bytes(&self) -> Self {
95        Self(self.0.swap_bytes())
96    }
97
98    /// Get the Ecc as a slice.
99    pub const fn as_slice(&self) -> &[u8] {
100        unsafe { std::slice::from_raw_parts((&self.0 as *const u64) as *const u8, 8) }
101    }
102
103    /// Get the Ecc as a mutable slice.
104    pub fn as_slice_mut(&mut self) -> &mut [u8] {
105        unsafe { std::slice::from_raw_parts_mut((&mut self.0 as *mut u64) as *mut u8, 8) }
106    }
107
108    /// Read from a stream.
109    pub fn read<E: ByteOrder>(reader: &mut dyn Read) -> Result<Self, Error> {
110        Ok(Self(reader.read_u64::<E>()?))
111    }
112
113    /// Write to a stream.
114    pub fn write<E: ByteOrder>(self, writer: &mut dyn Write) -> Result<(), Error> {
115        writer.write_u64::<E>(self.0)?;
116        Ok(())
117    }
118}
119
120impl Deref for Ecc {
121    type Target = u64;
122
123    fn deref(&self) -> &Self::Target {
124        &self.0
125    }
126}
127
128impl DerefMut for Ecc {
129    fn deref_mut(&mut self) -> &mut Self::Target {
130        &mut self.0
131    }
132}
133
134impl TryFrom<String> for Ecc {
135    type Error = crate::Error;
136
137    fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
138        let bytes = value.as_bytes();
139        let count = bytes.len();
140
141        if count > 0 && count < 9 {
142            Ok(Ecc::new(value.as_str()))
143        } else {
144            Err(crate::Error::InvalidEcc(value))
145        }
146    }
147}
148
149impl From<&str> for Ecc {
150    fn from(value: &str) -> Self {
151        Ecc::new(value)
152    }
153}
154
155impl From<u64> for Ecc {
156    fn from(value: u64) -> Self {
157        Self(value)
158    }
159}
160
161impl From<[u8; 8]> for Ecc {
162    fn from(value: [u8; 8]) -> Self {
163        unsafe { Self(*(value.as_ptr() as *const u64)) }
164    }
165}
166
167impl ToString for Ecc {
168    fn to_string(&self) -> String {
169        let mut name = String::with_capacity(8);
170        let code = self.as_slice();
171        if self.is_valid() {
172            for i in 0..8 {
173                if code[i].is_ascii() {
174                    if code[i] == 0 {
175                        break;
176                    } else {
177                        name.push(code[i] as char);
178                    }
179                } else {
180                    name = format!("{}", self.0);
181                    break;
182                }
183            }
184        } else {
185            name = "INVALID".into();
186        }
187        name
188    }
189}
190
191#[cfg(test)]
192mod tests {
193    use super::*;
194
195    #[test]
196    fn test_layout() {
197        // Must be 8 bytes.
198        assert_eq!(std::mem::size_of::<Ecc>(), 8);
199    }
200
201    #[test]
202    fn test_construction() {
203        for i in 1..=8 {
204            // Create and check the returned string.
205            let id = (0..i).into_iter().map(|_| 'x').collect::<String>();
206            let code = Ecc::new(&id);
207            assert_eq!(code.to_string(), id);
208
209            // Make sure all the remaining bytes are 0's.
210            let bytes = code.as_slice();
211            for j in i..8 {
212                assert_eq!(bytes[j], 0);
213            }
214        }
215    }
216
217    #[test]
218    fn test_serialization_le() {
219        let mut buffer = vec![];
220        let ecc = Ecc::new("test");
221        assert!(ecc.write::<crate::LE>(&mut buffer).is_ok());
222
223        let result = Ecc::read::<crate::LE>(&mut buffer.as_slice()).unwrap();
224        assert_eq!(ecc, result);
225        assert_eq!(result.to_string(), "test");
226    }
227
228    #[test]
229    fn test_serialization_be() {
230        let mut buffer = vec![];
231        let ecc = Ecc::new("test");
232        assert!(ecc.write::<crate::BE>(&mut buffer).is_ok());
233
234        let result = Ecc::read::<crate::BE>(&mut buffer.as_slice()).unwrap();
235        assert_eq!(ecc, result);
236        assert_eq!(result.to_string(), "test");
237    }
238
239    #[test]
240    fn test_endianness() {
241        assert_eq!(
242            Ecc::HFF_MAGIC.endian(Ecc::HFF_MAGIC),
243            Some(crate::NATIVE_ENDIAN)
244        );
245        assert_eq!(
246            Ecc::from(Ecc::HFF_MAGIC.0.swap_bytes()).endian(Ecc::HFF_MAGIC),
247            Some(crate::OPPOSING_ENDIAN)
248        );
249    }
250}