gzip-header-0.3.0/.gitignore010066400017500001750000000004131307177036700141350ustar0000000000000000# Compiled files *.o *.so *.rlib *.dll # Executables *.exe # Generated by Cargo /target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock gzip-header-0.3.0/Cargo.toml.orig010064400017500001750000000010571350413533500150260ustar0000000000000000[package] name = "gzip-header" version = "0.3.0" license = "MIT/Apache-2.0" authors = ["oyvindln "] readme = "README.md" keywords = ["gzip", "compression"] repository = "https://github.com/oyvindln/gzip-header" homepage = "https://github.com/oyvindln/gzip-header" documentation = "https://docs.rs/gzip-header/" description = """ A crate for decoding and encoding the header part of gzip files based on the gzip header implementation in the flate2 crate. """ categories = ["compression"] [dependencies] crc32fast = "1.2.0" gzip-header-0.3.0/Cargo.toml0000644000000021000000000000000112610ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "gzip-header" version = "0.3.0" authors = ["oyvindln "] description = "A crate for decoding and encoding the header part of gzip files based on the gzip header\n implementation in the flate2 crate.\n" homepage = "https://github.com/oyvindln/gzip-header" documentation = "https://docs.rs/gzip-header/" readme = "README.md" keywords = ["gzip", "compression"] categories = ["compression"] license = "MIT/Apache-2.0" repository = "https://github.com/oyvindln/gzip-header" [dependencies.crc32fast] version = "1.2.0" gzip-header-0.3.0/LICENSE-APACHE010066400017500001750000000251371307214703700140740ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. gzip-header-0.3.0/LICENSE-MIT010066400017500001750000000020751307214705600136010ustar0000000000000000Copyright (c) 2014 Alex Crichton Copyright (c) 2017 oyvindln Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. gzip-header-0.3.0/README.md010066400017500001750000000017471307214702200134220ustar0000000000000000# gzip-header A library to decode and encode headers for the [gzip format](http://www.gzip.org/zlib/rfc-gzip.html). The library also contains a reader absctraction over a CRC checksum hasher. A file in the gzip format contains a gzip header, a number of compressed data blocks in the [DEFLATE](http://www.gzip.org/zlib/rfc-deflate.html) format, and ends with the CRC32-checksum (in the IEEE format) and number of bytes (modulo `2^32`) of the uncompressed data. The gzip header is purely a set of metadata, and doesn't have any impact on the decoding of the compressed data other than the fact that `DEFLATE`-encoded data with a gzip-header is checked using the CRC32 algorithm. This library is based on the gzip header functionality in the [flate2](https://crates.io/crates/flate2) crate. # License Like the non-C parts of `flate2-rs`, `gzip-header` is distributed under the terms of both the MIT license and the Apache License (Version 2.0), See LICENSE-APACHE, and LICENSE-MIT for details. gzip-header-0.3.0/src/crc_reader.rs010064400017500001750000000062701350413530100153600ustar0000000000000000//! Simple CRC wrappers backed by the crc32 crate. use std::io::{BufRead, Read}; use std::io; use std::fmt; use crc32fast::Hasher; /// A wrapper struct containing a CRC checksum in the format used by gzip and the amount of bytes /// input to it mod 2^32. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] pub struct Crc { // We don't use the Digest struct from the Crc crate for now as it doesn't implement `Display` // and other common traits. checksum: u32, amt: u32, } impl fmt::Display for Crc { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "({:#x},{})", self.checksum, self.amt) } } /// A reader that updates the checksum and counter of a `Crc` struct when reading from the wrapped /// reader. #[derive(Debug)] pub struct CrcReader { inner: R, crc: Crc, } impl Crc { /// Create a new empty CRC struct. pub fn new() -> Crc { Crc { checksum: 0, amt: 0, } } pub fn with_initial(checksum: u32, amount: u32) -> Crc { Crc { checksum: checksum, amt: amount, } } /// Return the current checksum value. pub fn sum(&self) -> u32 { self.checksum } /// Return the number of bytes input. pub fn amt_as_u32(&self) -> u32 { self.amt } /// Update the checksum and byte counter with the provided data. pub fn update(&mut self, data: &[u8]) { self.amt = self.amt.wrapping_add(data.len() as u32); let mut h = Hasher::new_with_initial(self.checksum); h.update(data); self.checksum = h.finalize(); } /// Reset the checksum and byte counter. pub fn reset(&mut self) { self.checksum = 0; self.amt = 0; } } impl CrcReader { /// Create a new `CrcReader` with a blank checksum. pub fn new(r: R) -> CrcReader { CrcReader { inner: r, crc: Crc::new(), } } /// Return a reference to the underlying checksum struct. pub fn crc(&self) -> &Crc { &self.crc } /// Consume this `CrcReader` and return the wrapped `Read` instance. pub fn into_inner(self) -> R { self.inner } /// Return a mutable reference to the wrapped reader. pub fn inner(&mut self) -> &mut R { &mut self.inner } /// Reset the checksum and counter. pub fn reset(&mut self) { self.crc.reset(); } } impl Read for CrcReader { fn read(&mut self, into: &mut [u8]) -> io::Result { let amt = try!(self.inner.read(into)); self.crc.update(&into[..amt]); Ok(amt) } } impl BufRead for CrcReader { fn fill_buf(&mut self) -> io::Result<&[u8]> { self.inner.fill_buf() } fn consume(&mut self, amt: usize) { if let Ok(data) = self.inner.fill_buf() { self.crc.update(&data[..amt]); } self.inner.consume(amt); } } #[cfg(test)] mod test { use super::Crc; #[test] fn checksum_correct() { let mut c = Crc::new(); c.update(b"abcdefg12345689\n"); assert_eq!(c.sum(), 0x141ddb83); assert_eq!(c.amt_as_u32(), 16); } } gzip-header-0.3.0/src/lib.rs010064400017500001750000000470151350413530100140370ustar0000000000000000//! A library to decode and encode headers for the //! [gzip format](http://www.gzip.org/zlib/rfc-gzip.html). //! The library also contains a reader absctraction over a CRC checksum hasher. //! //! A file in the gzip format contains a gzip header, a number of compressed data blocks in the //! [DEFLATE](http://www.gzip.org/zlib/rfc-deflate.html) format, and ends with the CRC32-checksum //! (in the IEEE format) and number of bytes (modulo `2^32`) of the uncompressed data. //! //! The gzip header is purely a set of metadata, and doesn't have any impact on the decoding of the //! compressed data other than the fact that `DEFLATE`-encoded data with a gzip-header is //! checked using the CRC32 algorithm. //! //! This library is based on the gzip header functionality in the //! [flate2](https://crates.io/crates/flate2) crate. extern crate crc32fast; mod crc_reader; use std::borrow::Cow; use std::ffi::CString; use std::{env, io, time}; use std::io::Read; use std::fmt; use std::default::Default; pub use crc_reader::{Crc, CrcReader}; static FHCRC: u8 = 1 << 1; static FEXTRA: u8 = 1 << 2; static FNAME: u8 = 1 << 3; static FCOMMENT: u8 = 1 << 4; /// An enum describing the different OS types described in the gzip format. /// See http://www.gzip.org/format.txt (Additionally, the Apple(19) value is defined in the zlib /// library). #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[repr(u8)] pub enum FileSystemType { ///MS-DOS/old FAT filesystem Fat = 0, Amiga = 1, Vms = 2, Unix = 3, Vcms = 4, AtariTos = 5, Hpfs = 6, /// Used for apple platforms. Newer encoders may use 19 instead for modern systems. Macintosh = 7, Zsystem = 8, Cpm = 9, /// This is used for Windows/NTFS in zlib newer than 1.2.11, but not in gzip due to following /// updates to the ZIP format. /// See https://github.com/madler/zlib/issues/235 and /// https://github.com/madler/zlib/commit/ce12c5cd00628bf8f680c98123a369974d32df15 Tops20OrNTFS = 10, /// Used for Windows platforms for older zlib versions and other encoders. NTFS = 11, SmsQdos = 12, Riscos = 13, /// Newer fat filesystems (i.e FAT32). Vfat = 14, Mvs = 15, Beos = 16, TandemNsk = 17, Theos = 18, /// Modern apple platforms. /// Defined in the zlib library (see zutil.h) Apple = 19, Unknown = 255, } impl FileSystemType { /// Get the raw byte value of this `FileSystemType` variant. pub fn as_u8(&self) -> u8 { *self as u8 } /// Get the corresponding `ExtraFlags` value from a raw byte. /// /// Returns `FileSystemType::Unknown` (defined as 255 as that is the value used in the /// specification for `Unknown`) if the value is not one of the currently known types /// (Which currently means any value > 19). pub fn from_u8(value: u8) -> FileSystemType { use FileSystemType::*; match value { 0 => Fat, 1 => Amiga, 2 => Vms, 3 => Unix, 4 => Vcms, 5 => AtariTos, 6 => Hpfs, 7 => Macintosh, 8 => Zsystem, 9 => Cpm, 10 => Tops20OrNTFS, 11 => NTFS, 12 => SmsQdos, 13 => Riscos, 14 => Vfat, 15 => Mvs, 16 => Beos, 17 => TandemNsk, 18 => Theos, 19 => Apple, _ => Unknown, } } } impl fmt::Display for FileSystemType { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use FileSystemType::*; match *self { Fat => "FAT filesystem (MS-DOS, OS/2, NT/Win32)", Amiga => "Amiga", Vms => "VMS or OpenVMS", Unix => "Unix type system/Linux", Vcms => "VM/CMS", AtariTos => "Atari TOS", Hpfs => "HPFS filesystem (OS/2, NT)", Macintosh => "Macintosh operating system (Classic Mac OS, OS/X, macOS, iOS etc.)", Zsystem => "Z-System", Cpm => "CP/M", Tops20OrNTFS => "NTFS (New zlib versions) or TOPS-20", NTFS => "NTFS", SmsQdos => "SMS/QDOS", Riscos => "Acorn RISC OS", Vfat => "VFAT file system (Win95, NT)", Mvs => "MVS or PRIMOS", Beos => "BeOS", TandemNsk => "Tandem/NSK", Theos => "THEOS", Apple => "macOS, OS/X, iOS or watchOS", _ => "Unknown or unset", }.fmt(f) } } /// Valid values for the extra flag in the gzip specification. /// /// This is a field to be used by the compression methods. For deflate, which is the only /// specified compression method, this is a value indicating the level of compression of the /// contained compressed data. This value does not have to correspond to the actual compression /// level of the contained data, it's only a hint that the the encoder may set. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] #[repr(u8)] pub enum ExtraFlags { Default = 0, MaximumCompression = 2, FastestCompression = 4, } impl ExtraFlags { /// Get the corresponding `ExtraFlags` value from a raw byte. /// /// Returns `ExtraFlags::Default` (defined as 0 by the gzip specification) for values other than /// 2 and 4. pub fn from_u8(value: u8) -> ExtraFlags { use ExtraFlags::*; match value { 2 => MaximumCompression, 4 => FastestCompression, _ => Default, } } /// Get the raw byte value of this `ExtraFlags` variant. pub fn as_u8(&self) -> u8 { *self as u8 } } impl fmt::Display for ExtraFlags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { ExtraFlags::Default => "No extra flags (Default) or unknown.", ExtraFlags::MaximumCompression => "Maximum compression algorithm (DEFLATE).", ExtraFlags::FastestCompression => "Fastest compression algorithm (DEFLATE)", }.fmt(f) } } impl Default for ExtraFlags { fn default() -> ExtraFlags { ExtraFlags::Default } } /// A builder structure to create a new gzip header. /// /// This structure controls header configuration options such as the filename. #[derive(Debug, Default, Clone, Eq, PartialEq)] pub struct GzBuilder { extra: Option>, filename: Option, comment: Option, // Whether this should be signed is a bit unclear, the gzip spec says mtime is in the unix // time format, which is normally signed, however zlib seems to use an unsigned long for this // field. mtime: u32, os: Option, xfl: ExtraFlags, } impl GzBuilder { /// Create a new blank builder with no header by default. pub fn new() -> GzBuilder { GzBuilder { extra: None, filename: None, comment: None, mtime: 0, os: None, xfl: ExtraFlags::Default, } } /// Configure the `mtime` field in the gzip header. pub fn mtime(mut self, mtime: u32) -> GzBuilder { self.mtime = mtime; self } /// Configure the `extra` field in the gzip header. pub fn extra>>(mut self, extra: T) -> GzBuilder { self.extra = Some(extra.into()); self } /// Configure the `filename` field in the gzip header. /// /// # Panics /// Panics if the filename argument contains a byte with the value 0. pub fn filename>>(mut self, filename: T) -> GzBuilder { self.filename = Some(CString::new(filename).unwrap()); self } /// Configure the `comment` field in the gzip header. /// /// # Panics /// Panics if the comment argument contains a byte with the value 0. pub fn comment>>(mut self, comment: T) -> GzBuilder { self.comment = Some(CString::new(comment).unwrap()); self } /// Configure the `os` field in the gzip header. /// /// This is taken from `std::env::consts::OS` if not set explicitly. pub fn os(mut self, os: FileSystemType) -> GzBuilder { self.os = Some(os); self } /// Configure the `xfl` field in the gzip header. /// /// The default is `ExtraFlags::Default` (meaning not set). pub fn xfl(mut self, xfl: ExtraFlags) -> GzBuilder { self.xfl = xfl; self } /// Transforms this builder structure into a raw vector of bytes, setting the `XFL` field to the /// value specified by `lvl`. pub fn into_header_xfl(mut self, lvl: ExtraFlags) -> Vec { self.xfl = lvl; self.into_header() } /// Transforms this builder structure into a raw vector of bytes. pub fn into_header(self) -> Vec { self.into_header_inner(false) } /// Transforms this builder structure into a raw vector of bytes. pub fn into_header_with_checksum(self) -> Vec { self.into_header_inner(true) } fn into_header_inner(self, use_crc: bool) -> Vec { let GzBuilder { extra, filename, comment, mtime, os, xfl, } = self; let os = match os { Some(f) => f, // Set the OS based on the system the binary is compiled for if not set, // as this is a required field. // These defaults are taken from what modern zlib uses, which are not the same as // what's used in flate2. None => match env::consts::OS { "linux" | "freebsd" | "dragonfly" | "netbsd" | "openbsd" | "solaris" | "bitrig" => { FileSystemType::Unix } "macos" => FileSystemType::Apple, "win32" => FileSystemType::Tops20OrNTFS, _ => FileSystemType::Unknown, }, }; let mut flg = 0; if use_crc { flg |= FHCRC; }; let mut header = vec![0u8; 10]; if let Some(v) = extra { flg |= FEXTRA; header.push((v.len()/* >> 0*/) as u8); header.push((v.len() >> 8) as u8); header.extend(v); } if let Some(filename) = filename { flg |= FNAME; header.extend(filename.as_bytes_with_nul().iter().cloned()); } if let Some(comment) = comment { flg |= FCOMMENT; header.extend(comment.as_bytes_with_nul().iter().cloned()); } header[0] = 0x1f; header[1] = 0x8b; header[2] = 8; header[3] = flg; header[4] = mtime /*>> 0*/ as u8; header[5] = (mtime >> 8) as u8; header[6] = (mtime >> 16) as u8; header[7] = (mtime >> 24) as u8; header[8] = xfl.as_u8(); header[9] = os.as_u8(); if use_crc { let mut crc = Crc::new(); crc.update(&header); let checksum = crc.sum() as u16; header.extend(&[checksum as u8, (checksum >> 8) as u8]); } header } } /// A structure representing the raw header of a gzip stream. /// /// The header can contain metadata about the file that was compressed, if /// present. #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GzHeader { extra: Option>, filename: Option>, comment: Option>, mtime: u32, os: u8, xfl: u8, } impl GzHeader { /// Returns the `filename` field of this gzip header, if present. /// /// The `filename` field the gzip header is supposed to be stored using ISO 8859-1 (LATIN-1) /// encoding and be zero-terminated if following the specification. pub fn filename(&self) -> Option<&[u8]> { self.filename.as_ref().map(|s| &s[..]) } /// Returns the `extra` field of this gzip header, if present. pub fn extra(&self) -> Option<&[u8]> { self.extra.as_ref().map(|s| &s[..]) } /// Returns the `comment` field of this gzip stream's header, if present. /// /// The `comment` field in the gzip header is supposed to be stored using ISO 8859-1 (LATIN-1) /// encoding and be zero-terminated if following the specification. pub fn comment(&self) -> Option<&[u8]> { self.comment.as_ref().map(|s| &s[..]) } /// Returns the `mtime` field of this gzip header. /// /// This gives the most recent modification time of the contained file, or alternatively /// the timestamp of when the file was compressed if the data did not come from a file, or /// a timestamp was not available when compressing. The time is specified the Unix format, /// that is: seconds since 00:00:00 GMT, Jan. 1, 1970. (Not that this may cause problems for /// MS-DOS and other systems that use local rather than Universal time.) /// An `mtime` value of 0 means that the timestamp is not set. pub fn mtime(&self) -> u32 { self.mtime } /// Returns the `mtime` field of this gzip header as a `SystemTime` if present. /// /// Returns `None` if the `mtime` is not set, i.e 0. /// See [`mtime`](#method.mtime) for more detail. pub fn mtime_as_datetime(&self) -> Option { if self.mtime == 0 { None } else { let duration = time::Duration::new(u64::from(self.mtime), 0); let datetime = time::UNIX_EPOCH + duration; Some(datetime) } } /// Returns the `os` field of this gzip stream's header. pub fn os(&self) -> u8 { self.os } /// Returns the `xfl` field of this gzip stream's header. pub fn xfl(&self) -> u8 { self.xfl } } #[inline] fn into_string(data: Option<&[u8]>) -> Cow { data.map_or_else( || Cow::Borrowed("(Not set)"), |d| String::from_utf8_lossy(d), ) } impl fmt::Display for GzHeader { /// Crudely display the contents of the header /// /// Note that filename/commend are required to be ISO 8859-1 (LATIN-1) encoded by the spec, /// however to avoid dragging in dependencies we simply interpret them as UTF-8. /// This may result in garbled output if the names contain special characters. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "Filename: {}\n\ Comment: {}\n\ Extra: {:?}\n\ mtime: {}\n\ os: {}\n\ xfl: {}", into_string(self.filename()), into_string(self.comment()), // We display extra as raw bytes for now. self.extra, self.mtime, FileSystemType::from_u8(self.os), ExtraFlags::Default, //ExtraFlags::from_u8(self.xfl), ) } } fn corrupt() -> io::Error { io::Error::new( io::ErrorKind::InvalidInput, "corrupt gzip stream does not have a matching header checksum", ) } fn bad_header() -> io::Error { io::Error::new(io::ErrorKind::InvalidInput, "invalid gzip header") } /// Try to read a little-endian u16 from the provided reader. fn read_le_u16(r: &mut R) -> io::Result { let mut b = [0; 2]; try!(r.read_exact(&mut b)); Ok((b[0] as u16) | ((b[1] as u16) << 8)) } /// Try to read a gzip header from the provided reader. /// /// Returns a `GzHeader` with the fields filled out if sucessful, or an `io::Error` with /// `ErrorKind::InvalidInput` if decoding of the header. /// /// Note that a gzip steam can contain multiple "members". Each member contains a header, /// followed by compressed data and finally a checksum and byte count. /// This method will only read the header for the "member" at the start of the stream. pub fn read_gz_header(r: &mut R) -> io::Result { let mut crc_reader = CrcReader::new(r); let mut header = [0; 10]; try!(crc_reader.read_exact(&mut header)); // `ID1` and `ID2` are fixed values to identify a gzip file. let id1 = header[0]; let id2 = header[1]; if id1 != 0x1f || id2 != 0x8b { return Err(bad_header()); } // `CM` describes the compression method. Currently only method 8 (DEFLATE) is specified. // by the gzip format. let cm = header[2]; if cm != 8 { return Err(bad_header()); } // `FLG` the bits in this field indicates whether the `FTEXT`, `FHCRC`, `FEXTRA`, `FNAME` and // `FCOMMENT` fields are present in the header. let flg = header[3]; let mtime = (header[4] as u32/* << 0*/) | ((header[5] as u32) << 8) | ((header[6] as u32) << 16) | ((header[7] as u32) << 24); // `XFL` describes the compression level used by the encoder. (May not actually // match what the encoder used and has no impact on decompression.) let xfl = header[8]; // `os` describes what type of operating system/file system the file was created on. let os = header[9]; let extra = if flg & FEXTRA != 0 { // Length of the FEXTRA field. let xlen = try!(read_le_u16(&mut crc_reader)); let mut extra = vec![0; xlen as usize]; try!(crc_reader.read_exact(&mut extra)); Some(extra) } else { None }; let filename = if flg & FNAME != 0 { // wow this is slow let mut b = Vec::new(); for byte in crc_reader.by_ref().bytes() { let byte = try!(byte); if byte == 0 { break; } b.push(byte); } Some(b) } else { None }; let comment = if flg & FCOMMENT != 0 { // wow this is slow let mut b = Vec::new(); for byte in crc_reader.by_ref().bytes() { let byte = try!(byte); if byte == 0 { break; } b.push(byte); } Some(b) } else { None }; // If the `FHCRC` flag is set, the header contains a two-byte CRC16 checksum of the header bytes // that needs to be validated. if flg & FHCRC != 0 { let calced_crc = crc_reader.crc().sum() as u16; let stored_crc = try!(read_le_u16(&mut crc_reader)); if calced_crc != stored_crc { return Err(corrupt()); } } Ok(GzHeader { extra: extra, filename: filename, comment: comment, mtime: mtime, os: os, xfl: xfl, }) } #[cfg(test)] mod tests { use super::*; use std::io::Cursor; fn roundtrip_inner(use_crc: bool) { const COMMENT: &'static [u8] = b"Comment"; const FILENAME: &'static [u8] = b"Filename"; const MTIME: u32 = 12345; const OS: FileSystemType = FileSystemType::NTFS; const XFL: ExtraFlags = ExtraFlags::FastestCompression; let header = GzBuilder::new() .comment(COMMENT) .filename(FILENAME) .mtime(MTIME) .os(OS) .xfl(ExtraFlags::FastestCompression) .into_header_inner(use_crc); let mut reader = Cursor::new(header.clone()); let header_read = read_gz_header(&mut reader).unwrap(); assert_eq!(header_read.comment().unwrap(), COMMENT); assert_eq!(header_read.filename().unwrap(), FILENAME); assert_eq!(header_read.mtime(), MTIME); assert_eq!(header_read.os(), OS.as_u8()); assert_eq!(header_read.xfl(), XFL.as_u8()); } #[test] fn roundtrip() { roundtrip_inner(false); } #[test] fn roundtrip_with_crc() { roundtrip_inner(true); } #[test] fn filesystem_enum() { for n in 0..20 { assert_eq!(n, FileSystemType::from_u8(n).as_u8()); } for n in 20..(u8::max_value() as u16) + 1 { assert_eq!(FileSystemType::from_u8(n as u8), FileSystemType::Unknown); } } } gzip-header-0.3.0/.cargo_vcs_info.json0000644000000001120000000000000132640ustar00{ "git": { "sha1": "9da7d36b2ad0654570e19b195f9d3abc9ca2efa4" } }