]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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> | |
b32b8144 | 35 | #include <boost/iostreams/detail/ios.hpp> // failure, streamsize. |
7c673cae FG |
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, | |
b32b8144 FG |
146 | std::string file_name_ = "", |
147 | std::string comment_ = "", | |
148 | std::time_t mtime_ = 0 ) | |
7c673cae | 149 | : zlib_params(level, method, window_bits, mem_level, strategy), |
b32b8144 | 150 | file_name(file_name_), comment(comment_), mtime(mtime_) |
7c673cae FG |
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, | |
b32b8144 | 200 | std::streamsize buffer_size = default_device_buffer_size ); |
7c673cae FG |
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> | |
b32b8144 | 288 | static void write_long(long, Sink&, boost::mpl::false_) |
7c673cae FG |
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, | |
b32b8144 | 414 | std::streamsize buffer_size = default_device_buffer_size ); |
7c673cae FG |
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)); | |
b32b8144 | 508 | c = boost::iostreams::get(peek); |
7c673cae FG |
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 | |
b32b8144 | 648 | (const gzip_params& p, std::streamsize buffer_size) |
7c673cae FG |
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. | |
b32b8144 | 674 | header_ += static_cast<char>(gzip::magic::id2); // ID2. |
7c673cae FG |
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 | |
b32b8144 | 734 | (int window_bits, std::streamsize buffer_size) |
7c673cae FG |
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 |