Ugly, not-to-be-pushed sucking in of all of Boost to get windows to work.
[dyninst.git] / external / boost / iostreams / filter / gzip.hpp
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
45 namespace std { using ::time_t; }
46 #endif
47
48 namespace boost { namespace iostreams {
49
50 namespace gzip {
51
52 using namespace boost::iostreams::zlib;
53
54     // Error codes used by gzip_error.
55
56 const int zlib_error        = 1;
57 const int bad_crc           = 2; // Recorded crc doesn't match data.
58 const int bad_length        = 3; // Recorded length doesn't match data.
59 const int bad_header        = 4; // Malformed header.
60 const int bad_footer        = 5; // Malformed footer.
61
62 namespace magic {
63
64     // Magic numbers used by gzip header.
65
66 const int id1               = 0x1f;
67 const int id2               = 0x8b;
68
69 } // End namespace magic.
70
71 namespace method {
72
73     // Codes used for the 'CM' byte of the gzip header.
74
75 const int deflate           = 8;
76
77 } // End namespace method.
78
79 namespace flags {
80
81     // Codes used for the 'FLG' byte of the gzip header.
82
83 const int text              = 1;
84 const int header_crc        = 2;
85 const int extra             = 4;
86 const int name              = 8;
87 const int comment           = 16;
88
89 } // End namespace flags.
90
91 namespace extra_flags {
92
93     // Codes used for the 'XFL' byte of the gzip header.
94
95 const int best_compression  = 2;
96 const int best_speed        = 4;
97
98 } // End namespace extra_flags.
99
100     // Codes used for the 'OS' byte of the gzip header.
101
102 const int os_fat            = 0;
103 const int os_amiga          = 1;
104 const int os_vms            = 2;
105 const int os_unix           = 3;
106 const int os_vm_cms         = 4;
107 const int os_atari          = 5;
108 const int os_hpfs           = 6;
109 const int os_macintosh      = 7;
110 const int os_z_system       = 8;
111 const int os_cp_m           = 9;
112 const int os_tops_20        = 10;
113 const int os_ntfs           = 11;
114 const int os_qdos           = 12;
115 const int os_acorn          = 13;
116 const 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 //
125 struct 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 //
149 class gzip_error : public BOOST_IOSTREAMS_FAILURE {
150 public:
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_; }
160 private:
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 //
170 template<typename Alloc = std::allocator<char> >
171 class basic_gzip_compressor : basic_zlib_compressor<Alloc> {
172 private:
173     typedef basic_zlib_compressor<Alloc>  base_type;
174 public:
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     }
262 private:
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 };
286 BOOST_IOSTREAMS_PIPABLE(basic_gzip_compressor, 1)
287
288 typedef 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 //
295 template<typename Alloc = std::allocator<char> >
296 class basic_gzip_decompressor : basic_zlib_decompressor<Alloc> {
297 public:
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_; }
356 private:
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 };
476 BOOST_IOSTREAMS_PIPABLE(basic_gzip_decompressor, 1)
477
478 typedef basic_gzip_decompressor<> gzip_decompressor;
479
480 //------------------Implementation of gzip_compressor-------------------------//
481
482 template<typename Alloc>
483 basic_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
529 template<typename Alloc>
530 gzip_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
537 template<typename Alloc>
538 void 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
547 template<typename Alloc>
548 std::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
569 template<typename Alloc>
570 basic_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
576 template<typename Alloc>
577 gzip_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