source: NonGTP/Boost/boost/iostreams/filter/gzip.hpp @ 857

Revision 857, 19.3 KB checked in by igarcia, 18 years ago (diff)
Line 
1// (C) Copyright Jonathan Turkanis 2003.
2// Distributed under the Boost Software License, Version 1.0. (See accompanying
3// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
4
5// See http://www.boost.org/libs/iostreams for documentation.
6
7// Contains the definitions of the class templates gzip_compressor and
8// gzip_decompressor for reading and writing files in the gzip file format
9// (RFC 1952). Based in part on work of Jonathan de Halleux; see [...]
10
11#ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED
12#define BOOST_IOSTREAMS_GZIP_HPP_INCLUDED
13
14#if defined(_MSC_VER) && (_MSC_VER >= 1020)
15# pragma once
16#endif
17
18#include <boost/config.hpp> // STATIC_CONSTANT, STDC_NAMESPACE,
19                            // DINKUMWARE_STDLIB, __STL_CONFIG_H.
20#include <algorithm>                      // min.
21#include <cstdio>                         // EOF.
22#include <cstddef>                        // size_t.
23#include <ctime>                          // std::time_t.
24#include <memory>                         // allocator.
25#include <boost/config.hpp>               // Put size_t in std.
26#include <boost/detail/workaround.hpp>
27#include <boost/cstdint.hpp>              // uint8_t, uint32_t.
28#include <boost/iostreams/constants.hpp>  // buffer size.
29#include <boost/iostreams/detail/adapter/non_blocking_adapter.hpp>
30#include <boost/iostreams/detail/adapter/range_adapter.hpp>
31#include <boost/iostreams/detail/char_traits.hpp>
32#include <boost/iostreams/detail/ios.hpp> // failure.
33#include <boost/iostreams/operations.hpp>
34#include <boost/iostreams/device/back_inserter.hpp>
35#include <boost/iostreams/filter/zlib.hpp>
36#include <boost/iostreams/pipeline.hpp>         
37
38// Must come last.
39#if defined(BOOST_MSVC)
40# pragma warning(push)
41# pragma warning(disable: 4309)    // Truncation of constant value.
42#endif
43
44#ifdef BOOST_NO_STDC_NAMESPACE
45namespace std { using ::time_t; }
46#endif
47
48namespace boost { namespace iostreams {
49
50namespace gzip {
51
52using namespace boost::iostreams::zlib;
53
54    // Error codes used by gzip_error.
55
56const int zlib_error        = 1;
57const int bad_crc           = 2; // Recorded crc doesn't match data.
58const int bad_length        = 3; // Recorded length doesn't match data.
59const int bad_header        = 4; // Malformed header.
60const int bad_footer        = 5; // Malformed footer.
61
62namespace magic {
63
64    // Magic numbers used by gzip header.
65
66const int id1               = 0x1f;
67const int id2               = 0x8b;
68
69} // End namespace magic.
70
71namespace method {
72
73    // Codes used for the 'CM' byte of the gzip header.
74
75const int deflate           = 8;
76
77} // End namespace method.
78
79namespace flags {
80
81    // Codes used for the 'FLG' byte of the gzip header.
82
83const int text              = 1;
84const int header_crc        = 2;
85const int extra             = 4;
86const int name              = 8;
87const int comment           = 16;
88
89} // End namespace flags.
90
91namespace extra_flags {
92
93    // Codes used for the 'XFL' byte of the gzip header.
94
95const int best_compression  = 2;
96const int best_speed        = 4;
97
98} // End namespace extra_flags.
99
100    // Codes used for the 'OS' byte of the gzip header.
101
102const int os_fat            = 0;
103const int os_amiga          = 1;
104const int os_vms            = 2;
105const int os_unix           = 3;
106const int os_vm_cms         = 4;
107const int os_atari          = 5;
108const int os_hpfs           = 6;
109const int os_macintosh      = 7;
110const int os_z_system       = 8;
111const int os_cp_m           = 9;
112const int os_tops_20        = 10;
113const int os_ntfs           = 11;
114const int os_qdos           = 12;
115const int os_acorn          = 13;
116const int os_unknown        = 255;
117
118} // End namespace gzip.
119
120//
121// Class name: gzip_params.
122// Description: Subclass of zlib_params with an additional field
123//      representing a file name.
124//
125struct gzip_params : zlib_params {
126
127    // Non-explicit constructor.
128    gzip_params( int level              = gzip::default_compression,
129                 int method             = gzip::deflated,
130                 int window_bits        = gzip::default_window_bits,
131                 int mem_level          = gzip::default_mem_level,
132                 int strategy           = gzip::default_strategy,
133                 std::string file_name  = "",
134                 std::string comment    = "",
135                 std::time_t mtime      = 0 )
136        : zlib_params(level, method, window_bits, mem_level, strategy),
137          file_name(file_name), mtime(mtime)
138        { }
139    std::string  file_name;
140    std::string  comment;
141    std::time_t  mtime;
142};
143
144//
145// Class name: gzip_error.
146// Description: Subclass of std::ios_base::failure thrown to indicate
147//     zlib errors other than out-of-memory conditions.
148//
149class gzip_error : public BOOST_IOSTREAMS_FAILURE {
150public:
151    explicit gzip_error(int error)
152        : BOOST_IOSTREAMS_FAILURE("gzip error"),
153          error_(error), zlib_error_code_(zlib::okay) { }
154    explicit gzip_error(const zlib_error& e)
155        : BOOST_IOSTREAMS_FAILURE("gzip error"),
156          error_(gzip::zlib_error), zlib_error_code_(e.error())
157        { }
158    int error() const { return error_; }
159    int zlib_error_code() const { return zlib_error_code_; }
160private:
161    int error_;
162    int zlib_error_code_;
163};
164
165//
166// Template name: gzip_compressor
167// Description: Model of OutputFilter implementing compression in the
168//      gzip format.
169//
170template<typename Alloc = std::allocator<char> >
171class basic_gzip_compressor : basic_zlib_compressor<Alloc> {
172private:
173    typedef basic_zlib_compressor<Alloc>  base_type;
174public:
175    typedef char char_type;
176    struct category
177        : dual_use,
178          filter_tag,
179          multichar_tag,
180          closable_tag
181        { };
182    basic_gzip_compressor( const gzip_params& = gzip::default_compression,
183                           int buffer_size = default_device_buffer_size );
184
185    template<typename Source>
186    std::streamsize read(Source& src, char_type* s, std::streamsize n)
187    {
188        using namespace std;
189        streamsize result = 0;
190
191        // Read header.
192        if (!(flags_ & f_header_done))
193            result += read_string(s, n, header_);
194
195        // Read body.
196        if (!(flags_ & f_body_done)) {
197
198            // Read from basic_zlib_filter.
199            streamsize amt = base_type::read(src, s + result, n - result);
200            if (amt != -1) {
201                result += amt;
202                if (amt < n - result) { // Double-check for EOF.
203                    amt = base_type::read(src, s + result, n - result);
204                    if (amt != -1)
205                        result += amt;
206                }
207            }
208            if (amt == -1)
209                prepare_footer();
210        }
211
212        // Read footer.
213        if ((flags_ & f_body_done) != 0 && result < n)
214            result += read_string(s + result, n - result, footer_);
215
216        return result != 0 ? result : -1;
217    }
218
219    template<typename Sink>
220    std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
221    {
222        if (!(flags_ & f_header_done)) {
223            std::streamsize amt =
224                static_cast<std::streamsize>(header_.size() - offset_);
225            offset_ += boost::iostreams::write(snk, header_.data() + offset_, amt);
226            if (offset_ == header_.size())
227                flags_ |= f_header_done;
228            else
229                return 0;
230        }
231        return base_type::write(snk, s, n);
232    }
233
234    template<typename Sink>
235    void close(Sink& snk, BOOST_IOS::openmode m)
236    {
237        namespace io = boost::iostreams;
238
239        if (m & BOOST_IOS::out) {
240
241                // Close zlib compressor.
242                base_type::close(snk, BOOST_IOS::out);
243
244            if (flags_ & f_header_done) {
245
246                // Write final fields of gzip file format.
247                write_long(this->crc(), snk);
248                write_long(this->total_in(), snk);
249            }
250
251        }
252        #if BOOST_WORKAROUND(__GNUC__, == 2) && defined(__STL_CONFIG_H) || \
253            BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1) \
254            /**/
255            footer_.erase(0, std::string::npos);
256        #else
257            footer_.clear();
258        #endif
259        offset_ = 0;
260        flags_ = 0;
261    }
262private:
263    static gzip_params normalize_params(gzip_params p);
264    void prepare_footer();
265    std::streamsize read_string(char* s, std::streamsize n, std::string& str);
266
267    template<typename Sink>
268    static void write_long(long n, Sink& next)
269    {
270        boost::iostreams::put(next, static_cast<char>(0xFF & n));
271        boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 8)));
272        boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 16)));
273        boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 24)));
274    }
275
276    enum flag_type {
277        f_header_done = 1,
278        f_body_done = f_header_done << 1,
279        f_footer_done = f_body_done << 1
280    };
281    std::string  header_;
282    std::string  footer_;
283    std::size_t  offset_;
284    int          flags_;
285};
286BOOST_IOSTREAMS_PIPABLE(basic_gzip_compressor, 1)
287
288typedef basic_gzip_compressor<> gzip_compressor;
289
290//
291// Template name: basic_gzip_decompressor
292// Description: Model of InputFilter implementing compression in the
293//      gzip format.
294//
295template<typename Alloc = std::allocator<char> >
296class basic_gzip_decompressor : basic_zlib_decompressor<Alloc> {
297public:
298    typedef char char_type;
299    struct category
300        : multichar_input_filter_tag,
301          closable_tag
302        { };
303    basic_gzip_decompressor( int window_bits = gzip::default_window_bits,
304                             int buffer_size = default_device_buffer_size );
305
306    template<typename Source>
307    std::streamsize read(Source& src, char_type* s, std::streamsize n)
308    {
309        if ((flags_ & f_header_read) == 0) {
310            non_blocking_adapter<Source> nb(src);
311            read_header(nb);
312            flags_ |= f_header_read;
313        }
314
315        if ((flags_ & f_footer_read) != 0)
316            return -1;
317       
318        try {
319            std::streamsize result = 0;
320            std::streamsize amt;
321            if ((amt = base_type::read(src, s, n)) != -1) {
322                result += amt;
323                if (amt < n) { // Double check for EOF.
324                    amt = base_type::read(src, s + result, n - result);
325                    if (amt != -1)
326                        result += amt;
327                }
328            }
329            if (amt == -1) {
330                non_blocking_adapter<Source> nb(src);
331                read_footer(nb);
332                flags_ |= f_footer_read;
333            }
334            return result;
335        } catch (const zlib_error& e) {
336            throw gzip_error(e);
337        }
338    }
339
340    template<typename Source>
341    void close(Source& src)
342    {
343        try {
344            base_type::close(src, BOOST_IOS::in);
345            flags_ = 0;
346        } catch (const zlib_error& e) {
347            throw gzip_error(e);
348        }
349    }
350
351    std::string file_name() const { return file_name_; }
352    std::string comment() const { return comment_; }
353    bool text() const { return (flags_ & gzip::flags::text) != 0; }
354    int os() const { return os_; }
355    std::time_t mtime() const { return mtime_; }
356private:
357    typedef basic_zlib_decompressor<Alloc>     base_type;
358    typedef BOOST_IOSTREAMS_CHAR_TRAITS(char)  traits_type;
359    static bool is_eof(int c) { return traits_type::eq_int_type(c, EOF); }
360    static gzip_params make_params(int window_bits);
361
362    template<typename Source>
363    static uint8_t read_uint8(Source& src, int error)
364     {
365        int c;
366        if ((c = boost::iostreams::get(src)) == EOF || c == WOULD_BLOCK)
367            throw gzip_error(error);
368        return static_cast<uint8_t>(traits_type::to_char_type(c));
369    }
370
371    template<typename Source>
372    static uint32_t read_uint32(Source& src, int error)
373    {
374        uint8_t b1 = read_uint8(src, error);
375        uint8_t b2 = read_uint8(src, error);
376        uint8_t b3 = read_uint8(src, error);
377        uint8_t b4 = read_uint8(src, error);
378        return b1 + (b2 << 8) + (b3 << 16) + (b4 << 24);
379    }
380
381    template<typename Source>
382    std::string read_string(Source& src)
383    {
384        std::string result;
385        while (true) {
386            int c;
387            if (is_eof(c = boost::iostreams::get(src)))
388                throw gzip_error(gzip::bad_header);
389            else if (c == 0)
390                return result;
391            else
392                result += static_cast<char>(c);
393        }
394    }
395
396    template<typename Source>
397    void read_header(Source& src) // Source is non-blocking.
398    {
399        // Reset saved values.
400        #if BOOST_WORKAROUND(__GNUC__, == 2) && defined(__STL_CONFIG_H) || \
401            BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, == 1) \
402            /**/
403            file_name_.erase(0, std::string::npos);
404            comment_.erase(0, std::string::npos);
405        #else
406            file_name_.clear();
407            comment_.clear();
408        #endif
409        os_ = gzip::os_unknown;
410        mtime_ = 0;
411
412        int flags;
413
414        // Read header, without checking header crc.
415        if ( boost::iostreams::get(src) != gzip::magic::id1 ||   // ID1.
416             boost::iostreams::get(src) != gzip::magic::id2 ||   // ID2.
417             is_eof(boost::iostreams::get(src)) ||               // CM.
418             is_eof(flags = boost::iostreams::get(src)) )        // FLG.
419        {
420            throw gzip_error(gzip::bad_header);
421        }
422        mtime_ = read_uint32(src, gzip::bad_header);        // MTIME.
423        read_uint8(src, gzip::bad_header);                 // XFL.
424        os_ = read_uint8(src, gzip::bad_header);          // OS.
425        if (flags & boost::iostreams::gzip::flags::text)
426            flags_ |= f_text;
427
428        // Skip extra field. (From J. Halleaux; see note at top.)
429        if (flags & gzip::flags::extra) {
430            int length =
431                static_cast<int>(
432                    read_uint8(src, gzip::bad_header) +
433                    (read_uint8(src, gzip::bad_header) << 8)
434                );
435            // length is garbage if EOF but the loop below will quit anyway.
436            do { }
437            while (length-- != 0 && !is_eof(boost::iostreams::get(src)));
438        }
439
440        if (flags & gzip::flags::name)          // Read file name.
441            file_name_ = read_string(src);
442        if (flags & gzip::flags::comment)       // Read comment.
443            comment_ = read_string(src);
444        if (flags & gzip::flags::header_crc) {  // Skip header crc.
445            read_uint8(src, gzip::bad_header);
446            read_uint8(src, gzip::bad_header);
447        }
448    }
449
450    template<typename Source>
451    void read_footer(Source& src)
452    {
453        typename base_type::string_type footer =
454            this->unconsumed_input();
455        int c;
456        while (!is_eof(c = boost::iostreams::get(src)))
457            footer += c;
458        detail::range_adapter<input, std::string>
459            rng(footer.begin(), footer.end());
460        if (read_uint32(rng, gzip::bad_footer) != this->crc())
461            throw gzip_error(gzip::bad_crc);
462        if (static_cast<int>(read_uint32(rng, gzip::bad_footer)) != this->total_out())
463            throw gzip_error(gzip::bad_length);
464    }
465    enum flag_type {
466        f_header_read  = 1,
467        f_footer_read  = f_header_read << 1,
468        f_text         = f_footer_read << 1
469    };
470    std::string  file_name_;
471    std::string  comment_;
472    int          os_;
473    std::time_t  mtime_;
474    int          flags_;
475};
476BOOST_IOSTREAMS_PIPABLE(basic_gzip_decompressor, 1)
477
478typedef basic_gzip_decompressor<> gzip_decompressor;
479
480//------------------Implementation of gzip_compressor-------------------------//
481
482template<typename Alloc>
483basic_gzip_compressor<Alloc>::basic_gzip_compressor
484    (const gzip_params& p, int buffer_size)
485    : base_type(normalize_params(p), buffer_size),
486      offset_(0), flags_(0)
487{
488    // Calculate gzip header.
489    bool has_name = !p.file_name.empty();
490    bool has_comment = !p.comment.empty();
491
492    std::string::size_type length =
493        10 +
494        (has_name ? p.file_name.size() + 1 : 0) +
495        (has_comment ? p.comment.size() + 1 : 0);
496        // + 2; // Header crc confuses gunzip.
497    int flags =
498        //gzip::flags::header_crc +
499        (has_name ? gzip::flags::name : 0) +
500        (has_comment ? gzip::flags::comment : 0);
501    int extra_flags =
502        ( p.level == zlib::best_compression ?
503              gzip::extra_flags::best_compression :
504              0 ) +
505        ( p.level == zlib::best_speed ?
506              gzip::extra_flags::best_speed :
507              0 );
508    header_.reserve(length);
509    header_ += gzip::magic::id1;                         // ID1.
510    header_ += gzip::magic::id2;                         // ID2.
511    header_ += gzip::method::deflate;                    // CM.
512    header_ += static_cast<char>(flags);                 // FLG.
513    header_ += static_cast<char>(0xFF & p.mtime);        // MTIME.
514    header_ += static_cast<char>(0xFF & (p.mtime >> 8));
515    header_ += static_cast<char>(0xFF & (p.mtime >> 16));
516    header_ += static_cast<char>(0xFF & (p.mtime >> 24));
517    header_ += static_cast<char>(extra_flags);           // XFL.
518    header_ += static_cast<char>(gzip::os_unknown);      // OS.
519    if (has_name) {
520        header_ += p.file_name;
521        header_ += '\0';
522    }
523    if (has_comment) {
524        header_ += p.comment;
525        header_ += '\0';
526    }
527}
528
529template<typename Alloc>
530gzip_params basic_gzip_compressor<Alloc>::normalize_params(gzip_params p)
531{
532    p.noheader = true;
533    p.calculate_crc = true;
534    return p;
535}
536
537template<typename Alloc>
538void basic_gzip_compressor<Alloc>::prepare_footer()
539{
540    boost::iostreams::back_insert_device<std::string> out(footer_);
541    write_long(this->crc(), out);
542    write_long(this->total_in(), out);
543    flags_ |= f_body_done;
544    offset_ = 0;
545}
546
547template<typename Alloc>
548std::streamsize basic_gzip_compressor<Alloc>::read_string
549    (char* s, std::streamsize n, std::string& str)
550{
551    using namespace std;
552    streamsize avail =
553        static_cast<streamsize>(str.size() - offset_);
554    streamsize amt = (std::min)(avail, n);
555    std::copy( str.data() + offset_,
556               str.data() + offset_ + amt,
557               s );
558    offset_ += amt;
559    if ( !(flags_ & f_header_done) &&
560         offset_ == static_cast<std::size_t>(str.size()) )
561    {
562        flags_ |= f_header_done;
563    }
564    return amt;
565}
566
567//------------------Implementation of gzip_decompressor-----------------------//
568
569template<typename Alloc>
570basic_gzip_decompressor<Alloc>::basic_gzip_decompressor
571    (int window_bits, int buffer_size)
572    : base_type(make_params(window_bits), buffer_size),
573      os_(gzip::os_unknown), mtime_(0), flags_(0)
574    { }
575
576template<typename Alloc>
577gzip_params basic_gzip_decompressor<Alloc>::make_params(int window_bits)
578{
579    gzip_params p;
580    p.window_bits = window_bits;
581    p.noheader = true;
582    p.calculate_crc = true;
583    return p;
584}
585
586//----------------------------------------------------------------------------//
587
588} } // End namespaces iostreams, boost.
589
590#if defined(BOOST_MSVC)
591# pragma warning(pop)
592#endif
593
594#endif // #ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED
Note: See TracBrowser for help on using the repository browser.