lzma_rs/encode/
xz.rs

1use crate::decode;
2use crate::encode::{lzma2, util};
3use crate::xz::crc::CRC32;
4use crate::xz::{footer, header, CheckMethod, StreamFlags};
5use byteorder::{LittleEndian, WriteBytesExt};
6use std::io;
7use std::io::Write;
8
9pub fn encode_stream<R, W>(input: &mut R, output: &mut W) -> io::Result<()>
10where
11    R: io::BufRead,
12    W: io::Write,
13{
14    let stream_flags = StreamFlags {
15        check_method: CheckMethod::None,
16    };
17
18    // Header
19    write_header(output, stream_flags)?;
20
21    // Block
22    let (unpadded_size, unpacked_size) = write_block(input, output)?;
23
24    // Index
25    let index_size = write_index(output, unpadded_size, unpacked_size)?;
26
27    // Footer
28    write_footer(output, stream_flags, index_size)
29}
30
31fn write_header<W>(output: &mut W, stream_flags: StreamFlags) -> io::Result<()>
32where
33    W: io::Write,
34{
35    output.write_all(header::XZ_MAGIC)?;
36    let mut digest = CRC32.digest();
37    {
38        let mut digested = util::CrcDigestWrite::new(output, &mut digest);
39        stream_flags.serialize(&mut digested)?;
40    }
41    let crc32 = digest.finalize();
42    output.write_u32::<LittleEndian>(crc32)?;
43    Ok(())
44}
45
46fn write_footer<W>(output: &mut W, stream_flags: StreamFlags, index_size: usize) -> io::Result<()>
47where
48    W: io::Write,
49{
50    let mut digest = CRC32.digest();
51    let mut footer_buf: Vec<u8> = Vec::new();
52    {
53        let mut digested = util::CrcDigestWrite::new(&mut footer_buf, &mut digest);
54
55        let backward_size = (index_size >> 2) - 1;
56        digested.write_u32::<LittleEndian>(backward_size as u32)?;
57        stream_flags.serialize(&mut digested)?;
58    }
59    let crc32 = digest.finalize();
60    output.write_u32::<LittleEndian>(crc32)?;
61    output.write_all(footer_buf.as_slice())?;
62
63    output.write_all(footer::XZ_MAGIC_FOOTER)?;
64    Ok(())
65}
66
67fn write_block<R, W>(input: &mut R, output: &mut W) -> io::Result<(usize, usize)>
68where
69    R: io::BufRead,
70    W: io::Write,
71{
72    let (unpadded_size, unpacked_size) = {
73        let mut count_output = util::CountWrite::new(output);
74
75        // Block header
76        let mut digest = CRC32.digest();
77        {
78            let mut digested = util::CrcDigestWrite::new(&mut count_output, &mut digest);
79            let header_size = 8;
80            digested.write_u8((header_size >> 2) as u8)?;
81            let flags = 0x00; // 1 filter, no (un)packed size provided
82            digested.write_u8(flags)?;
83            let filter_id = 0x21; // LZMA2
84            digested.write_u8(filter_id)?;
85            let size_of_properties = 1;
86            digested.write_u8(size_of_properties)?;
87            let properties = 22; // TODO
88            digested.write_u8(properties)?;
89            let padding = [0, 0, 0];
90            digested.write_all(&padding)?;
91        }
92        let crc32 = digest.finalize();
93        count_output.write_u32::<LittleEndian>(crc32)?;
94
95        // Block
96        let mut count_input = decode::util::CountBufRead::new(input);
97        lzma2::encode_stream(&mut count_input, &mut count_output)?;
98        (count_output.count(), count_input.count())
99    };
100    lzma_info!(
101        "Unpadded size = {}, unpacked_size = {}",
102        unpadded_size,
103        unpacked_size
104    );
105
106    let padding_size = ((unpadded_size ^ 0x03) + 1) & 0x03;
107    let padding = vec![0; padding_size];
108    output.write_all(padding.as_slice())?;
109    // Checksum = None (cf. above)
110
111    Ok((unpadded_size, unpacked_size))
112}
113
114fn write_index<W>(output: &mut W, unpadded_size: usize, unpacked_size: usize) -> io::Result<usize>
115where
116    W: io::Write,
117{
118    let mut count_output = util::CountWrite::new(output);
119
120    let mut digest = CRC32.digest();
121    {
122        let mut digested = util::CrcDigestWrite::new(&mut count_output, &mut digest);
123        digested.write_u8(0)?; // No more block
124        let num_records = 1;
125        write_multibyte(&mut digested, num_records)?;
126
127        write_multibyte(&mut digested, unpadded_size as u64)?;
128        write_multibyte(&mut digested, unpacked_size as u64)?;
129    }
130
131    // Padding
132    let count = count_output.count();
133    let padding_size = ((count ^ 0x03) + 1) & 0x03;
134    {
135        let mut digested = util::CrcDigestWrite::new(&mut count_output, &mut digest);
136        let padding = vec![0; padding_size];
137        digested.write_all(padding.as_slice())?;
138    }
139
140    let crc32 = digest.finalize();
141    count_output.write_u32::<LittleEndian>(crc32)?;
142
143    Ok(count_output.count())
144}
145
146fn write_multibyte<W>(output: &mut W, mut value: u64) -> io::Result<()>
147where
148    W: io::Write,
149{
150    loop {
151        let byte = (value & 0x7F) as u8;
152        value >>= 7;
153        if value == 0 {
154            output.write_u8(byte)?;
155            break;
156        } else {
157            output.write_u8(0x80 | byte)?;
158        }
159    }
160
161    Ok(())
162}