eazip/utils/
mod.rs

1pub mod cp437;
2mod crc32;
3
4pub use crc32::{Crc32Checker, Crc32Writer};
5
6use std::{fmt, io, time::SystemTime};
7
8/// The type of an entry in an archive.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum FileType {
11    /// A file.
12    File,
13    /// A directory.
14    Directory,
15    /// A symlink.
16    Symlink,
17}
18
19impl FileType {
20    /// Returns whether `self` is `FileType::File`.
21    #[inline]
22    pub fn is_file(&self) -> bool {
23        matches!(self, FileType::File)
24    }
25
26    /// Returns whether `self` is `FileType::Directory`.
27    #[inline]
28    pub fn is_directory(&self) -> bool {
29        matches!(self, FileType::Directory)
30    }
31
32    /// Returns whether `self` is `FileType::Symlink`.
33    #[inline]
34    pub fn is_symlink(&self) -> bool {
35        matches!(self, FileType::Symlink)
36    }
37}
38
39/// A timestamp for an entry in an archive.
40///
41/// It is stored as a 64-bits UNIX timestamp, and therefore has second precision.
42#[derive(Clone, Copy)]
43pub struct Timestamp(u64);
44
45impl Timestamp {
46    /// Returns the timestamp corresponding to "now".
47    #[inline]
48    pub fn now() -> Self {
49        Self::from_std(SystemTime::now())
50    }
51
52    /// Returns a `Timestamp` from a NTFS timestamp.
53    ///
54    /// Sub-second precision is lost in the process.
55    pub fn from_ntfs(time: u64) -> Self {
56        /// Time in seconds between NT and Unix epochs
57        const NT_EPOCH: u64 = 11_644_473_600;
58
59        let time = time.saturating_sub(NT_EPOCH * 10_000_000);
60
61        Self(time / 10_000_000)
62    }
63
64    /// Returns a `Timestamp` from an UNIX timestamp.
65    #[inline]
66    pub fn from_unix(time: u64) -> Self {
67        Self(time)
68    }
69
70    /// Returns a `Timestamp` from a [`SystemTime`].
71    ///
72    /// Sub-second precision is lost in the process.
73    #[inline]
74    pub fn from_std(t: SystemTime) -> Self {
75        Self(t.duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs())
76    }
77
78    /// Converts this timestamp to an UNIX timestamp.
79    #[inline]
80    pub fn to_unix(self) -> u64 {
81        self.0
82    }
83
84    /// Converts this timestamp to a [`SystemTime`].
85    #[inline]
86    pub fn to_std(self) -> SystemTime {
87        SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(self.0)
88    }
89}
90
91impl fmt::Debug for Timestamp {
92    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93        write!(f, "Timestamp({})", self.0)
94    }
95}
96
97#[derive(Default)]
98pub(crate) struct Counter<T> {
99    pub amt: u64,
100    pub inner: T,
101}
102
103impl<T> Counter<T> {
104    #[inline]
105    pub const fn new(inner: T) -> Self {
106        Self { amt: 0, inner }
107    }
108
109    pub(crate) fn advance(&mut self, amt: u64) -> io::Result<()>
110    where
111        T: io::Seek,
112    {
113        #[cold]
114        fn out_of_range() -> io::Error {
115            io::Error::new(io::ErrorKind::InvalidInput, "seek out of range")
116        }
117
118        let offset = amt.try_into().map_err(|_| out_of_range())?;
119        self.amt = self.amt.checked_add(amt).ok_or_else(out_of_range)?;
120        self.inner.seek_relative(offset)
121    }
122}
123
124impl<R: io::Read> io::Read for Counter<R> {
125    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
126        let n = self.inner.read(buf)?;
127        self.amt += n as u64;
128        Ok(n)
129    }
130}
131
132impl<R: io::BufRead> io::BufRead for Counter<R> {
133    #[inline]
134    fn fill_buf(&mut self) -> io::Result<&[u8]> {
135        self.inner.fill_buf()
136    }
137
138    #[inline]
139    fn consume(&mut self, amount: usize) {
140        self.amt += amount as u64;
141        self.inner.consume(amount);
142    }
143}
144
145impl<W: io::Write> io::Write for Counter<W> {
146    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
147        let n = self.inner.write(buf)?;
148        self.amt += n as u64;
149        Ok(n)
150    }
151
152    #[inline]
153    fn flush(&mut self) -> io::Result<()> {
154        self.inner.flush()
155    }
156}
157
158#[cold]
159fn bad_length() -> io::Error {
160    io::Error::new(io::ErrorKind::InvalidData, "unexpected file length")
161}
162
163pub(crate) struct LengthChecker<R> {
164    expected: u64,
165    reader: R,
166}
167
168impl<R> LengthChecker<R> {
169    #[inline]
170    pub fn new(reader: R, expected: u64) -> Self {
171        Self { expected, reader }
172    }
173}
174
175impl<R: io::Read> io::Read for LengthChecker<R> {
176    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
177        let n = self.reader.read(buf)?;
178        if n == 0 && self.expected != 0 {
179            return Err(bad_length());
180        }
181        self.expected = self.expected.checked_sub(n as u64).ok_or_else(bad_length)?;
182        Ok(n)
183    }
184
185    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
186        let size = self
187            .expected
188            .try_into()
189            .map_err(|_| io::ErrorKind::OutOfMemory)?;
190        buf.try_reserve(size)?;
191
192        let initial_len = buf.len();
193        buf.extend((0..size).map(|_| 0));
194        self.read_exact(&mut buf[initial_len..])?;
195
196        // Check that we really are at EOF
197        self.read(&mut [0])?;
198
199        Ok(size)
200    }
201
202    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
203        let size = self
204            .expected
205            .try_into()
206            .map_err(|_| io::ErrorKind::OutOfMemory)?;
207        buf.try_reserve(size)?;
208
209        // Forward to the default implementation of `read_to_string`
210
211        struct Reader<R>(R);
212        impl<R: io::Read> io::Read for Reader<R> {
213            fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
214                self.0.read(buf)
215            }
216            fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
217                self.0.read_to_end(buf)
218            }
219        }
220
221        Reader(self).read_to_string(buf)
222    }
223}