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 write_header(output, stream_flags)?;
20
21 let (unpadded_size, unpacked_size) = write_block(input, output)?;
23
24 let index_size = write_index(output, unpadded_size, unpacked_size)?;
26
27 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 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; digested.write_u8(flags)?;
83 let filter_id = 0x21; digested.write_u8(filter_id)?;
85 let size_of_properties = 1;
86 digested.write_u8(size_of_properties)?;
87 let properties = 22; 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 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 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)?; 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 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}