1#[allow(clippy::module_name_repetitions)]
2use super::version::Version;
3use anyhow::{Result, anyhow};
4use core::str;
5use memmap3::Mmap;
7use serde::{Serialize, Serializer, ser::SerializeMap};
8use std::ops::RangeInclusive;
9
10#[derive(Clone, Debug, Hash)]
18pub struct Header {
19 pub version: Version,
20 pub text_offset: RangeInclusive<usize>,
21 pub data_offset: RangeInclusive<usize>,
22 pub analysis_offset: RangeInclusive<usize>,
23}
24impl Serialize for Header {
25 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26 where
27 S: Serializer,
28 {
29 let mut state = serializer.serialize_map(Some(2))?;
30 state.serialize_entry("version", &self.version)?;
31 state.serialize_entry("text_offset", &self.text_offset)?;
32 state.serialize_entry("data_offset", &self.data_offset)?;
33 state.serialize_entry("analysis_offset", &self.analysis_offset)?;
34 state.end()
35 }
36}
37
38impl Header {
39 #[must_use]
40 pub const fn new() -> Self {
41 Self {
42 version: Version::V3_1,
43 text_offset: 0..=0,
44 data_offset: 0..=0,
45 analysis_offset: 0..=0,
46 }
47 }
48 pub fn from_mmap(mmap: &Mmap) -> Result<Self> {
55 Self::check_header_spaces(&mmap[6..=9])?;
57 Ok(Self {
61 version: Self::get_version(mmap)?,
62 text_offset: Self::get_text_offsets(mmap)?,
63 data_offset: Self::get_data_offsets(mmap)?,
64 analysis_offset: Self::get_analysis_offsets(mmap)?,
65 })
66 }
67
68 pub fn get_version(mmap: &Mmap) -> Result<Version> {
72 let version = String::from_utf8(mmap[..6].to_vec())?;
73 Self::check_fcs_version(&version)
74 }
75
76 pub fn check_fcs_version(version: &str) -> Result<Version> {
80 match version {
81 "FCS1.0" => Ok(Version::V1_0),
82 "FCS2.0" => Ok(Version::V2_0),
83 "FCS3.0" => Ok(Version::V3_0),
84 "FCS3.1" => Ok(Version::V3_1),
85 "FCS3.2" => Ok(Version::V3_2),
86 "FCS4.0" => Ok(Version::V4_0),
87 _ => Err(anyhow!("Invalid FCS version: {}", version)),
88 }
89 }
90 pub fn check_header_spaces(buffer: &[u8]) -> Result<()> {
94 if bytecount::count(buffer, b' ') != 4 {
95 return Err(anyhow!(
96 "Invalid number of spaces in header segment. File may be corrupted."
97 ));
98 }
99 Ok(())
100 }
101 fn get_offset_from_header(mmap: &Mmap, start: usize, end: usize) -> Result<usize> {
103 let offset_char = mmap[start..=end].as_ascii().expect("ascii not found");
104 Ok(offset_char.as_str().trim_ascii().parse::<usize>()?)
110 }
111 fn get_text_offset_start(mmap: &Mmap) -> Result<usize> {
113 Self::get_offset_from_header(mmap, 10, 17)
114 }
115 fn get_text_offset_end(mmap: &Mmap) -> Result<usize> {
117 Self::get_offset_from_header(mmap, 18, 25)
118 }
119 fn get_data_offset_start(mmap: &Mmap) -> Result<usize> {
121 Self::get_offset_from_header(mmap, 26, 33)
122 }
123 fn get_data_offset_end(mmap: &Mmap) -> Result<usize> {
125 Self::get_offset_from_header(mmap, 34, 41)
126 }
127 fn get_analysis_offset_start(mmap: &Mmap) -> Result<usize> {
129 Self::get_offset_from_header(mmap, 42, 49)
130 }
131 fn get_analysis_offset_end(mmap: &Mmap) -> Result<usize> {
133 Self::get_offset_from_header(mmap, 50, 57)
134 }
135 fn get_text_offsets(mmap: &Mmap) -> Result<RangeInclusive<usize>> {
137 let text_offset_start = Self::get_text_offset_start(mmap)?;
138 let text_offset_end = Self::get_text_offset_end(mmap)?;
139 Ok(text_offset_start..=text_offset_end)
140 }
141 fn get_data_offsets(mmap: &Mmap) -> Result<RangeInclusive<usize>> {
143 let data_offset_start = Self::get_data_offset_start(mmap)?;
144 let data_offset_end = Self::get_data_offset_end(mmap)?;
145 Ok(data_offset_start..=data_offset_end)
146 }
147 fn get_analysis_offsets(mmap: &Mmap) -> Result<RangeInclusive<usize>> {
149 let analysis_offset_start = Self::get_analysis_offset_start(mmap)?;
150 let analysis_offset_end = Self::get_analysis_offset_end(mmap)?;
151 Ok(analysis_offset_start..=analysis_offset_end)
152 }
153 pub fn check_fcs_offsets(mmap: &Mmap) -> Result<()> {
165 println!("HEADER (first 58 bytes): {:?}", &mmap[0..58].as_ascii());
166 println!(
167 "TEXT segment start offset: {:?}",
168 Self::get_text_offset_start(mmap)?
169 );
170 println!(
171 "TEXT segment end offset: {:?}",
172 Self::get_text_offset_end(mmap)?
173 );
174 println!(
175 "DATA segment start offset: {:?}",
176 Self::get_data_offset_start(mmap)?
177 );
178 println!(
179 "DATA segment end offset: {:?}",
180 Self::get_data_offset_end(mmap)?
181 );
182 println!(
183 "ANALYSIS segment start offset (optional): {:?}",
184 Self::get_analysis_offset_start(mmap)
185 );
186 println!(
187 "ANALYSIS segment end offset (optional): {:?}",
188 Self::get_analysis_offset_end(mmap)
189 );
190 println!("header range of TEXT: {:?}", &mmap[4700..=5216].as_ascii());
192 Ok(())
193 }
194}
195impl Default for Header {
196 fn default() -> Self {
197 Self::new()
198 }
199}