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