// The maximum length of the header filename and comment fields. More than // enough for these fields in reasonable use, but prevents possible attacks. const MAX_HEADER_BUF: usize = 65535;
/// A structure representing the header of a gzip stream. /// /// The header can contain metadata about the file that was compressed, if /// present. #[derive(PartialEq, Clone, Debug, Default)] pubstruct GzHeader {
extra: Option<Vec<u8>>,
filename: Option<Vec<u8>>,
comment: Option<Vec<u8>>,
operating_system: u8,
mtime: u32,
}
impl GzHeader { /// Returns the `filename` field of this gzip stream's header, if present. pubfn filename(&self) -> Option<&[u8]> { self.filename.as_ref().map(|s| &s[..])
}
/// Returns the `extra` field of this gzip stream's header, if present. pubfn extra(&self) -> Option<&[u8]> { self.extra.as_ref().map(|s| &s[..])
}
/// Returns the `comment` field of this gzip stream's header, if present. pubfn comment(&self) -> Option<&[u8]> { self.comment.as_ref().map(|s| &s[..])
}
/// Returns the `operating_system` field of this gzip stream's header. /// /// There are predefined values for various operating systems. /// 255 means that the value is unknown. pubfn operating_system(&self) -> u8 { self.operating_system
}
/// This gives the most recent modification time of the original file being compressed. /// /// The time is in Unix format, i.e., seconds since 00:00:00 GMT, Jan. 1, 1970. /// (Note that this may cause problems for MS-DOS and other systems that use local /// rather than Universal time.) If the compressed data did not come from a file, /// `mtime` is set to the time at which compression started. /// `mtime` = 0 means no time stamp is available. /// /// The usage of `mtime` is discouraged because of Year 2038 problem. pubfn mtime(&self) -> u32 { self.mtime
}
/// Returns the most recent modification time represented by a date-time type. /// Returns `None` if the value of the underlying counter is 0, /// indicating no time stamp is available. /// /// /// The time is measured as seconds since 00:00:00 GMT, Jan. 1 1970. /// See [`mtime`](#method.mtime) for more detail. pubfn mtime_as_datetime(&self) -> Option<time::SystemTime> { ifself.mtime == 0 {
None
} else { let duration = time::Duration::new(u64::from(self.mtime), 0); let datetime = time::UNIX_EPOCH + duration;
Some(datetime)
}
}
}
// Attempt to fill the `buffer` from `r`. Return the number of bytes read. // Return an error if EOF is read before the buffer is full. This differs // from `read` in that Ok(0) means that more data may be available. fn read_into<R: Read>(r: &mut R, buffer: &mut [u8]) -> Result<usize> {
debug_assert!(!buffer.is_empty()); match r.read(buffer) {
Ok(0) => Err(ErrorKind::UnexpectedEof.into()),
Ok(n) => Ok(n),
Err(ref e) if e.kind() == ErrorKind::Interrupted => Ok(0),
Err(e) => Err(e),
}
}
// Read `r` up to the first nul byte, pushing non-nul bytes to `buffer`. fn read_to_nul<R: BufRead>(r: &mut R, buffer: &mut Vec<u8>) -> Result<()> { letmut bytes = r.bytes(); loop { match bytes.next().transpose()? {
Some(0) => return Ok(()),
Some(_) if buffer.len() == MAX_HEADER_BUF => { return Err(Error::new(
ErrorKind::InvalidInput, "gzip header field too long",
));
}
Some(byte) => {
buffer.push(byte);
}
None => { return Err(ErrorKind::UnexpectedEof.into());
}
}
}
}
fn corrupt() -> Error {
Error::new(
ErrorKind::InvalidInput, "corrupt gzip stream does not have a matching checksum",
)
}
/// A builder structure to create a new gzip Encoder. /// /// This structure controls header configuration options such as the filename. /// /// # Examples /// /// ``` /// use std::io::prelude::*; /// # use std::io; /// use std::fs::File; /// use flate2::GzBuilder; /// use flate2::Compression; /// /// // GzBuilder opens a file and writes a sample string using GzBuilder pattern /// /// # fn sample_builder() -> Result<(), io::Error> { /// let f = File::create("examples/hello_world.gz")?; /// let mut gz = GzBuilder::new() /// .filename("hello_world.txt") /// .comment("test file, please delete") /// .write(f, Compression::default()); /// gz.write_all(b"hello world")?; /// gz.finish()?; /// # Ok(()) /// # } /// ``` #[derive(Debug, Default)] pubstruct GzBuilder {
extra: Option<Vec<u8>>,
filename: Option<CString>,
comment: Option<CString>,
operating_system: Option<u8>,
mtime: u32,
}
impl GzBuilder { /// Create a new blank builder with no header by default. pubfn new() -> GzBuilder { Self::default()
}
/// Configure the `mtime` field in the gzip header. pubfn mtime(mutself, mtime: u32) -> GzBuilder { self.mtime = mtime; self
}
/// Configure the `operating_system` field in the gzip header. pubfn operating_system(mutself, os: u8) -> GzBuilder { self.operating_system = Some(os); self
}
/// Configure the `extra` field in the gzip header. pubfn extra<T: Into<Vec<u8>>>(mutself, extra: T) -> GzBuilder { self.extra = Some(extra.into()); self
}
/// Configure the `filename` field in the gzip header. /// /// # Panics /// /// Panics if the `filename` slice contains a zero. pubfn filename<T: Into<Vec<u8>>>(mutself, filename: T) -> GzBuilder { self.filename = Some(CString::new(filename.into()).unwrap()); self
}
/// Configure the `comment` field in the gzip header. /// /// # Panics /// /// Panics if the `comment` slice contains a zero. pubfn comment<T: Into<Vec<u8>>>(mutself, comment: T) -> GzBuilder { self.comment = Some(CString::new(comment.into()).unwrap()); self
}
/// Consume this builder, creating a writer encoder in the process. /// /// The data written to the returned encoder will be compressed and then /// written out to the supplied parameter `w`. pubfn write<W: Write>(self, w: W, lvl: Compression) -> write::GzEncoder<W> {
write::gz_encoder(self.into_header(lvl), w, lvl)
}
/// Consume this builder, creating a reader encoder in the process. /// /// Data read from the returned encoder will be the compressed version of /// the data read from the given reader. pubfn read<R: Read>(self, r: R, lvl: Compression) -> read::GzEncoder<R> {
read::gz_encoder(self.buf_read(BufReader::new(r), lvl))
}
/// Consume this builder, creating a reader encoder in the process. /// /// Data read from the returned encoder will be the compressed version of /// the data read from the given reader. pubfn buf_read<R>(self, r: R, lvl: Compression) -> bufread::GzEncoder<R> where
R: BufRead,
{
bufread::gz_encoder(self.into_header(lvl), r, lvl)
}
// Typically this byte indicates what OS the gz stream was created on, // but in an effort to have cross-platform reproducible streams just // default this value to 255. I'm not sure that if we "correctly" set // this it'd do anything anyway...
header[9] = operating_system.unwrap_or(255);
header
}
}
#[cfg(test)] mod tests { use std::io::prelude::*;
usesuper::{read, write, GzBuilder, GzHeaderParser}; usecrate::{Compression, GzHeader}; use rand::{rng, Rng};
#[test] fn roundtrip() { letmut e = write::GzEncoder::new(Vec::new(), Compression::default());
e.write_all(b"foo bar baz").unwrap(); let inner = e.finish().unwrap(); letmut d = read::GzDecoder::new(&inner[..]); letmut s = String::new();
d.read_to_string(&mut s).unwrap();
assert_eq!(s, "foo bar baz");
}
#[test] fn roundtrip_zero() { let e = write::GzEncoder::new(Vec::new(), Compression::default()); let inner = e.finish().unwrap(); letmut d = read::GzDecoder::new(&inner[..]); letmut s = String::new();
d.read_to_string(&mut s).unwrap();
assert_eq!(s, "");
}
#[test] fn roundtrip_big() { letmut real = Vec::new(); letmut w = write::GzEncoder::new(Vec::new(), Compression::default()); let v = crate::random_bytes().take(1024).collect::<Vec<_>>(); for _ in0..200 { let to_write = &v[..rng().random_range(0..v.len())];
real.extend(to_write.iter().copied());
w.write_all(to_write).unwrap();
} let result = w.finish().unwrap(); letmut r = read::GzDecoder::new(&result[..]); letmut v = Vec::new();
r.read_to_end(&mut v).unwrap();
assert_eq!(v, real);
}
#[test] fn roundtrip_big2() { let v = crate::random_bytes().take(1024 * 1024).collect::<Vec<_>>(); letmut r = read::GzDecoder::new(read::GzEncoder::new(&v[..], Compression::default())); letmut res = Vec::new();
r.read_to_end(&mut res).unwrap();
assert_eq!(res, v);
}
// A Rust implementation of CRC that closely matches the C code in RFC1952. // Only use this to create CRCs for tests. struct Rfc1952Crc { /* Table of CRCs of all 8-bit messages. */
crc_table: [u32; 256],
}
impl Rfc1952Crc { fn new() -> Self { letmut crc = Rfc1952Crc {
crc_table: [0; 256],
}; /* Make the table for a fast CRC. */ for n in0usize..256 { letmut c = n as u32; for _k in0..8 { if c & 1 != 0 {
c = 0xedb88320 ^ (c >> 1);
} else {
c >>= 1;
}
}
crc.crc_table[n] = c;
}
crc
}
// Add a CRC to the header
header[3] ^= super::FHCRC; let rfc1952_crc = Rfc1952Crc::new(); let crc32 = rfc1952_crc.crc(&header); let crc16 = crc32 as u16;
header.extend(&crc16.to_le_bytes());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.