Skip to main content

tzif_codec/
parse.rs

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    /// Parses a `TZif` byte slice into a validated file.
9    ///
10    /// # Errors
11    ///
12    /// Returns an error if the input is malformed, truncated, has invalid counts or
13    /// indexes, or fails semantic validation.
14    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}