lzma_rs/xz/
mod.rs

1//! Logic for handling `.xz` file format.
2//!
3//! Format specifications are at [https://tukaani.org/xz/xz-file-format.txt](spec).
4//!
5//! [spec]: https://tukaani.org/xz/xz-file-format.txt
6
7use crate::error;
8use std::io;
9
10pub(crate) mod crc;
11pub(crate) mod footer;
12pub(crate) mod header;
13
14/// Stream flags, see sect. 2.1.1.2.
15///
16/// This does not store the leading null byte, which is currently unused.
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub(crate) struct StreamFlags {
19    pub(crate) check_method: CheckMethod,
20}
21
22impl StreamFlags {
23    /// Parse Stream Flags from a 16bits value.
24    pub(crate) fn parse(input: u16) -> error::Result<Self> {
25        let flags_bytes = input.to_be_bytes();
26
27        if flags_bytes[0] != 0x00 {
28            return Err(error::Error::XzError(format!(
29                "Invalid null byte in Stream Flags: {:x}",
30                flags_bytes[0]
31            )));
32        }
33
34        let flags = Self {
35            check_method: CheckMethod::try_from(flags_bytes[1])?,
36        };
37        Ok(flags)
38    }
39
40    /// Serialize Stream Flags into a writer.
41    pub(crate) fn serialize<W>(self, writer: &mut W) -> io::Result<usize>
42    where
43        W: io::Write,
44    {
45        // First byte is currently unused and hard-coded to null.
46        writer
47            .write(&[0x00, self.check_method as u8])
48            .map_err(Into::into)
49    }
50}
51
52/// Stream check type, see sect. 2.1.1.2.
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54#[repr(u8)]
55pub enum CheckMethod {
56    None = 0x00,
57    Crc32 = 0x01,
58    Crc64 = 0x04,
59    Sha256 = 0x0A,
60}
61
62impl CheckMethod {
63    /// Parse Check ID (second byte in Stream Flags).
64    pub fn try_from(id: u8) -> error::Result<CheckMethod> {
65        match id {
66            0x00 => Ok(CheckMethod::None),
67            0x01 => Ok(CheckMethod::Crc32),
68            0x04 => Ok(CheckMethod::Crc64),
69            0x0A => Ok(CheckMethod::Sha256),
70            _ => Err(error::Error::XzError(format!(
71                "Invalid check method {:x}, expected one of [0x00, 0x01, 0x04, 0x0A]",
72                id
73            ))),
74        }
75    }
76}
77
78impl From<CheckMethod> for u8 {
79    fn from(method: CheckMethod) -> u8 {
80        method as u8
81    }
82}
83
84#[cfg(test)]
85mod test {
86    use super::*;
87    use byteorder::{BigEndian, ReadBytesExt};
88    use std::io::{Seek, SeekFrom};
89
90    #[test]
91    fn test_checkmethod_roundtrip() {
92        let mut count_valid = 0;
93        for input in 0..std::u8::MAX {
94            if let Ok(check) = CheckMethod::try_from(input) {
95                let output: u8 = check.into();
96                assert_eq!(input, output);
97                count_valid += 1;
98            }
99        }
100        assert_eq!(count_valid, 4);
101    }
102
103    #[test]
104    fn test_streamflags_roundtrip() {
105        let input = StreamFlags {
106            check_method: CheckMethod::Crc32,
107        };
108
109        let mut cursor = std::io::Cursor::new(vec![0u8; 2]);
110        let len = input.serialize(&mut cursor).unwrap();
111        assert_eq!(len, 2);
112
113        cursor.seek(SeekFrom::Start(0)).unwrap();
114        let field = cursor.read_u16::<BigEndian>().unwrap();
115        let output = StreamFlags::parse(field).unwrap();
116        assert_eq!(input, output);
117    }
118}