diff options
Diffstat (limited to 'rust/vendor/lzma-rs/src/encode')
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/dumbencoder.rs | 140 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/lzma2.rs | 26 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/mod.rs | 8 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/options.rs | 30 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/rangecoder.rs | 377 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/util.rs | 75 | ||||
-rw-r--r-- | rust/vendor/lzma-rs/src/encode/xz.rs | 163 |
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(()) +} |