diff options
author | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
---|---|---|
committer | Anas Nashif <anas.nashif@intel.com> | 2012-10-30 12:57:26 -0700 |
commit | 1a78a62555be32868418fe52f8e330c9d0f95d5a (patch) | |
tree | d3765a80e7d3b9640ec2e930743630cd6b9fce2b /boost/iostreams/filter/gzip.hpp | |
download | boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.gz boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.tar.bz2 boost-1a78a62555be32868418fe52f8e330c9d0f95d5a.zip |
Imported Upstream version 1.49.0upstream/1.49.0
Diffstat (limited to 'boost/iostreams/filter/gzip.hpp')
-rw-r--r-- | boost/iostreams/filter/gzip.hpp | 757 |
1 files changed, 757 insertions, 0 deletions
diff --git a/boost/iostreams/filter/gzip.hpp b/boost/iostreams/filter/gzip.hpp new file mode 100644 index 0000000000..0f483b4463 --- /dev/null +++ b/boost/iostreams/filter/gzip.hpp @@ -0,0 +1,757 @@ +// (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com) +// (C) Copyright 2003-2007 Jonathan Turkanis +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.) + +// See http://www.boost.org/libs/iostreams for documentation. + +// Contains the definitions of the class templates gzip_compressor and +// gzip_decompressor for reading and writing files in the gzip file format +// (RFC 1952). Based in part on work of Jonathan de Halleux; see [...] + +#ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED +#define BOOST_IOSTREAMS_GZIP_HPP_INCLUDED + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +# pragma once +#endif + +#include <boost/config.hpp> // STATIC_CONSTANT, STDC_NAMESPACE, + // DINKUMWARE_STDLIB, __STL_CONFIG_H. +#include <algorithm> // min. +#include <boost/assert.hpp> +#include <cstdio> // EOF. +#include <cstddef> // size_t. +#include <ctime> // std::time_t. +#include <memory> // allocator. +#include <boost/config.hpp> // Put size_t in std. +#include <boost/detail/workaround.hpp> +#include <boost/cstdint.hpp> // uint8_t, uint32_t. +#include <boost/iostreams/constants.hpp> // buffer size. +#include <boost/iostreams/detail/adapter/non_blocking_adapter.hpp> +#include <boost/iostreams/detail/adapter/range_adapter.hpp> +#include <boost/iostreams/detail/char_traits.hpp> +#include <boost/iostreams/detail/ios.hpp> // failure. +#include <boost/iostreams/detail/error.hpp> +#include <boost/iostreams/operations.hpp> +#include <boost/iostreams/device/back_inserter.hpp> +#include <boost/iostreams/filter/zlib.hpp> +#include <boost/iostreams/pipeline.hpp> +#include <boost/iostreams/putback.hpp> +#include <boost/throw_exception.hpp> + +// Must come last. +#if defined(BOOST_MSVC) +# pragma warning(push) +# pragma warning(disable: 4309) // Truncation of constant value. +#endif + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::time_t; } +#endif + +namespace boost { namespace iostreams { + +//------------------Definitions of constants----------------------------------// + +namespace gzip { + +using namespace boost::iostreams::zlib; + + // Error codes used by gzip_error. + +const int zlib_error = 1; +const int bad_crc = 2; // Recorded crc doesn't match data. +const int bad_length = 3; // Recorded length doesn't match data. +const int bad_header = 4; // Malformed header. +const int bad_footer = 5; // Malformed footer. +const int bad_method = 6; // Unsupported compression method. + +namespace magic { + + // Magic numbers used by gzip header. + +const int id1 = 0x1f; +const int id2 = 0x8b; + +} // End namespace magic. + +namespace method { + + // Codes used for the 'CM' byte of the gzip header. + +const int deflate = 8; + +} // End namespace method. + +namespace flags { + + // Codes used for the 'FLG' byte of the gzip header. + +const int text = 1; +const int header_crc = 2; +const int extra = 4; +const int name = 8; +const int comment = 16; + +} // End namespace flags. + +namespace extra_flags { + + // Codes used for the 'XFL' byte of the gzip header. + +const int best_compression = 2; +const int best_speed = 4; + +} // End namespace extra_flags. + + // Codes used for the 'OS' byte of the gzip header. + +const int os_fat = 0; +const int os_amiga = 1; +const int os_vms = 2; +const int os_unix = 3; +const int os_vm_cms = 4; +const int os_atari = 5; +const int os_hpfs = 6; +const int os_macintosh = 7; +const int os_z_system = 8; +const int os_cp_m = 9; +const int os_tops_20 = 10; +const int os_ntfs = 11; +const int os_qdos = 12; +const int os_acorn = 13; +const int os_unknown = 255; + +} // End namespace gzip. + +//------------------Definition of gzip_params---------------------------------// + +// +// Class name: gzip_params. +// Description: Subclass of zlib_params with an additional field +// representing a file name. +// +struct gzip_params : zlib_params { + + // Non-explicit constructor. + gzip_params( int level = gzip::default_compression, + int method = gzip::deflated, + int window_bits = gzip::default_window_bits, + int mem_level = gzip::default_mem_level, + int strategy = gzip::default_strategy, + std::string file_name = "", + std::string comment = "", + std::time_t mtime = 0 ) + : zlib_params(level, method, window_bits, mem_level, strategy), + file_name(file_name), comment(comment), mtime(mtime) + { } + std::string file_name; + std::string comment; + std::time_t mtime; +}; + +//------------------Definition of gzip_error----------------------------------// + +// +// Class name: gzip_error. +// Description: Subclass of std::ios_base::failure thrown to indicate +// zlib errors other than out-of-memory conditions. +// +class gzip_error : public BOOST_IOSTREAMS_FAILURE { +public: + explicit gzip_error(int error) + : BOOST_IOSTREAMS_FAILURE("gzip error"), + error_(error), zlib_error_code_(zlib::okay) { } + explicit gzip_error(const zlib_error& e) + : BOOST_IOSTREAMS_FAILURE("gzip error"), + error_(gzip::zlib_error), zlib_error_code_(e.error()) + { } + int error() const { return error_; } + int zlib_error_code() const { return zlib_error_code_; } +private: + int error_; + int zlib_error_code_; +}; + +//------------------Definition of gzip_compressor-----------------------------// + +// +// Template name: gzip_compressor +// Description: Model of OutputFilter implementing compression in the +// gzip format. +// +template<typename Alloc = std::allocator<char> > +class basic_gzip_compressor : basic_zlib_compressor<Alloc> { +private: + typedef basic_zlib_compressor<Alloc> base_type; +public: + typedef char char_type; + struct category + : dual_use, + filter_tag, + multichar_tag, + closable_tag + { }; + basic_gzip_compressor( const gzip_params& = gzip::default_compression, + int buffer_size = default_device_buffer_size ); + + template<typename Source> + std::streamsize read(Source& src, char_type* s, std::streamsize n) + { + std::streamsize result = 0; + + // Read header. + if (!(flags_ & f_header_done)) + result += read_string(s, n, header_); + + // Read body. + if (!(flags_ & f_body_done)) { + + // Read from basic_zlib_filter. + std::streamsize amt = base_type::read(src, s + result, n - result); + if (amt != -1) { + result += amt; + if (amt < n - result) { // Double-check for EOF. + amt = base_type::read(src, s + result, n - result); + if (amt != -1) + result += amt; + } + } + if (amt == -1) + prepare_footer(); + } + + // Read footer. + if ((flags_ & f_body_done) != 0 && result < n) + result += read_string(s + result, n - result, footer_); + + return result != 0 ? result : -1; + } + + template<typename Sink> + std::streamsize write(Sink& snk, const char_type* s, std::streamsize n) + { + if (!(flags_ & f_header_done)) { + std::streamsize amt = + static_cast<std::streamsize>(header_.size() - offset_); + offset_ += boost::iostreams::write(snk, header_.data() + offset_, amt); + if (offset_ == header_.size()) + flags_ |= f_header_done; + else + return 0; + } + return base_type::write(snk, s, n); + } + + template<typename Sink> + void close(Sink& snk, BOOST_IOS::openmode m) + { + try { + // Close zlib compressor. + base_type::close(snk, m); + + if (m == BOOST_IOS::out) { + if (flags_ & f_header_done) { + + // Write final fields of gzip file format. + write_long(this->crc(), snk); + write_long(this->total_in(), snk); + } + } + } catch(...) { + close_impl(); + throw; + } + close_impl(); + } +private: + static gzip_params normalize_params(gzip_params p); + void prepare_footer(); + std::streamsize read_string(char* s, std::streamsize n, std::string& str); + + template<typename Sink> + static void write_long(long n, Sink& next, boost::mpl::true_) + { + boost::iostreams::put(next, static_cast<char>(0xFF & n)); + boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 8))); + boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 16))); + boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 24))); + } + template<typename Sink> + static void write_long(long n, Sink& next, boost::mpl::false_) + { + } + template<typename Sink> + static void write_long(long n, Sink& next) + { + typedef typename category_of<Sink>::type category; + typedef is_convertible<category, output> can_write; + write_long(n, next, can_write()); + } + + void close_impl() + { + #if BOOST_WORKAROUND(__GNUC__, == 2) && defined(__STL_CONFIG_H) || \ + BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1) \ + /**/ + footer_.erase(0, std::string::npos); + #else + footer_.clear(); + #endif + offset_ = 0; + flags_ = 0; + } + + enum state_type { + f_header_done = 1, + f_body_done = f_header_done << 1, + f_footer_done = f_body_done << 1 + }; + std::string header_; + std::string footer_; + std::size_t offset_; + int flags_; +}; +BOOST_IOSTREAMS_PIPABLE(basic_gzip_compressor, 1) + +typedef basic_gzip_compressor<> gzip_compressor; + +//------------------Definition of helper templates for decompression----------// + +namespace detail { + +// Processes gzip headers +class BOOST_IOSTREAMS_DECL gzip_header { +public: + gzip_header() { reset(); } + + // Members for processing header data + void process(char c); + bool done() const { return state_ == s_done; } + void reset(); + + // Members for accessing header data + std::string file_name() const { return file_name_; } + std::string comment() const { return comment_; } + bool text() const { return (flags_ & gzip::flags::text) != 0; } + int os() const { return os_; } + std::time_t mtime() const { return mtime_; } +private: + enum state_type { + s_id1 = 1, + s_id2 = s_id1 + 1, + s_cm = s_id2 + 1, + s_flg = s_cm + 1, + s_mtime = s_flg + 1, + s_xfl = s_mtime + 1, + s_os = s_xfl + 1, + s_xlen = s_os + 1, + s_extra = s_xlen + 1, + s_name = s_extra + 1, + s_comment = s_name + 1, + s_hcrc = s_comment + 1, + s_done = s_hcrc + 1 + }; + std::string file_name_; + std::string comment_; + int os_; + std::time_t mtime_; + int flags_; + int state_; + int offset_; // Offset within fixed-length region. + int xlen_; // Bytes remaining in extra field. +}; + +// Processes gzip footers +class BOOST_IOSTREAMS_DECL gzip_footer { +public: + gzip_footer() { reset(); } + + // Members for processing footer data + void process(char c); + bool done() const { return state_ == s_done; } + void reset(); + + // Members for accessing footer data + zlib::ulong crc() const { return crc_; } + zlib::ulong uncompressed_size() const { return isize_; } +private: + enum state_type { + s_crc = 1, + s_isize = s_crc + 1, + s_done = s_isize + 1 + }; + zlib::ulong crc_; + zlib::ulong isize_; + int state_; + int offset_; +}; + +} // End namespace boost::iostreams::detail. + +//------------------Definition of basic_gzip_decompressor---------------------// + +// +// Template name: basic_gzip_decompressor +// Description: Model of InputFilter implementing compression in the +// gzip format. +// +template<typename Alloc = std::allocator<char> > +class basic_gzip_decompressor : basic_zlib_decompressor<Alloc> { +private: + typedef basic_zlib_decompressor<Alloc> base_type; + typedef typename base_type::string_type string_type; +public: + typedef char char_type; + struct category + : dual_use, + filter_tag, + multichar_tag, + closable_tag + { }; + basic_gzip_decompressor( int window_bits = gzip::default_window_bits, + int buffer_size = default_device_buffer_size ); + + template<typename Sink> + std::streamsize write(Sink& snk, const char_type* s, std::streamsize n) + { + std::streamsize result = 0; + while(result < n) { + if(state_ == s_start) { + state_ = s_header; + header_.reset(); + footer_.reset(); + } + if (state_ == s_header) { + int c = s[result++]; + header_.process(c); + if (header_.done()) + state_ = s_body; + } else if (state_ == s_body) { + try { + std::streamsize amt = + base_type::write(snk, s + result, n - result); + result += amt; + if (!this->eof()) { + break; + } else { + state_ = s_footer; + } + } catch (const zlib_error& e) { + boost::throw_exception(gzip_error(e)); + } + } else { // state_ == s_footer + if (footer_.done()) { + if (footer_.crc() != this->crc()) + boost::throw_exception(gzip_error(gzip::bad_crc)); + + base_type::close(snk, BOOST_IOS::out); + state_ = s_start; + } else { + int c = s[result++]; + footer_.process(c); + } + } + } + return result; + } + + template<typename Source> + std::streamsize read(Source& src, char_type* s, std::streamsize n) + { + typedef char_traits<char> traits_type; + std::streamsize result = 0; + peekable_source<Source> peek(src, putback_); + while (result < n && state_ != s_done) { + if (state_ == s_start) { + state_ = s_header; + header_.reset(); + footer_.reset(); + } + if (state_ == s_header) { + int c = boost::iostreams::get(peek); + if (traits_type::is_eof(c)) { + boost::throw_exception(gzip_error(gzip::bad_header)); + } else if (traits_type::would_block(c)) { + break; + } + header_.process(c); + if (header_.done()) + state_ = s_body; + } else if (state_ == s_body) { + try { + std::streamsize amt = + base_type::read(peek, s + result, n - result); + if (amt != -1) { + result += amt; + if (amt < n - result) + break; + } else { + peek.putback(this->unconsumed_input()); + state_ = s_footer; + } + } catch (const zlib_error& e) { + boost::throw_exception(gzip_error(e)); + } + } else { // state_ == s_footer + int c = boost::iostreams::get(peek); + if (traits_type::is_eof(c)) { + boost::throw_exception(gzip_error(gzip::bad_footer)); + } else if (traits_type::would_block(c)) { + break; + } + footer_.process(c); + if (footer_.done()) { + if (footer_.crc() != this->crc()) + boost::throw_exception(gzip_error(gzip::bad_crc)); + int c = boost::iostreams::get(peek); + if (traits_type::is_eof(c)) { + state_ = s_done; + } else { + peek.putback(c); + base_type::close(peek, BOOST_IOS::in); + state_ = s_start; + header_.reset(); + footer_.reset(); + } + } + } + } + if (peek.has_unconsumed_input()) { + putback_ = peek.unconsumed_input(); + } else { + putback_.clear(); + } + return result != 0 || state_ != s_done ? + result : + -1; + } + + template<typename Source> + void close(Source& src, BOOST_IOS::openmode m) + { + try { + base_type::close(src, m); + } catch (const zlib_error& e) { + state_ = s_start; + boost::throw_exception(gzip_error(e)); + } + if (m == BOOST_IOS::out) { + if (state_ == s_start || state_ == s_header) + boost::throw_exception(gzip_error(gzip::bad_header)); + else if (state_ == s_body) + boost::throw_exception(gzip_error(gzip::bad_footer)); + else if (state_ == s_footer) { + if (!footer_.done()) + boost::throw_exception(gzip_error(gzip::bad_footer)); + else if(footer_.crc() != this->crc()) + boost::throw_exception(gzip_error(gzip::bad_crc)); + } else { + BOOST_ASSERT(!"Bad state"); + } + } + state_ = s_start; + } + + std::string file_name() const { return header_.file_name(); } + std::string comment() const { return header_.comment(); } + bool text() const { return header_.text(); } + int os() const { return header_.os(); } + std::time_t mtime() const { return header_.mtime(); } +private: + static gzip_params make_params(int window_bits); + + // Source adapter allowing an arbitrary character sequence to be put back. + template<typename Source> + struct peekable_source { + typedef char char_type; + struct category : source_tag, peekable_tag { }; + explicit peekable_source(Source& src, const string_type& putback = "") + : src_(src), putback_(putback), offset_(0) + { } + std::streamsize read(char* s, std::streamsize n) + { + std::streamsize result = 0; + + // Copy characters from putback buffer + std::streamsize pbsize = + static_cast<std::streamsize>(putback_.size()); + if (offset_ < pbsize) { + result = (std::min)(n, pbsize - offset_); + BOOST_IOSTREAMS_CHAR_TRAITS(char)::copy( + s, putback_.data() + offset_, result); + offset_ += result; + if (result == n) + return result; + } + + // Read characters from src_ + std::streamsize amt = + boost::iostreams::read(src_, s + result, n - result); + return amt != -1 ? + result + amt : + result ? result : -1; + } + bool putback(char c) + { + if (offset_) { + putback_[--offset_] = c; + } else { + boost::throw_exception( + boost::iostreams::detail::bad_putback()); + } + return true; + } + void putback(const string_type& s) + { + putback_.replace(0, offset_, s); + offset_ = 0; + } + + // Returns true if some characters have been putback but not re-read. + bool has_unconsumed_input() const + { + return offset_ < static_cast<std::streamsize>(putback_.size()); + } + + // Returns the sequence of characters that have been put back but not re-read. + string_type unconsumed_input() const + { + return string_type(putback_, offset_, putback_.size() - offset_); + } + Source& src_; + string_type putback_; + std::streamsize offset_; + }; + + enum state_type { + s_start = 1, + s_header = s_start + 1, + s_body = s_header + 1, + s_footer = s_body + 1, + s_done = s_footer + 1 + }; + detail::gzip_header header_; + detail::gzip_footer footer_; + string_type putback_; + int state_; +}; +BOOST_IOSTREAMS_PIPABLE(basic_gzip_decompressor, 1) + +typedef basic_gzip_decompressor<> gzip_decompressor; + +//------------------Implementation of gzip_compressor-------------------------// + +template<typename Alloc> +basic_gzip_compressor<Alloc>::basic_gzip_compressor + (const gzip_params& p, int buffer_size) + : base_type(normalize_params(p), buffer_size), + offset_(0), flags_(0) +{ + // Calculate gzip header. + bool has_name = !p.file_name.empty(); + bool has_comment = !p.comment.empty(); + + std::string::size_type length = + 10 + + (has_name ? p.file_name.size() + 1 : 0) + + (has_comment ? p.comment.size() + 1 : 0); + // + 2; // Header crc confuses gunzip. + int flags = + //gzip::flags::header_crc + + (has_name ? gzip::flags::name : 0) + + (has_comment ? gzip::flags::comment : 0); + int extra_flags = + ( p.level == zlib::best_compression ? + gzip::extra_flags::best_compression : + 0 ) + + ( p.level == zlib::best_speed ? + gzip::extra_flags::best_speed : + 0 ); + header_.reserve(length); + header_ += gzip::magic::id1; // ID1. + header_ += gzip::magic::id2; // ID2. + header_ += gzip::method::deflate; // CM. + header_ += static_cast<char>(flags); // FLG. + header_ += static_cast<char>(0xFF & p.mtime); // MTIME. + header_ += static_cast<char>(0xFF & (p.mtime >> 8)); + header_ += static_cast<char>(0xFF & (p.mtime >> 16)); + header_ += static_cast<char>(0xFF & (p.mtime >> 24)); + header_ += static_cast<char>(extra_flags); // XFL. + header_ += static_cast<char>(gzip::os_unknown); // OS. + if (has_name) { + header_ += p.file_name; + header_ += '\0'; + } + if (has_comment) { + header_ += p.comment; + header_ += '\0'; + } +} + +template<typename Alloc> +gzip_params basic_gzip_compressor<Alloc>::normalize_params(gzip_params p) +{ + p.noheader = true; + p.calculate_crc = true; + return p; +} + +template<typename Alloc> +void basic_gzip_compressor<Alloc>::prepare_footer() +{ + boost::iostreams::back_insert_device<std::string> out(footer_); + write_long(this->crc(), out); + write_long(this->total_in(), out); + flags_ |= f_body_done; + offset_ = 0; +} + +template<typename Alloc> +std::streamsize basic_gzip_compressor<Alloc>::read_string + (char* s, std::streamsize n, std::string& str) +{ + std::streamsize avail = + static_cast<std::streamsize>(str.size() - offset_); + std::streamsize amt = (std::min)(avail, n); + std::copy( str.data() + offset_, + str.data() + offset_ + amt, + s ); + offset_ += amt; + if ( !(flags_ & f_header_done) && + offset_ == static_cast<std::size_t>(str.size()) ) + { + flags_ |= f_header_done; + } + return amt; +} + +//------------------Implementation of gzip_decompressor-----------------------// + +template<typename Alloc> +basic_gzip_decompressor<Alloc>::basic_gzip_decompressor + (int window_bits, int buffer_size) + : base_type(make_params(window_bits), buffer_size), + state_(s_start) + { } + +template<typename Alloc> +gzip_params basic_gzip_decompressor<Alloc>::make_params(int window_bits) +{ + gzip_params p; + p.window_bits = window_bits; + p.noheader = true; + p.calculate_crc = true; + return p; +} + +//----------------------------------------------------------------------------// + +} } // End namespaces iostreams, boost. + +#if defined(BOOST_MSVC) +# pragma warning(pop) +#endif + +#endif // #ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED |