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    /// Deserializes 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 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    /// Parses a `TZif` byte slice into a validated file.
46    ///
47    /// # Errors
48    ///
49    /// Returns an error if the input cannot be deserialized as valid `TZif`.
50    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}