]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/iostreams/include/boost/iostreams/filter/gzip.hpp
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / iostreams / include / boost / iostreams / filter / gzip.hpp
1 // (C) Copyright 2008 CodeRage, LLC (turkanis at coderage dot com)
2 // (C) Copyright 2003-2007 Jonathan Turkanis
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt.)
5
6 // See http://www.boost.org/libs/iostreams for documentation.
7
8 // Contains the definitions of the class templates gzip_compressor and
9 // gzip_decompressor for reading and writing files in the gzip file format
10 // (RFC 1952). Based in part on work of Jonathan de Halleux; see [...]
11
12 #ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED
13 #define BOOST_IOSTREAMS_GZIP_HPP_INCLUDED
14
15 #if defined(_MSC_VER)
16 # pragma once
17 #endif
18
19 #include <boost/config.hpp> // STATIC_CONSTANT, STDC_NAMESPACE,
20 // DINKUMWARE_STDLIB, __STL_CONFIG_H.
21 #include <algorithm> // min.
22 #include <boost/assert.hpp>
23 #include <cstdio> // EOF.
24 #include <cstddef> // size_t.
25 #include <ctime> // std::time_t.
26 #include <memory> // allocator.
27 #include <boost/config.hpp> // Put size_t in std.
28 #include <boost/detail/workaround.hpp>
29 #include <boost/cstdint.hpp> // uint8_t, uint32_t.
30 #include <boost/iostreams/checked_operations.hpp>
31 #include <boost/iostreams/constants.hpp> // buffer size.
32 #include <boost/iostreams/detail/adapter/non_blocking_adapter.hpp>
33 #include <boost/iostreams/detail/adapter/range_adapter.hpp>
34 #include <boost/iostreams/detail/char_traits.hpp>
35 #include <boost/iostreams/detail/ios.hpp> // failure.
36 #include <boost/iostreams/detail/error.hpp>
37 #include <boost/iostreams/operations.hpp>
38 #include <boost/iostreams/device/back_inserter.hpp>
39 #include <boost/iostreams/filter/zlib.hpp>
40 #include <boost/iostreams/pipeline.hpp>
41 #include <boost/iostreams/putback.hpp>
42 #include <boost/throw_exception.hpp>
43
44 // Must come last.
45 #if defined(BOOST_MSVC)
46 # pragma warning(push)
47 # pragma warning(disable:4244) // Possible truncation
48 # pragma warning(disable:4251) // Missing DLL interface for std::string
49 # pragma warning(disable:4309) // Truncation of constant value.
50 #endif
51
52 #ifdef BOOST_NO_STDC_NAMESPACE
53 namespace std { using ::time_t; }
54 #endif
55
56 namespace boost { namespace iostreams {
57
58 //------------------Definitions of constants----------------------------------//
59
60 namespace gzip {
61
62 using namespace boost::iostreams::zlib;
63
64 // Error codes used by gzip_error.
65
66 const int zlib_error = 1;
67 const int bad_crc = 2; // Recorded crc doesn't match data.
68 const int bad_length = 3; // Recorded length doesn't match data.
69 const int bad_header = 4; // Malformed header.
70 const int bad_footer = 5; // Malformed footer.
71 const int bad_method = 6; // Unsupported compression method.
72
73 namespace magic {
74
75 // Magic numbers used by gzip header.
76
77 const int id1 = 0x1f;
78 const int id2 = 0x8b;
79
80 } // End namespace magic.
81
82 namespace method {
83
84 // Codes used for the 'CM' byte of the gzip header.
85
86 const int deflate = 8;
87
88 } // End namespace method.
89
90 namespace flags {
91
92 // Codes used for the 'FLG' byte of the gzip header.
93
94 const int text = 1;
95 const int header_crc = 2;
96 const int extra = 4;
97 const int name = 8;
98 const int comment = 16;
99
100 } // End namespace flags.
101
102 namespace extra_flags {
103
104 // Codes used for the 'XFL' byte of the gzip header.
105
106 const int best_compression = 2;
107 const int best_speed = 4;
108
109 } // End namespace extra_flags.
110
111 // Codes used for the 'OS' byte of the gzip header.
112
113 const int os_fat = 0;
114 const int os_amiga = 1;
115 const int os_vms = 2;
116 const int os_unix = 3;
117 const int os_vm_cms = 4;
118 const int os_atari = 5;
119 const int os_hpfs = 6;
120 const int os_macintosh = 7;
121 const int os_z_system = 8;
122 const int os_cp_m = 9;
123 const int os_tops_20 = 10;
124 const int os_ntfs = 11;
125 const int os_qdos = 12;
126 const int os_acorn = 13;
127 const int os_unknown = 255;
128
129 } // End namespace gzip.
130
131 //------------------Definition of gzip_params---------------------------------//
132
133 //
134 // Class name: gzip_params.
135 // Description: Subclass of zlib_params with an additional field
136 // representing a file name.
137 //
138 struct gzip_params : zlib_params {
139
140 // Non-explicit constructor.
141 gzip_params( int level = gzip::default_compression,
142 int method = gzip::deflated,
143 int window_bits = gzip::default_window_bits,
144 int mem_level = gzip::default_mem_level,
145 int strategy = gzip::default_strategy,
146 std::string file_name = "",
147 std::string comment = "",
148 std::time_t mtime = 0 )
149 : zlib_params(level, method, window_bits, mem_level, strategy),
150 file_name(file_name), comment(comment), mtime(mtime)
151 { }
152 std::string file_name;
153 std::string comment;
154 std::time_t mtime;
155 };
156
157 //------------------Definition of gzip_error----------------------------------//
158
159 //
160 // Class name: gzip_error.
161 // Description: Subclass of std::ios_base::failure thrown to indicate
162 // zlib errors other than out-of-memory conditions.
163 //
164 class gzip_error : public BOOST_IOSTREAMS_FAILURE {
165 public:
166 explicit gzip_error(int error)
167 : BOOST_IOSTREAMS_FAILURE("gzip error"),
168 error_(error), zlib_error_code_(zlib::okay) { }
169 explicit gzip_error(const zlib_error& e)
170 : BOOST_IOSTREAMS_FAILURE("gzip error"),
171 error_(gzip::zlib_error), zlib_error_code_(e.error())
172 { }
173 int error() const { return error_; }
174 int zlib_error_code() const { return zlib_error_code_; }
175 private:
176 int error_;
177 int zlib_error_code_;
178 };
179
180 //------------------Definition of gzip_compressor-----------------------------//
181
182 //
183 // Template name: gzip_compressor
184 // Description: Model of OutputFilter implementing compression in the
185 // gzip format.
186 //
187 template<typename Alloc = std::allocator<char> >
188 class basic_gzip_compressor : basic_zlib_compressor<Alloc> {
189 private:
190 typedef basic_zlib_compressor<Alloc> base_type;
191 public:
192 typedef char char_type;
193 struct category
194 : dual_use,
195 filter_tag,
196 multichar_tag,
197 closable_tag
198 { };
199 basic_gzip_compressor( const gzip_params& = gzip::default_compression,
200 int buffer_size = default_device_buffer_size );
201
202 template<typename Source>
203 std::streamsize read(Source& src, char_type* s, std::streamsize n)
204 {
205 std::streamsize result = 0;
206
207 // Read header.
208 if (!(flags_ & f_header_done))
209 result += read_string(s, n, header_);
210
211 // Read body.
212 if (!(flags_ & f_body_done)) {
213
214 // Read from basic_zlib_filter.
215 std::streamsize amt = base_type::read(src, s + result, n - result);
216 if (amt != -1) {
217 result += amt;
218 if (amt < n - result) { // Double-check for EOF.
219 amt = base_type::read(src, s + result, n - result);
220 if (amt != -1)
221 result += amt;
222 }
223 }
224 if (amt == -1)
225 prepare_footer();
226 }
227
228 // Read footer.
229 if ((flags_ & f_body_done) != 0 && result < n)
230 result += read_string(s + result, n - result, footer_);
231
232 return result != 0 ? result : -1;
233 }
234
235 template<typename Sink>
236 std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
237 {
238 if (!(flags_ & f_header_done)) {
239 std::streamsize amt =
240 static_cast<std::streamsize>(header_.size() - offset_);
241 offset_ += boost::iostreams::write_if(snk, header_.data() + offset_, amt);
242 if (offset_ == header_.size())
243 flags_ |= f_header_done;
244 else
245 return 0;
246 }
247 return base_type::write(snk, s, n);
248 }
249
250 template<typename Sink>
251 void close(Sink& snk, BOOST_IOS::openmode m)
252 {
253 try {
254 if (m == BOOST_IOS::out && !(flags_ & f_header_done))
255 this->write(snk, 0, 0);
256
257 // Close zlib compressor.
258 base_type::close(snk, m);
259
260 if (m == BOOST_IOS::out) {
261 if (flags_ & f_header_done) {
262
263 // Write final fields of gzip file format.
264 write_long(this->crc(), snk);
265 write_long(this->total_in(), snk);
266 }
267 }
268 } catch(...) {
269 close_impl();
270 throw;
271 }
272 close_impl();
273 }
274 private:
275 static gzip_params normalize_params(gzip_params p);
276 void prepare_footer();
277 std::streamsize read_string(char* s, std::streamsize n, std::string& str);
278
279 template<typename Sink>
280 static void write_long(long n, Sink& next, boost::mpl::true_)
281 {
282 boost::iostreams::put(next, static_cast<char>(0xFF & n));
283 boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 8)));
284 boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 16)));
285 boost::iostreams::put(next, static_cast<char>(0xFF & (n >> 24)));
286 }
287 template<typename Sink>
288 static void write_long(long n, Sink& next, boost::mpl::false_)
289 {
290 }
291 template<typename Sink>
292 static void write_long(long n, Sink& next)
293 {
294 typedef typename category_of<Sink>::type category;
295 typedef is_convertible<category, output> can_write;
296 write_long(n, next, can_write());
297 }
298
299 void close_impl()
300 {
301 footer_.clear();
302 offset_ = 0;
303 flags_ = 0;
304 }
305
306 enum state_type {
307 f_header_done = 1,
308 f_body_done = f_header_done << 1,
309 f_footer_done = f_body_done << 1
310 };
311 std::string header_;
312 std::string footer_;
313 std::size_t offset_;
314 int flags_;
315 };
316 BOOST_IOSTREAMS_PIPABLE(basic_gzip_compressor, 1)
317
318 typedef basic_gzip_compressor<> gzip_compressor;
319
320 //------------------Definition of helper templates for decompression----------//
321
322 namespace detail {
323
324 // Processes gzip headers
325 class BOOST_IOSTREAMS_DECL gzip_header {
326 public:
327 gzip_header() { reset(); }
328
329 // Members for processing header data
330 void process(char c);
331 bool done() const { return state_ == s_done; }
332 void reset();
333
334 // Members for accessing header data
335 std::string file_name() const { return file_name_; }
336 std::string comment() const { return comment_; }
337 bool text() const { return (flags_ & gzip::flags::text) != 0; }
338 int os() const { return os_; }
339 std::time_t mtime() const { return mtime_; }
340 private:
341 enum state_type {
342 s_id1 = 1,
343 s_id2 = s_id1 + 1,
344 s_cm = s_id2 + 1,
345 s_flg = s_cm + 1,
346 s_mtime = s_flg + 1,
347 s_xfl = s_mtime + 1,
348 s_os = s_xfl + 1,
349 s_xlen = s_os + 1,
350 s_extra = s_xlen + 1,
351 s_name = s_extra + 1,
352 s_comment = s_name + 1,
353 s_hcrc = s_comment + 1,
354 s_done = s_hcrc + 1
355 };
356 std::string file_name_;
357 std::string comment_;
358 int os_;
359 std::time_t mtime_;
360 int flags_;
361 int state_;
362 int offset_; // Offset within fixed-length region.
363 int xlen_; // Bytes remaining in extra field.
364 };
365
366 // Processes gzip footers
367 class BOOST_IOSTREAMS_DECL gzip_footer {
368 public:
369 gzip_footer() { reset(); }
370
371 // Members for processing footer data
372 void process(char c);
373 bool done() const { return state_ == s_done; }
374 void reset();
375
376 // Members for accessing footer data
377 zlib::ulong crc() const { return crc_; }
378 zlib::ulong uncompressed_size() const { return isize_; }
379 private:
380 enum state_type {
381 s_crc = 1,
382 s_isize = s_crc + 1,
383 s_done = s_isize + 1
384 };
385 zlib::ulong crc_;
386 zlib::ulong isize_;
387 int state_;
388 int offset_;
389 };
390
391 } // End namespace boost::iostreams::detail.
392
393 //------------------Definition of basic_gzip_decompressor---------------------//
394
395 //
396 // Template name: basic_gzip_decompressor
397 // Description: Model of InputFilter implementing compression in the
398 // gzip format.
399 //
400 template<typename Alloc = std::allocator<char> >
401 class basic_gzip_decompressor : basic_zlib_decompressor<Alloc> {
402 private:
403 typedef basic_zlib_decompressor<Alloc> base_type;
404 typedef typename base_type::string_type string_type;
405 public:
406 typedef char char_type;
407 struct category
408 : dual_use,
409 filter_tag,
410 multichar_tag,
411 closable_tag
412 { };
413 basic_gzip_decompressor( int window_bits = gzip::default_window_bits,
414 int buffer_size = default_device_buffer_size );
415
416 template<typename Sink>
417 std::streamsize write(Sink& snk, const char_type* s, std::streamsize n)
418 {
419 std::streamsize result = 0;
420 while(result < n) {
421 if(state_ == s_start) {
422 state_ = s_header;
423 header_.reset();
424 footer_.reset();
425 }
426 if (state_ == s_header) {
427 int c = s[result++];
428 header_.process(c);
429 if (header_.done())
430 state_ = s_body;
431 } else if (state_ == s_body) {
432 try {
433 std::streamsize amt =
434 base_type::write(snk, s + result, n - result);
435 result += amt;
436 if (!this->eof()) {
437 break;
438 } else {
439 state_ = s_footer;
440 }
441 } catch (const zlib_error& e) {
442 boost::throw_exception(gzip_error(e));
443 }
444 } else { // state_ == s_footer
445 if (footer_.done()) {
446 if (footer_.crc() != this->crc())
447 boost::throw_exception(gzip_error(gzip::bad_crc));
448
449 base_type::close(snk, BOOST_IOS::out);
450 state_ = s_start;
451 } else {
452 int c = s[result++];
453 footer_.process(c);
454 }
455 }
456 }
457 return result;
458 }
459
460 template<typename Source>
461 std::streamsize read(Source& src, char_type* s, std::streamsize n)
462 {
463 typedef char_traits<char> traits_type;
464 std::streamsize result = 0;
465 peekable_source<Source> peek(src, putback_);
466 while (result < n && state_ != s_done) {
467 if (state_ == s_start) {
468 state_ = s_header;
469 header_.reset();
470 footer_.reset();
471 }
472 if (state_ == s_header) {
473 int c = boost::iostreams::get(peek);
474 if (traits_type::is_eof(c)) {
475 boost::throw_exception(gzip_error(gzip::bad_header));
476 } else if (traits_type::would_block(c)) {
477 break;
478 }
479 header_.process(c);
480 if (header_.done())
481 state_ = s_body;
482 } else if (state_ == s_body) {
483 try {
484 std::streamsize amt =
485 base_type::read(peek, s + result, n - result);
486 if (amt != -1) {
487 result += amt;
488 if (amt < n - result)
489 break;
490 } else {
491 peek.putback(this->unconsumed_input());
492 state_ = s_footer;
493 }
494 } catch (const zlib_error& e) {
495 boost::throw_exception(gzip_error(e));
496 }
497 } else { // state_ == s_footer
498 int c = boost::iostreams::get(peek);
499 if (traits_type::is_eof(c)) {
500 boost::throw_exception(gzip_error(gzip::bad_footer));
501 } else if (traits_type::would_block(c)) {
502 break;
503 }
504 footer_.process(c);
505 if (footer_.done()) {
506 if (footer_.crc() != this->crc())
507 boost::throw_exception(gzip_error(gzip::bad_crc));
508 int c = boost::iostreams::get(peek);
509 if (traits_type::is_eof(c)) {
510 state_ = s_done;
511 } else {
512 peek.putback(c);
513 base_type::close(peek, BOOST_IOS::in);
514 state_ = s_start;
515 header_.reset();
516 footer_.reset();
517 }
518 }
519 }
520 }
521 if (peek.has_unconsumed_input()) {
522 putback_ = peek.unconsumed_input();
523 } else {
524 putback_.clear();
525 }
526 return result != 0 || state_ != s_done ?
527 result :
528 -1;
529 }
530
531 template<typename Source>
532 void close(Source& src, BOOST_IOS::openmode m)
533 {
534 try {
535 base_type::close(src, m);
536 } catch (const zlib_error& e) {
537 state_ = s_start;
538 boost::throw_exception(gzip_error(e));
539 }
540 if (m == BOOST_IOS::out) {
541 if (state_ == s_start || state_ == s_header)
542 boost::throw_exception(gzip_error(gzip::bad_header));
543 else if (state_ == s_body)
544 boost::throw_exception(gzip_error(gzip::bad_footer));
545 else if (state_ == s_footer) {
546 if (!footer_.done())
547 boost::throw_exception(gzip_error(gzip::bad_footer));
548 else if(footer_.crc() != this->crc())
549 boost::throw_exception(gzip_error(gzip::bad_crc));
550 } else {
551 BOOST_ASSERT(!"Bad state");
552 }
553 }
554 state_ = s_start;
555 }
556
557 std::string file_name() const { return header_.file_name(); }
558 std::string comment() const { return header_.comment(); }
559 bool text() const { return header_.text(); }
560 int os() const { return header_.os(); }
561 std::time_t mtime() const { return header_.mtime(); }
562 private:
563 static gzip_params make_params(int window_bits);
564
565 // Source adapter allowing an arbitrary character sequence to be put back.
566 template<typename Source>
567 struct peekable_source {
568 typedef char char_type;
569 struct category : source_tag, peekable_tag { };
570 explicit peekable_source(Source& src, const string_type& putback = "")
571 : src_(src), putback_(putback), offset_(0)
572 { }
573 std::streamsize read(char* s, std::streamsize n)
574 {
575 std::streamsize result = 0;
576
577 // Copy characters from putback buffer
578 std::streamsize pbsize =
579 static_cast<std::streamsize>(putback_.size());
580 if (offset_ < pbsize) {
581 result = (std::min)(n, pbsize - offset_);
582 BOOST_IOSTREAMS_CHAR_TRAITS(char)::copy(
583 s, putback_.data() + offset_, result);
584 offset_ += result;
585 if (result == n)
586 return result;
587 }
588
589 // Read characters from src_
590 std::streamsize amt =
591 boost::iostreams::read(src_, s + result, n - result);
592 return amt != -1 ?
593 result + amt :
594 result ? result : -1;
595 }
596 bool putback(char c)
597 {
598 if (offset_) {
599 putback_[--offset_] = c;
600 } else {
601 boost::throw_exception(
602 boost::iostreams::detail::bad_putback());
603 }
604 return true;
605 }
606 void putback(const string_type& s)
607 {
608 putback_.replace(0, offset_, s);
609 offset_ = 0;
610 }
611
612 // Returns true if some characters have been putback but not re-read.
613 bool has_unconsumed_input() const
614 {
615 return offset_ < static_cast<std::streamsize>(putback_.size());
616 }
617
618 // Returns the sequence of characters that have been put back but not re-read.
619 string_type unconsumed_input() const
620 {
621 return string_type(putback_, offset_, putback_.size() - offset_);
622 }
623 Source& src_;
624 string_type putback_;
625 std::streamsize offset_;
626 };
627
628 enum state_type {
629 s_start = 1,
630 s_header = s_start + 1,
631 s_body = s_header + 1,
632 s_footer = s_body + 1,
633 s_done = s_footer + 1
634 };
635 detail::gzip_header header_;
636 detail::gzip_footer footer_;
637 string_type putback_;
638 int state_;
639 };
640 BOOST_IOSTREAMS_PIPABLE(basic_gzip_decompressor, 1)
641
642 typedef basic_gzip_decompressor<> gzip_decompressor;
643
644 //------------------Implementation of gzip_compressor-------------------------//
645
646 template<typename Alloc>
647 basic_gzip_compressor<Alloc>::basic_gzip_compressor
648 (const gzip_params& p, int buffer_size)
649 : base_type(normalize_params(p), buffer_size),
650 offset_(0), flags_(0)
651 {
652 // Calculate gzip header.
653 bool has_name = !p.file_name.empty();
654 bool has_comment = !p.comment.empty();
655
656 std::string::size_type length =
657 10 +
658 (has_name ? p.file_name.size() + 1 : 0) +
659 (has_comment ? p.comment.size() + 1 : 0);
660 // + 2; // Header crc confuses gunzip.
661 int flags =
662 //gzip::flags::header_crc +
663 (has_name ? gzip::flags::name : 0) +
664 (has_comment ? gzip::flags::comment : 0);
665 int extra_flags =
666 ( p.level == zlib::best_compression ?
667 gzip::extra_flags::best_compression :
668 0 ) +
669 ( p.level == zlib::best_speed ?
670 gzip::extra_flags::best_speed :
671 0 );
672 header_.reserve(length);
673 header_ += gzip::magic::id1; // ID1.
674 header_ += gzip::magic::id2; // ID2.
675 header_ += gzip::method::deflate; // CM.
676 header_ += static_cast<char>(flags); // FLG.
677 header_ += static_cast<char>(0xFF & p.mtime); // MTIME.
678 header_ += static_cast<char>(0xFF & (p.mtime >> 8));
679 header_ += static_cast<char>(0xFF & (p.mtime >> 16));
680 header_ += static_cast<char>(0xFF & (p.mtime >> 24));
681 header_ += static_cast<char>(extra_flags); // XFL.
682 header_ += static_cast<char>(gzip::os_unknown); // OS.
683 if (has_name) {
684 header_ += p.file_name;
685 header_ += '\0';
686 }
687 if (has_comment) {
688 header_ += p.comment;
689 header_ += '\0';
690 }
691 }
692
693 template<typename Alloc>
694 gzip_params basic_gzip_compressor<Alloc>::normalize_params(gzip_params p)
695 {
696 p.noheader = true;
697 p.calculate_crc = true;
698 return p;
699 }
700
701 template<typename Alloc>
702 void basic_gzip_compressor<Alloc>::prepare_footer()
703 {
704 boost::iostreams::back_insert_device<std::string> out(footer_);
705 write_long(this->crc(), out);
706 write_long(this->total_in(), out);
707 flags_ |= f_body_done;
708 offset_ = 0;
709 }
710
711 template<typename Alloc>
712 std::streamsize basic_gzip_compressor<Alloc>::read_string
713 (char* s, std::streamsize n, std::string& str)
714 {
715 std::streamsize avail =
716 static_cast<std::streamsize>(str.size() - offset_);
717 std::streamsize amt = (std::min)(avail, n);
718 std::copy( str.data() + offset_,
719 str.data() + offset_ + amt,
720 s );
721 offset_ += amt;
722 if ( !(flags_ & f_header_done) &&
723 offset_ == static_cast<std::size_t>(str.size()) )
724 {
725 flags_ |= f_header_done;
726 }
727 return amt;
728 }
729
730 //------------------Implementation of gzip_decompressor-----------------------//
731
732 template<typename Alloc>
733 basic_gzip_decompressor<Alloc>::basic_gzip_decompressor
734 (int window_bits, int buffer_size)
735 : base_type(make_params(window_bits), buffer_size),
736 state_(s_start)
737 { }
738
739 template<typename Alloc>
740 gzip_params basic_gzip_decompressor<Alloc>::make_params(int window_bits)
741 {
742 gzip_params p;
743 p.window_bits = window_bits;
744 p.noheader = true;
745 p.calculate_crc = true;
746 return p;
747 }
748
749 //----------------------------------------------------------------------------//
750
751 } } // End namespaces iostreams, boost.
752
753 #if defined(BOOST_MSVC)
754 # pragma warning(pop)
755 #endif
756
757 #endif // #ifndef BOOST_IOSTREAMS_GZIP_HPP_INCLUDED