1use crate::{Ecc, Endian, Error, IdType, Result, Version, BE, LE, NATIVE_ENDIAN, NE};
3use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
4use std::io::Write;
5
6pub const FORMAT_VERSION: Version = Version::new(0, 3);
8
9#[repr(C)]
11#[derive(Debug, Clone, Eq, PartialEq)]
12pub struct Header {
13 magic: Ecc,
15 version: Version,
17 id_type: u32,
23 content_type: Ecc,
26 table_count: u32,
28 chunk_count: u32,
30}
31
32impl Header {
33 pub const SIZE: usize = std::mem::size_of::<Self>();
35
36 pub fn new(id_type: IdType, content_type: Ecc, table_count: u32, chunk_count: u32) -> Self {
38 Self {
39 magic: Ecc::HFF_MAGIC,
40 version: FORMAT_VERSION,
41 id_type: *id_type,
42 content_type,
43 table_count,
44 chunk_count,
45 }
46 }
47
48 pub fn with(
50 magic: Ecc,
51 version: Version,
52 id_type: u32,
53 content_type: Ecc,
54 table_count: u32,
55 chunk_count: u32,
56 ) -> Self {
57 Self {
58 magic,
59 version,
60 id_type,
61 content_type,
62 table_count,
63 chunk_count,
64 }
65 }
66
67 pub fn is_valid(&self) -> bool {
69 match self.magic.endian(Ecc::HFF_MAGIC) {
70 Some(endian) => {
71 if endian == NATIVE_ENDIAN {
72 self.version == FORMAT_VERSION
73 } else {
74 self.version.swap_bytes() == FORMAT_VERSION
75 }
76 }
77 None => false,
78 }
79 }
80
81 pub fn magic(&self) -> Ecc {
83 self.magic
84 }
85
86 pub fn version(&self) -> Version {
88 self.version
89 }
90
91 pub fn content_type(&self) -> Ecc {
93 self.content_type
94 }
95
96 pub fn id_type(&self) -> IdType {
98 self.id_type.into()
99 }
100
101 pub fn is_native_endian(&self) -> bool {
103 self.magic == Ecc::HFF_MAGIC
104 }
105
106 pub fn table_count(&self) -> u32 {
108 self.table_count
109 }
110
111 pub fn chunk_count(&self) -> u32 {
113 self.chunk_count
114 }
115
116 pub fn to_bytes<E: ByteOrder>(self) -> Result<Vec<u8>> {
118 let mut buffer = vec![];
119 let writer: &mut dyn Write = &mut buffer;
120 self.magic.write::<E>(writer)?;
121 self.version.write::<E>(writer)?;
122 writer.write_u32::<E>(self.id_type)?;
123 self.content_type.write::<E>(writer)?;
124 writer.write_u32::<E>(self.table_count)?;
125 writer.write_u32::<E>(self.chunk_count)?;
126 Ok(buffer)
127 }
128
129 #[cfg(test)]
133 pub fn swap_bytes(&self) -> Self {
134 Self {
135 magic: self.magic.swap_bytes(),
136 version: self.version.swap_bytes(),
137 id_type: self.id_type.swap_bytes(),
138 content_type: self.content_type.swap_bytes(),
139 table_count: self.table_count.swap_bytes(),
140 chunk_count: self.chunk_count.swap_bytes(),
141 }
142 }
143}
144
145impl TryFrom<&[u8]> for Header {
146 type Error = crate::Error;
147
148 fn try_from(mut value: &[u8]) -> std::prelude::v1::Result<Self, Self::Error> {
149 let reader: &mut dyn std::io::Read = &mut value;
150
151 let magic = Ecc::read::<NE>(reader)?;
153
154 match Ecc::HFF_MAGIC.endian(magic.clone()) {
158 Some(endian) => match endian {
159 Endian::Little => Ok(Header::with(
160 magic,
161 Version::read::<LE>(reader)?,
162 reader.read_u32::<LE>()?,
163 Ecc::read::<LE>(reader)?,
164 reader.read_u32::<LE>()?,
165 reader.read_u32::<LE>()?,
166 )),
167 Endian::Big => Ok(Header::with(
168 magic,
169 Version::read::<BE>(reader)?,
170 reader.read_u32::<BE>()?,
171 Ecc::read::<BE>(reader)?,
172 reader.read_u32::<BE>()?,
173 reader.read_u32::<BE>()?,
174 )),
175 },
176 None => Err(Error::Invalid("Not an HFF file.".into())),
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_struct_layout() {
187 assert_eq!(std::mem::size_of::<Header>(), 32);
188 }
189
190 #[test]
191 fn validation() {
192 assert!(Header::new(IdType::Ecc2, Ecc::new("test"), 0, 0).is_valid());
193 assert!(Header::new(IdType::Ecc2, Ecc::new("test"), 0, 0).is_native_endian());
194 assert!(Header::new(IdType::Ecc2, Ecc::new("test"), 0, 0)
195 .swap_bytes()
196 .is_valid());
197 assert!(!Header::new(IdType::Ecc2, Ecc::new("test"), 0, 0)
198 .swap_bytes()
199 .is_native_endian());
200 }
201
202 #[test]
203 fn serialization() {
204 {
205 let header = Header::new(IdType::Ecc2, "Test".into(), 1, 2);
207 let buffer = header.clone().to_bytes::<LE>().unwrap();
208 let dup: Header = buffer.as_slice().try_into().unwrap();
209
210 assert_eq!(dup.magic, Ecc::HFF_MAGIC);
211 assert_eq!(dup.version, FORMAT_VERSION);
212 assert_eq!(dup.content_type, Ecc::new("Test"));
213 assert_eq!(dup.table_count, 1);
214 assert_eq!(dup.chunk_count, 2);
215 }
216
217 {
218 let header = Header::new(IdType::Ecc2, "Test".into(), 1, 2);
220 let buffer = header.clone().to_bytes::<BE>().unwrap();
221 let dup: Header = buffer.as_slice().try_into().unwrap();
222
223 assert_eq!(dup.magic, Ecc::HFF_MAGIC.swap_bytes());
224 assert_eq!(dup.version, FORMAT_VERSION);
225 assert_eq!(dup.content_type, Ecc::new("Test"));
226 assert_eq!(dup.table_count, 1);
227 assert_eq!(dup.chunk_count, 2);
228 }
229 }
230}