1use crate::{
2 common::{Header, TimeSize, HEADER_RESERVED_LEN, TZIF_MAGIC},
3 validate::validate_file,
4 DataBlock, LeapSecond, LocalTimeType, TzifError, TzifFile, Version,
5};
6
7impl TzifFile {
8 pub fn deserialize(input: &[u8]) -> Result<Self, TzifError> {
15 let mut reader = Reader::new(input);
16 let first = reader.read_header()?;
17 let v1 = reader.read_data_block(&first, TimeSize::ThirtyTwo)?;
18 if first.version == Version::V1 {
19 reader.expect_eof()?;
20 let file = Self::v1(v1);
21 validate_file(&file)?;
22 return Ok(file);
23 }
24
25 let second = reader.read_header()?;
26 if second.version != first.version {
27 return Err(TzifError::VersionMismatch {
28 first: first.version,
29 second: second.version,
30 });
31 }
32 let v2_plus = reader.read_data_block(&second, TimeSize::SixtyFour)?;
33 let footer = reader.read_footer()?;
34 reader.expect_eof()?;
35 let file = Self {
36 version: first.version,
37 v1,
38 v2_plus: Some(v2_plus),
39 footer: Some(footer),
40 };
41 validate_file(&file)?;
42 Ok(file)
43 }
44
45 pub fn parse(input: &[u8]) -> Result<Self, TzifError> {
51 Self::deserialize(input)
52 }
53}
54
55struct Reader<'a> {
56 input: &'a [u8],
57 offset: usize,
58}
59
60impl<'a> Reader<'a> {
61 const fn new(input: &'a [u8]) -> Self {
62 Self { input, offset: 0 }
63 }
64
65 fn read_header(&mut self) -> Result<Header, TzifError> {
66 let header_offset = self.offset;
67 let magic = self.read_exact(4, "magic")?;
68 if magic != TZIF_MAGIC {
69 return Err(TzifError::InvalidMagic {
70 offset: header_offset,
71 });
72 }
73 let version = Version::from_byte(self.read_u8("version")?)?;
74 self.read_exact(HEADER_RESERVED_LEN, "reserved header bytes")?;
75 Ok(Header {
76 version,
77 isutcnt: self.read_count("isutcnt")?,
78 isstdcnt: self.read_count("isstdcnt")?,
79 leapcnt: self.read_count("leapcnt")?,
80 timecnt: self.read_count("timecnt")?,
81 typecnt: self.read_count("typecnt")?,
82 charcnt: self.read_count("charcnt")?,
83 })
84 }
85
86 fn read_data_block(
87 &mut self,
88 header: &Header,
89 time_size: TimeSize,
90 ) -> Result<DataBlock, TzifError> {
91 self.ensure_remaining(header.data_block_len(time_size)?, "data block")?;
92
93 let mut transition_times = Vec::with_capacity(header.timecnt);
94 for _ in 0..header.timecnt {
95 transition_times.push(self.read_time(time_size, "transition time")?);
96 }
97
98 let transition_types = self
99 .read_exact(header.timecnt, "transition types")?
100 .to_vec();
101
102 let mut local_time_types = Vec::with_capacity(header.typecnt);
103 for index in 0..header.typecnt {
104 let utc_offset = self.read_i32("local time type UTC offset")?;
105 let is_dst = match self.read_u8("local time type DST indicator")? {
106 0 => false,
107 1 => true,
108 value => return Err(TzifError::InvalidDstIndicator { index, value }),
109 };
110 let designation_index = self.read_u8("local time type designation index")?;
111 local_time_types.push(LocalTimeType {
112 utc_offset,
113 is_dst,
114 designation_index,
115 });
116 }
117
118 let designations = self.read_exact(header.charcnt, "designations")?.to_vec();
119
120 let mut leap_seconds = Vec::with_capacity(header.leapcnt);
121 for _ in 0..header.leapcnt {
122 leap_seconds.push(LeapSecond {
123 occurrence: self.read_time(time_size, "leap-second occurrence")?,
124 correction: self.read_i32("leap-second correction")?,
125 });
126 }
127
128 let standard_wall_indicators =
129 self.read_bool_indicators("standard_wall_indicators", header.isstdcnt)?;
130 let ut_local_indicators =
131 self.read_bool_indicators("ut_local_indicators", header.isutcnt)?;
132
133 Ok(DataBlock {
134 transition_times,
135 transition_types,
136 local_time_types,
137 designations,
138 leap_seconds,
139 standard_wall_indicators,
140 ut_local_indicators,
141 })
142 }
143
144 fn read_footer(&mut self) -> Result<String, TzifError> {
145 let start = self.offset;
146 if self.read_u8("footer start newline")? != b'\n' {
147 return Err(TzifError::MissingFooterStart { offset: start });
148 }
149 let footer_start = self.offset;
150 let footer_bytes = self
151 .input
152 .get(footer_start..)
153 .ok_or(TzifError::UnexpectedEof {
154 offset: footer_start,
155 context: "footer",
156 })?;
157 let footer_len = footer_bytes.iter().position(|&byte| byte == b'\n').ok_or(
158 TzifError::MissingFooterEnd {
159 offset: footer_start,
160 },
161 )?;
162 let footer = std::str::from_utf8(
163 self.input
164 .get(footer_start..footer_start + footer_len)
165 .ok_or(TzifError::UnexpectedEof {
166 offset: footer_start,
167 context: "footer",
168 })?,
169 )
170 .map_err(|_| TzifError::InvalidFooterUtf8)?
171 .to_string();
172 self.offset = footer_start + footer_len + 1;
173 Ok(footer)
174 }
175
176 const fn expect_eof(&self) -> Result<(), TzifError> {
177 if self.offset == self.input.len() {
178 Ok(())
179 } else {
180 Err(TzifError::TrailingData {
181 offset: self.offset,
182 })
183 }
184 }
185
186 fn read_bool_indicators(
187 &mut self,
188 field: &'static str,
189 count: usize,
190 ) -> Result<Vec<bool>, TzifError> {
191 let mut values = Vec::with_capacity(count);
192 for index in 0..count {
193 values.push(match self.read_u8(field)? {
194 0 => false,
195 1 => true,
196 value => {
197 return Err(TzifError::InvalidBooleanIndicator {
198 field,
199 index,
200 value,
201 })
202 }
203 });
204 }
205 Ok(values)
206 }
207
208 fn read_time(&mut self, time_size: TimeSize, context: &'static str) -> Result<i64, TzifError> {
209 match time_size {
210 TimeSize::ThirtyTwo => Ok(i64::from(self.read_i32(context)?)),
211 TimeSize::SixtyFour => Ok(i64::from_be_bytes(self.read_array(context)?)),
212 }
213 }
214
215 fn read_count(&mut self, field: &'static str) -> Result<usize, TzifError> {
216 let count = self.read_u32(field)?;
217 usize::try_from(count).map_err(|_| TzifError::CountTooLarge { field, count })
218 }
219
220 fn read_i32(&mut self, context: &'static str) -> Result<i32, TzifError> {
221 Ok(i32::from_be_bytes(self.read_array(context)?))
222 }
223
224 fn read_u32(&mut self, context: &'static str) -> Result<u32, TzifError> {
225 Ok(u32::from_be_bytes(self.read_array(context)?))
226 }
227
228 fn read_u8(&mut self, context: &'static str) -> Result<u8, TzifError> {
229 let [byte] = self.read_array(context)?;
230 Ok(byte)
231 }
232
233 fn read_array<const N: usize>(&mut self, context: &'static str) -> Result<[u8; N], TzifError> {
234 let mut bytes = [0; N];
235 bytes.copy_from_slice(self.read_exact(N, context)?);
236 Ok(bytes)
237 }
238
239 fn read_exact(&mut self, len: usize, context: &'static str) -> Result<&'a [u8], TzifError> {
240 let end = self
241 .offset
242 .checked_add(len)
243 .ok_or(TzifError::UnexpectedEof {
244 offset: self.offset,
245 context,
246 })?;
247 let bytes = self
248 .input
249 .get(self.offset..end)
250 .ok_or(TzifError::UnexpectedEof {
251 offset: self.offset,
252 context,
253 })?;
254 self.offset = end;
255 Ok(bytes)
256 }
257
258 fn ensure_remaining(&self, len: usize, context: &'static str) -> Result<(), TzifError> {
259 let end = self
260 .offset
261 .checked_add(len)
262 .ok_or(TzifError::UnexpectedEof {
263 offset: self.offset,
264 context,
265 })?;
266 if end <= self.input.len() {
267 Ok(())
268 } else {
269 Err(TzifError::UnexpectedEof {
270 offset: self.offset,
271 context,
272 })
273 }
274 }
275}