summaryrefslogtreecommitdiffstats
path: root/rust/vendor/lzma-rs/src/encode
diff options
context:
space:
mode:
Diffstat (limited to 'rust/vendor/lzma-rs/src/encode')
-rw-r--r--rust/vendor/lzma-rs/src/encode/dumbencoder.rs140
-rw-r--r--rust/vendor/lzma-rs/src/encode/lzma2.rs26
-rw-r--r--rust/vendor/lzma-rs/src/encode/mod.rs8
-rw-r--r--rust/vendor/lzma-rs/src/encode/options.rs30
-rw-r--r--rust/vendor/lzma-rs/src/encode/rangecoder.rs377
-rw-r--r--rust/vendor/lzma-rs/src/encode/util.rs75
-rw-r--r--rust/vendor/lzma-rs/src/encode/xz.rs163
7 files changed, 819 insertions, 0 deletions
diff --git a/rust/vendor/lzma-rs/src/encode/dumbencoder.rs b/rust/vendor/lzma-rs/src/encode/dumbencoder.rs
new file mode 100644
index 0000000..f1574c5
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/dumbencoder.rs
@@ -0,0 +1,140 @@
+use crate::compress::{Options, UnpackedSize};
+use crate::encode::rangecoder;
+use byteorder::{LittleEndian, WriteBytesExt};
+use std::io;
+
+pub struct Encoder<'a, W>
+where
+ W: 'a + io::Write,
+{
+ rangecoder: rangecoder::RangeEncoder<'a, W>,
+ literal_probs: [[u16; 0x300]; 8],
+ is_match: [u16; 4], // true = LZ, false = literal
+ unpacked_size: UnpackedSize,
+}
+
+const LC: u32 = 3;
+const LP: u32 = 0;
+const PB: u32 = 2;
+
+impl<'a, W> Encoder<'a, W>
+where
+ W: io::Write,
+{
+ pub fn from_stream(stream: &'a mut W, options: &Options) -> io::Result<Self> {
+ let dict_size = 0x0080_0000;
+
+ // Properties
+ let props = (LC + 9 * (LP + 5 * PB)) as u8;
+ lzma_info!("Properties {{ lc: {}, lp: {}, pb: {} }}", LC, LP, PB);
+ stream.write_u8(props)?;
+
+ // Dictionary
+ lzma_info!("Dict size: {}", dict_size);
+ stream.write_u32::<LittleEndian>(dict_size)?;
+
+ // Unpacked size
+ match &options.unpacked_size {
+ UnpackedSize::WriteToHeader(unpacked_size) => {
+ let value: u64 = match unpacked_size {
+ None => {
+ lzma_info!("Unpacked size: unknown");
+ 0xFFFF_FFFF_FFFF_FFFF
+ }
+ Some(x) => {
+ lzma_info!("Unpacked size: {}", x);
+ *x
+ }
+ };
+ stream.write_u64::<LittleEndian>(value)?;
+ }
+ UnpackedSize::SkipWritingToHeader => {}
+ };
+
+ let encoder = Encoder {
+ rangecoder: rangecoder::RangeEncoder::new(stream),
+ literal_probs: [[0x400; 0x300]; 8],
+ is_match: [0x400; 4],
+ unpacked_size: options.unpacked_size,
+ };
+
+ Ok(encoder)
+ }
+
+ pub fn process<R>(mut self, input: R) -> io::Result<()>
+ where
+ R: io::Read,
+ {
+ let mut prev_byte = 0u8;
+ let mut input_len = 0;
+
+ for (out_len, byte_result) in input.bytes().enumerate() {
+ let byte = byte_result?;
+ let pos_state = out_len & 3;
+ input_len = out_len;
+
+ // Literal
+ self.rangecoder
+ .encode_bit(&mut self.is_match[pos_state], false)?;
+
+ self.encode_literal(byte, prev_byte)?;
+ prev_byte = byte;
+ }
+
+ self.finish(input_len + 1)
+ }
+
+ fn finish(&mut self, input_len: usize) -> io::Result<()> {
+ match self.unpacked_size {
+ UnpackedSize::SkipWritingToHeader | UnpackedSize::WriteToHeader(Some(_)) => {}
+ UnpackedSize::WriteToHeader(None) => {
+ // Write end-of-stream marker
+ let pos_state = input_len & 3;
+
+ // Match
+ self.rangecoder
+ .encode_bit(&mut self.is_match[pos_state], true)?;
+ // New distance
+ self.rangecoder.encode_bit(&mut 0x400, false)?;
+
+ // Dummy len, as small as possible (len = 0)
+ for _ in 0..4 {
+ self.rangecoder.encode_bit(&mut 0x400, false)?;
+ }
+
+ // Distance marker = 0xFFFFFFFF
+ // pos_slot = 63
+ for _ in 0..6 {
+ self.rangecoder.encode_bit(&mut 0x400, true)?;
+ }
+ // num_direct_bits = 30
+ // result = 3 << 30 = C000_0000
+ // + 3FFF_FFF0 (26 bits)
+ // + F ( 4 bits)
+ for _ in 0..30 {
+ self.rangecoder.encode_bit(&mut 0x400, true)?;
+ }
+ // = FFFF_FFFF
+ }
+ }
+
+ // Flush range coder
+ self.rangecoder.finish()
+ }
+
+ fn encode_literal(&mut self, byte: u8, prev_byte: u8) -> io::Result<()> {
+ let prev_byte = prev_byte as usize;
+
+ let mut result: usize = 1;
+ let lit_state = prev_byte >> 5;
+ let probs = &mut self.literal_probs[lit_state];
+
+ for i in 0..8 {
+ let bit = ((byte >> (7 - i)) & 1) != 0;
+ self.rangecoder.encode_bit(&mut probs[result], bit)?;
+ result = (result << 1) ^ (bit as usize);
+ }
+
+ Ok(())
+ }
+}
diff --git a/rust/vendor/lzma-rs/src/encode/lzma2.rs b/rust/vendor/lzma-rs/src/encode/lzma2.rs
new file mode 100644
index 0000000..ead0726
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/lzma2.rs
@@ -0,0 +1,26 @@
+use byteorder::{BigEndian, WriteBytesExt};
+use std::io;
+
+pub fn encode_stream<R, W>(input: &mut R, output: &mut W) -> io::Result<()>
+where
+ R: io::BufRead,
+ W: io::Write,
+{
+ let mut buf = vec![0u8; 0x10000];
+ loop {
+ let n = input.read(&mut buf)?;
+ if n == 0 {
+ // status = EOF
+ output.write_u8(0)?;
+ break;
+ }
+
+ // status = uncompressed reset dict
+ output.write_u8(1)?;
+ // unpacked size
+ output.write_u16::<BigEndian>((n - 1) as u16)?;
+ // contents
+ output.write_all(&buf[..n])?;
+ }
+ Ok(())
+}
diff --git a/rust/vendor/lzma-rs/src/encode/mod.rs b/rust/vendor/lzma-rs/src/encode/mod.rs
new file mode 100644
index 0000000..98a0e84
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/mod.rs
@@ -0,0 +1,8 @@
+//! Encoding logic.
+
+pub mod dumbencoder;
+pub mod lzma2;
+pub mod options;
+mod rangecoder;
+mod util;
+pub mod xz;
diff --git a/rust/vendor/lzma-rs/src/encode/options.rs b/rust/vendor/lzma-rs/src/encode/options.rs
new file mode 100644
index 0000000..cf2d305
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/options.rs
@@ -0,0 +1,30 @@
+/// Options for the `lzma_compress` function
+#[derive(Clone, Copy, Debug, Default)]
+pub struct Options {
+ /// Defines whether the unpacked size should be written to the header.
+ /// The default is
+ /// [`UnpackedSize::WriteToHeader(None)`](enum.encode.UnpackedSize.html#variant.WriteValueToHeader)
+ pub unpacked_size: UnpackedSize,
+}
+
+/// Alternatives for handling unpacked size
+#[derive(Clone, Copy, Debug)]
+pub enum UnpackedSize {
+ /// If the value is `Some(u64)`, write the provided u64 value to the header.
+ /// There is currently no check in place that verifies that this is the actual number of bytes
+ /// provided by the input stream.
+ /// If the value is `None`, write the special `0xFFFF_FFFF_FFFF_FFFF` code to the header,
+ /// indicating that the unpacked size is unknown.
+ WriteToHeader(Option<u64>),
+ /// Do not write anything to the header. The unpacked size needs to be stored elsewhere and
+ /// provided when reading the file. Note that this is a non-standard way of writing LZMA data,
+ /// but is used by certain libraries such as
+ /// [OpenCTM](http://openctm.sourceforge.net/).
+ SkipWritingToHeader,
+}
+
+impl Default for UnpackedSize {
+ fn default() -> UnpackedSize {
+ UnpackedSize::WriteToHeader(None)
+ }
+}
diff --git a/rust/vendor/lzma-rs/src/encode/rangecoder.rs b/rust/vendor/lzma-rs/src/encode/rangecoder.rs
new file mode 100644
index 0000000..da5385d
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/rangecoder.rs
@@ -0,0 +1,377 @@
+use byteorder::WriteBytesExt;
+use std::io;
+
+pub struct RangeEncoder<'a, W>
+where
+ W: 'a + io::Write,
+{
+ stream: &'a mut W,
+ range: u32,
+ low: u64,
+ cache: u8,
+ cachesz: u32,
+}
+
+impl<'a, W> RangeEncoder<'a, W>
+where
+ W: io::Write,
+{
+ #[allow(clippy::let_and_return)]
+ pub fn new(stream: &'a mut W) -> Self {
+ let enc = Self {
+ stream,
+ range: 0xFFFF_FFFF,
+ low: 0,
+ cache: 0,
+ cachesz: 1,
+ };
+ lzma_debug!("0 {{ range: {:08x}, low: {:010x} }}", enc.range, enc.low);
+ enc
+ }
+
+ fn write_low(&mut self) -> io::Result<()> {
+ if self.low < 0xFF00_0000 || self.low > 0xFFFF_FFFF {
+ let mut tmp = self.cache;
+ loop {
+ let byte = tmp.wrapping_add((self.low >> 32) as u8);
+ self.stream.write_u8(byte)?;
+ lzma_debug!("> byte: {:02x}", byte);
+ tmp = 0xFF;
+ self.cachesz -= 1;
+ if self.cachesz == 0 {
+ break;
+ }
+ }
+ self.cache = (self.low >> 24) as u8;
+ }
+
+ self.cachesz += 1;
+ self.low = (self.low << 8) & 0xFFFF_FFFF;
+ Ok(())
+ }
+
+ pub fn finish(&mut self) -> io::Result<()> {
+ for _ in 0..5 {
+ self.write_low()?;
+
+ lzma_debug!("$ {{ range: {:08x}, low: {:010x} }}", self.range, self.low);
+ }
+ Ok(())
+ }
+
+ fn normalize(&mut self) -> io::Result<()> {
+ while self.range < 0x0100_0000 {
+ lzma_debug!(
+ "+ {{ range: {:08x}, low: {:010x}, cache: {:02x}, {} }}",
+ self.range,
+ self.low,
+ self.cache,
+ self.cachesz
+ );
+ self.range <<= 8;
+ self.write_low()?;
+ lzma_debug!(
+ "* {{ range: {:08x}, low: {:010x}, cache: {:02x}, {} }}",
+ self.range,
+ self.low,
+ self.cache,
+ self.cachesz
+ );
+ }
+ lzma_trace!(" {{ range: {:08x}, low: {:010x} }}", self.range, self.low);
+ Ok(())
+ }
+
+ pub fn encode_bit(&mut self, prob: &mut u16, bit: bool) -> io::Result<()> {
+ let bound: u32 = (self.range >> 11) * (*prob as u32);
+ lzma_trace!(
+ " bound: {:08x}, prob: {:04x}, bit: {}",
+ bound,
+ prob,
+ bit as u8
+ );
+
+ if bit {
+ *prob -= *prob >> 5;
+ self.low += bound as u64;
+ self.range -= bound;
+ } else {
+ *prob += (0x800_u16 - *prob) >> 5;
+ self.range = bound;
+ }
+
+ self.normalize()
+ }
+
+ #[cfg(test)]
+ fn encode_bit_tree(
+ &mut self,
+ num_bits: usize,
+ probs: &mut [u16],
+ value: u32,
+ ) -> io::Result<()> {
+ debug_assert!(value.leading_zeros() as usize + num_bits >= 32);
+ let mut tmp: usize = 1;
+ for i in 0..num_bits {
+ let bit = ((value >> (num_bits - i - 1)) & 1) != 0;
+ self.encode_bit(&mut probs[tmp], bit)?;
+ tmp = (tmp << 1) ^ (bit as usize);
+ }
+ Ok(())
+ }
+
+ #[cfg(test)]
+ pub fn encode_reverse_bit_tree(
+ &mut self,
+ num_bits: usize,
+ probs: &mut [u16],
+ offset: usize,
+ mut value: u32,
+ ) -> io::Result<()> {
+ debug_assert!(value.leading_zeros() as usize + num_bits >= 32);
+ let mut tmp: usize = 1;
+ for _ in 0..num_bits {
+ let bit = (value & 1) != 0;
+ value >>= 1;
+ self.encode_bit(&mut probs[offset + tmp], bit)?;
+ tmp = (tmp << 1) ^ (bit as usize);
+ }
+ Ok(())
+ }
+}
+
+// TODO: parametrize by constant and use [u16; 1 << num_bits] as soon as Rust supports this
+#[cfg(test)]
+#[derive(Clone)]
+pub struct BitTree {
+ num_bits: usize,
+ probs: Vec<u16>,
+}
+
+#[cfg(test)]
+impl BitTree {
+ pub fn new(num_bits: usize) -> Self {
+ BitTree {
+ num_bits,
+ probs: vec![0x400; 1 << num_bits],
+ }
+ }
+
+ pub fn encode<W: io::Write>(
+ &mut self,
+ rangecoder: &mut RangeEncoder<W>,
+ value: u32,
+ ) -> io::Result<()> {
+ rangecoder.encode_bit_tree(self.num_bits, self.probs.as_mut_slice(), value)
+ }
+
+ pub fn encode_reverse<W: io::Write>(
+ &mut self,
+ rangecoder: &mut RangeEncoder<W>,
+ value: u32,
+ ) -> io::Result<()> {
+ rangecoder.encode_reverse_bit_tree(self.num_bits, self.probs.as_mut_slice(), 0, value)
+ }
+}
+
+#[cfg(test)]
+pub struct LenEncoder {
+ choice: u16,
+ choice2: u16,
+ low_coder: Vec<BitTree>,
+ mid_coder: Vec<BitTree>,
+ high_coder: BitTree,
+}
+
+#[cfg(test)]
+impl LenEncoder {
+ pub fn new() -> Self {
+ LenEncoder {
+ choice: 0x400,
+ choice2: 0x400,
+ low_coder: vec![BitTree::new(3); 16],
+ mid_coder: vec![BitTree::new(3); 16],
+ high_coder: BitTree::new(8),
+ }
+ }
+
+ pub fn encode<W: io::Write>(
+ &mut self,
+ rangecoder: &mut RangeEncoder<W>,
+ pos_state: usize,
+ value: u32,
+ ) -> io::Result<()> {
+ let is_low: bool = value < 8;
+ rangecoder.encode_bit(&mut self.choice, !is_low)?;
+ if is_low {
+ return self.low_coder[pos_state].encode(rangecoder, value);
+ }
+
+ let is_middle: bool = value < 16;
+ rangecoder.encode_bit(&mut self.choice2, !is_middle)?;
+ if is_middle {
+ return self.mid_coder[pos_state].encode(rangecoder, value - 8);
+ }
+
+ self.high_coder.encode(rangecoder, value - 16)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::decode::rangecoder::{LenDecoder, RangeDecoder};
+ use crate::{decode, encode};
+ use std::io::BufReader;
+
+ fn encode_decode(prob_init: u16, bits: &[bool]) {
+ let mut buf: Vec<u8> = Vec::new();
+
+ let mut encoder = RangeEncoder::new(&mut buf);
+ let mut prob = prob_init;
+ for &b in bits {
+ encoder.encode_bit(&mut prob, b).unwrap();
+ }
+ encoder.finish().unwrap();
+
+ let mut bufread = BufReader::new(buf.as_slice());
+ let mut decoder = RangeDecoder::new(&mut bufread).unwrap();
+ let mut prob = prob_init;
+ for &b in bits {
+ assert_eq!(decoder.decode_bit(&mut prob, true).unwrap(), b);
+ }
+ assert!(decoder.is_finished_ok().unwrap());
+ }
+
+ #[test]
+ fn test_encode_decode_zeros() {
+ encode_decode(0x400, &[false; 10000]);
+ }
+
+ #[test]
+ fn test_encode_decode_ones() {
+ encode_decode(0x400, &[true; 10000]);
+ }
+
+ fn encode_decode_bittree(num_bits: usize, values: &[u32]) {
+ let mut buf: Vec<u8> = Vec::new();
+
+ let mut encoder = RangeEncoder::new(&mut buf);
+ let mut tree = encode::rangecoder::BitTree::new(num_bits);
+ for &v in values {
+ tree.encode(&mut encoder, v).unwrap();
+ }
+ encoder.finish().unwrap();
+
+ let mut bufread = BufReader::new(buf.as_slice());
+ let mut decoder = RangeDecoder::new(&mut bufread).unwrap();
+ let mut tree = decode::rangecoder::BitTree::new(num_bits);
+ for &v in values {
+ assert_eq!(tree.parse(&mut decoder, true).unwrap(), v);
+ }
+ assert!(decoder.is_finished_ok().unwrap());
+ }
+
+ #[test]
+ fn test_encode_decode_bittree_zeros() {
+ for num_bits in 0..16 {
+ encode_decode_bittree(num_bits, &[0; 10000]);
+ }
+ }
+
+ #[test]
+ fn test_encode_decode_bittree_ones() {
+ for num_bits in 0..16 {
+ encode_decode_bittree(num_bits, &[(1 << num_bits) - 1; 10000]);
+ }
+ }
+
+ #[test]
+ fn test_encode_decode_bittree_all() {
+ for num_bits in 0..16 {
+ let max = 1 << num_bits;
+ let values: Vec<u32> = (0..max).collect();
+ encode_decode_bittree(num_bits, &values);
+ }
+ }
+
+ fn encode_decode_reverse_bittree(num_bits: usize, values: &[u32]) {
+ let mut buf: Vec<u8> = Vec::new();
+
+ let mut encoder = RangeEncoder::new(&mut buf);
+ let mut tree = encode::rangecoder::BitTree::new(num_bits);
+ for &v in values {
+ tree.encode_reverse(&mut encoder, v).unwrap();
+ }
+ encoder.finish().unwrap();
+
+ let mut bufread = BufReader::new(buf.as_slice());
+ let mut decoder = RangeDecoder::new(&mut bufread).unwrap();
+ let mut tree = decode::rangecoder::BitTree::new(num_bits);
+ for &v in values {
+ assert_eq!(tree.parse_reverse(&mut decoder, true).unwrap(), v);
+ }
+ assert!(decoder.is_finished_ok().unwrap());
+ }
+
+ #[test]
+ fn test_encode_decode_reverse_bittree_zeros() {
+ for num_bits in 0..16 {
+ encode_decode_reverse_bittree(num_bits, &[0; 10000]);
+ }
+ }
+
+ #[test]
+ fn test_encode_decode_reverse_bittree_ones() {
+ for num_bits in 0..16 {
+ encode_decode_reverse_bittree(num_bits, &[(1 << num_bits) - 1; 10000]);
+ }
+ }
+
+ #[test]
+ fn test_encode_decode_reverse_bittree_all() {
+ for num_bits in 0..16 {
+ let max = 1 << num_bits;
+ let values: Vec<u32> = (0..max).collect();
+ encode_decode_reverse_bittree(num_bits, &values);
+ }
+ }
+
+ fn encode_decode_length(pos_state: usize, values: &[u32]) {
+ let mut buf: Vec<u8> = Vec::new();
+
+ let mut encoder = RangeEncoder::new(&mut buf);
+ let mut len_encoder = LenEncoder::new();
+ for &v in values {
+ len_encoder.encode(&mut encoder, pos_state, v).unwrap();
+ }
+ encoder.finish().unwrap();
+
+ let mut bufread = BufReader::new(buf.as_slice());
+ let mut decoder = RangeDecoder::new(&mut bufread).unwrap();
+ let mut len_decoder = LenDecoder::new();
+ for &v in values {
+ assert_eq!(
+ len_decoder.decode(&mut decoder, pos_state, true).unwrap(),
+ v as usize
+ );
+ }
+ assert!(decoder.is_finished_ok().unwrap());
+ }
+
+ #[test]
+ fn test_encode_decode_length_zeros() {
+ for pos_state in 0..16 {
+ encode_decode_length(pos_state, &[0; 10000]);
+ }
+ }
+
+ #[test]
+ fn test_encode_decode_length_all() {
+ for pos_state in 0..16 {
+ let max = (1 << 8) + 16;
+ let values: Vec<u32> = (0..max).collect();
+ encode_decode_length(pos_state, &values);
+ }
+ }
+}
diff --git a/rust/vendor/lzma-rs/src/encode/util.rs b/rust/vendor/lzma-rs/src/encode/util.rs
new file mode 100644
index 0000000..e231f60
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/util.rs
@@ -0,0 +1,75 @@
+use std::hash;
+use std::io;
+
+// A Write computing a digest on the bytes written.
+pub struct HasherWrite<'a, W, H>
+where
+ W: 'a + io::Write,
+ H: 'a + hash::Hasher,
+{
+ write: &'a mut W, // underlying writer
+ hasher: &'a mut H, // hasher
+}
+
+impl<'a, W, H> HasherWrite<'a, W, H>
+where
+ W: io::Write,
+ H: hash::Hasher,
+{
+ pub fn new(write: &'a mut W, hasher: &'a mut H) -> Self {
+ Self { write, hasher }
+ }
+}
+
+impl<'a, W, H> io::Write for HasherWrite<'a, W, H>
+where
+ W: io::Write,
+ H: hash::Hasher,
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let result = self.write.write(buf)?;
+ self.hasher.write(&buf[..result]);
+ Ok(result)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.write.flush()
+ }
+}
+
+// A Write counting the bytes written.
+pub struct CountWrite<'a, W>
+where
+ W: 'a + io::Write,
+{
+ write: &'a mut W, // underlying writer
+ count: usize, // number of bytes written
+}
+
+impl<'a, W> CountWrite<'a, W>
+where
+ W: io::Write,
+{
+ pub fn new(write: &'a mut W) -> Self {
+ Self { write, count: 0 }
+ }
+
+ pub fn count(&self) -> usize {
+ self.count
+ }
+}
+
+impl<'a, W> io::Write for CountWrite<'a, W>
+where
+ W: io::Write,
+{
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ let result = self.write.write(buf)?;
+ self.count += result;
+ Ok(result)
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ self.write.flush()
+ }
+}
diff --git a/rust/vendor/lzma-rs/src/encode/xz.rs b/rust/vendor/lzma-rs/src/encode/xz.rs
new file mode 100644
index 0000000..15eca80
--- /dev/null
+++ b/rust/vendor/lzma-rs/src/encode/xz.rs
@@ -0,0 +1,163 @@
+use crate::decode;
+use crate::encode::lzma2;
+use crate::encode::util;
+use crate::xz::{footer, header, CheckMethod, StreamFlags};
+use byteorder::{LittleEndian, WriteBytesExt};
+use crc::{crc32, Hasher32};
+use std::io;
+use std::io::Write;
+
+pub fn encode_stream<R, W>(input: &mut R, output: &mut W) -> io::Result<()>
+where
+ R: io::BufRead,
+ W: io::Write,
+{
+ let stream_flags = StreamFlags {
+ check_method: CheckMethod::None,
+ };
+
+ // Header
+ write_header(output, stream_flags)?;
+
+ // Block
+ let (unpadded_size, unpacked_size) = write_block(input, output)?;
+
+ // Index
+ let index_size = write_index(output, unpadded_size, unpacked_size)?;
+
+ // Footer
+ write_footer(output, stream_flags, index_size)
+}
+
+fn write_header<W>(output: &mut W, stream_flags: StreamFlags) -> io::Result<()>
+where
+ W: io::Write,
+{
+ output.write_all(header::XZ_MAGIC)?;
+ let mut digest = crc32::Digest::new(crc32::IEEE);
+ {
+ let mut digested = util::HasherWrite::new(output, &mut digest);
+ stream_flags.serialize(&mut digested)?;
+ }
+ let crc32 = digest.sum32();
+ output.write_u32::<LittleEndian>(crc32)?;
+ Ok(())
+}
+
+fn write_footer<W>(output: &mut W, stream_flags: StreamFlags, index_size: usize) -> io::Result<()>
+where
+ W: io::Write,
+{
+ let mut digest = crc32::Digest::new(crc32::IEEE);
+ let mut footer_buf: Vec<u8> = Vec::new();
+ {
+ let mut digested = util::HasherWrite::new(&mut footer_buf, &mut digest);
+
+ let backward_size = (index_size >> 2) - 1;
+ digested.write_u32::<LittleEndian>(backward_size as u32)?;
+ stream_flags.serialize(&mut digested)?;
+ }
+ let crc32 = digest.sum32();
+ output.write_u32::<LittleEndian>(crc32)?;
+ output.write_all(footer_buf.as_slice())?;
+
+ output.write_all(footer::XZ_MAGIC_FOOTER)?;
+ Ok(())
+}
+
+fn write_block<R, W>(input: &mut R, output: &mut W) -> io::Result<(usize, usize)>
+where
+ R: io::BufRead,
+ W: io::Write,
+{
+ let (unpadded_size, unpacked_size) = {
+ let mut count_output = util::CountWrite::new(output);
+
+ // Block header
+ let mut digest = crc32::Digest::new(crc32::IEEE);
+ {
+ let mut digested = util::HasherWrite::new(&mut count_output, &mut digest);
+ let header_size = 8;
+ digested.write_u8((header_size >> 2) as u8)?;
+ let flags = 0x00; // 1 filter, no (un)packed size provided
+ digested.write_u8(flags)?;
+ let filter_id = 0x21; // LZMA2
+ digested.write_u8(filter_id)?;
+ let size_of_properties = 1;
+ digested.write_u8(size_of_properties)?;
+ let properties = 22; // TODO
+ digested.write_u8(properties)?;
+ let padding = [0, 0, 0];
+ digested.write_all(&padding)?;
+ }
+ let crc32 = digest.sum32();
+ count_output.write_u32::<LittleEndian>(crc32)?;
+
+ // Block
+ let mut count_input = decode::util::CountBufRead::new(input);
+ lzma2::encode_stream(&mut count_input, &mut count_output)?;
+ (count_output.count(), count_input.count())
+ };
+ lzma_info!(
+ "Unpadded size = {}, unpacked_size = {}",
+ unpadded_size,
+ unpacked_size
+ );
+
+ let padding_size = ((unpadded_size ^ 0x03) + 1) & 0x03;
+ let padding = vec![0; padding_size];
+ output.write_all(padding.as_slice())?;
+ // Checksum = None (cf. above)
+
+ Ok((unpadded_size, unpacked_size))
+}
+
+fn write_index<W>(output: &mut W, unpadded_size: usize, unpacked_size: usize) -> io::Result<usize>
+where
+ W: io::Write,
+{
+ let mut count_output = util::CountWrite::new(output);
+
+ let mut digest = crc32::Digest::new(crc32::IEEE);
+ {
+ let mut digested = util::HasherWrite::new(&mut count_output, &mut digest);
+ digested.write_u8(0)?; // No more block
+ let num_records = 1;
+ write_multibyte(&mut digested, num_records)?;
+
+ write_multibyte(&mut digested, unpadded_size as u64)?;
+ write_multibyte(&mut digested, unpacked_size as u64)?;
+ }
+
+ // Padding
+ let count = count_output.count();
+ let padding_size = ((count ^ 0x03) + 1) & 0x03;
+ {
+ let mut digested = util::HasherWrite::new(&mut count_output, &mut digest);
+ let padding = vec![0; padding_size];
+ digested.write_all(padding.as_slice())?;
+ }
+
+ let crc32 = digest.sum32();
+ count_output.write_u32::<LittleEndian>(crc32)?;
+
+ Ok(count_output.count())
+}
+
+fn write_multibyte<W>(output: &mut W, mut value: u64) -> io::Result<()>
+where
+ W: io::Write,
+{
+ loop {
+ let byte = (value & 0x7F) as u8;
+ value >>= 7;
+ if value == 0 {
+ output.write_u8(byte)?;
+ break;
+ } else {
+ output.write_u8(0x80 | byte)?;
+ }
+ }
+
+ Ok(())
+}