root/trunk/whisperlib/common/io/zlib/zlibwrapper.cc

Revision 7, 13.8 kB (checked in by whispercastorg, 2 years ago)

version 0.2.0

Line 
1 // Copyright (c) 2009, Whispersoft s.r.l.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Whispersoft s.r.l. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Authors: Catalin Popescu
31
32 #include "common/io/zlib/zlibwrapper.h"
33
34 namespace io {
35
36 //////////////////////////////////////////////////////////////////////
37 //
38 // ZlibDeflateWrapper
39 //
40 ZlibDeflateWrapper::ZlibDeflateWrapper(int compress_level)
41   : compress_level_(compress_level),
42     initialized_(false) {
43   Clear();
44 }
45 ZlibDeflateWrapper::~ZlibDeflateWrapper() {
46   Clear();
47 }
48
49 void ZlibDeflateWrapper::Clear() {
50   if ( initialized_ ) {
51     deflateEnd(&strm_);
52   }
53   strm_.zalloc = Z_NULL;
54   strm_.zfree = Z_NULL;
55   strm_.opaque = Z_NULL;
56   strm_.avail_in = 0;
57   strm_.next_in = Z_NULL;
58   initialized_ = false;
59 }
60 bool ZlibDeflateWrapper::Initialize() {
61   Clear();
62   if ( Z_OK !=  deflateInit2(&strm_, compress_level_, Z_DEFLATED,
63                              -MAX_WBITS,       // supress zlib-header
64                              8, Z_DEFAULT_STRATEGY) ) {
65     return false;
66   }
67   initialized_ = true;
68   return true;
69 }
70
71 bool ZlibDeflateWrapper::Deflate(const char* in, int in_size,
72                                  io::MemoryStream* out) {
73   if ( !initialized_ && !Initialize() ) {
74     return false;
75   }
76   int32 out_size = 0;
77   strm_.avail_in = in_size;
78   strm_.next_in =
79       reinterpret_cast<Bytef*>(const_cast<char*>(in));
80
81   // run deflate() on input until output buffer not full, finish
82   // compression if all of source has been read in
83   do {
84     void* const pout = &strm_.next_out;
85     out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
86     strm_.avail_out = out_size;
87     deflate(&strm_, Z_NO_FLUSH);
88     out->ConfirmScratch(out_size - strm_.avail_out);
89   } while ( strm_.avail_in > 0 );
90
91   // Flush the zlib stuff..
92   do {
93     void* const pout = &strm_.next_out;
94     out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
95     strm_.avail_out = out_size;
96     deflate(&strm_, Z_FINISH);
97     out->ConfirmScratch(out_size - strm_.avail_out);
98   } while ( out_size > strm_.avail_out );
99   return true;
100 }
101
102 bool ZlibDeflateWrapper::DeflateSize(io::MemoryStream* in,
103                                      io::MemoryStream* out,
104                                      int32* size) {
105   if ( !initialized_ && !Initialize() ) {
106     return false;
107   }
108   int32 out_size = 0;
109   while ( *size > 0 ) {
110     int32 crt_size = *size;
111     const char* crt_buf = NULL;
112     if ( !in->ReadNext(&crt_buf, &crt_size) ) {
113       break;
114     }
115     strm_.avail_in = crt_size;
116     strm_.next_in =
117       reinterpret_cast<Bytef*>(const_cast<char*>(crt_buf));
118     // run deflate() on input until output buffer not full, finish
119     // compression if all of source has been read in
120     do {
121       void* const pout = &strm_.next_out;
122       out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
123       strm_.avail_out = out_size;
124       deflate(&strm_, Z_NO_FLUSH);
125       out->ConfirmScratch(out_size - strm_.avail_out);
126     } while ( strm_.avail_in > 0 );
127     *size -= crt_size;
128   }
129   if ( *size <= 0 ) {
130     // Flush the zlib stuff..
131     do {
132       void* const pout = &strm_.next_out;
133       out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
134       strm_.avail_out = out_size;
135       deflate(&strm_, Z_FINISH);
136       out->ConfirmScratch(out_size - strm_.avail_out);
137     } while ( out_size > strm_.avail_out );
138     Clear();
139   }
140   return true;
141 }
142
143 //////////////////////////////////////////////////////////////////////
144 //
145 // ZlibInflateWrapper
146 //
147 ZlibInflateWrapper::ZlibInflateWrapper()
148   : initialized_(false) {
149   Clear();
150 }
151 ZlibInflateWrapper::~ZlibInflateWrapper() {
152   Clear();
153 }
154
155 void ZlibInflateWrapper::Clear() {
156   if ( initialized_ ) {
157     inflateEnd(&strm_);
158   }
159   strm_.zalloc = Z_NULL;
160   strm_.zfree = Z_NULL;
161   strm_.opaque = Z_NULL;
162   strm_.avail_in = 0;
163   strm_.next_in = Z_NULL;
164   initialized_ = false;
165 }
166
167
168 int ZlibInflateWrapper::InflateSize(io::MemoryStream* in,
169                                     io::MemoryStream* out,
170                                     int32* size) {
171   int zlib_err = Z_OK;
172   if ( !initialized_ ) {
173     Clear();
174     zlib_err = inflateInit2(&strm_, -MAX_WBITS);
175     if ( Z_OK != zlib_err ) {
176       return zlib_err;
177     }
178     initialized_ = true;
179   }
180   while ( size == NULL || *size > 0 ) {
181     int32 crt_size = size == NULL ? 0 : *size;
182     const char* crt_buf = NULL;
183     in->MarkerSet();
184     if ( !in->ReadNext(&crt_buf, &crt_size) ) {
185       return zlib_err;   // no error actually ..
186     }
187     strm_.avail_in = crt_size;
188     strm_.next_in =
189       reinterpret_cast<Bytef*>(const_cast<char*>(crt_buf));
190     do {
191       int32 out_size = 0;
192       void* const pout = &strm_.next_out;
193       out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
194       strm_.avail_out = out_size;
195       zlib_err = inflate(&strm_, Z_NO_FLUSH);
196       DCHECK_NE(zlib_err, Z_STREAM_ERROR);   // state not clobbered
197       if ( zlib_err == Z_NEED_DICT ||
198            zlib_err == Z_DATA_ERROR ||
199            zlib_err == Z_MEM_ERROR ) {
200         out->ConfirmScratch(0);
201         Clear();
202         return zlib_err;
203       }
204       out->ConfirmScratch(out_size - strm_.avail_out);
205     } while ( zlib_err == Z_OK && strm_.avail_in > 0 );
206     if ( size ) {
207       *size -= (crt_size - strm_.avail_in);
208     }
209     if ( strm_.avail_in > 0 ) {
210       in->MarkerRestore();
211       in->Skip(crt_size - strm_.avail_in);
212     } else {
213       in->MarkerClear();
214     }
215     if ( zlib_err == Z_STREAM_END ) {
216       Clear();
217       break;
218     }
219   }
220   return zlib_err;
221 }
222
223 //////////////////////////////////////////////////////////////////////
224
225 //       +---+---+---+---+---+---+---+---+---+---+
226 //       |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)
227 //       +---+---+---+---+---+---+---+---+---+---+
228
229 static const uint8 kGzipHeader[] = {
230   0x1f, 0x8b, 0x08, 0, 0, 0, 0, 0, 0, 0xff,
231 };
232
233 ZlibGzipEncodeWrapper::ZlibGzipEncodeWrapper(int compress_level)
234   : compress_level_(compress_level) {
235 }
236
237 ZlibGzipEncodeWrapper::~ZlibGzipEncodeWrapper() {
238 }
239 void ZlibGzipEncodeWrapper::InitStream() {
240   strm_.zalloc = Z_NULL;
241   strm_.zfree = Z_NULL;
242   strm_.opaque = Z_NULL;
243   strm_.avail_in = 0;
244   strm_.next_in = Z_NULL;
245   // NB - this params are the KEY for good http compression
246   deflateInit2(&strm_, compress_level_, Z_DEFLATED,
247                -MAX_WBITS,       // supress zlib-header
248                8,
249                Z_DEFAULT_STRATEGY);
250   crc_ = crc32(0L, Z_NULL, 0);
251   input_size_ = 0;
252 }
253
254 void ZlibGzipEncodeWrapper::BeginEncoding(io::MemoryStream* out) {
255   InitStream();
256   out->Write(kGzipHeader, sizeof(kGzipHeader));
257 }
258
259 void ZlibGzipEncodeWrapper::ContinueEncoding(io::MemoryStream* in,
260                                              io::MemoryStream* out) {
261   int32 size = in->Size();
262   input_size_ += size;
263   int32 out_size = 0;
264   io::MemoryStream compressed;
265
266   while ( size > 0 ) {
267     int32 crt_size = size;
268     const char* crt_buf = NULL;
269     CHECK(in->ReadNext(&crt_buf, &crt_size));
270
271     strm_.avail_in = crt_size;
272     strm_.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(crt_buf));
273     // update the CRC
274     crc_ = crc32(crc_, strm_.next_in, strm_.avail_in);
275
276     // run deflate() on input until output buffer not full, finish
277     // compression if all of source has been read in
278     do {
279       void* const pout = &strm_.next_out;
280       compressed.GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
281       CHECK_GT(out_size, 0);
282       strm_.avail_out = out_size;
283       int z_err = deflate(&strm_, Z_NO_FLUSH);
284       CHECK(z_err == Z_OK)   // some progress was made
285           << " Bad z_err = " << z_err;
286       compressed.ConfirmScratch(out_size - strm_.avail_out);
287     } while ( strm_.avail_in > 0 );
288     size -= crt_size;
289   }
290   CHECK_EQ(size, 0);
291
292   // Some data was output in compressed buffer.
293   // There is still more data in zlib to be flushed.
294   out->AppendStream(&compressed);
295 }
296
297 void ZlibGzipEncodeWrapper::EndEncoding(io::MemoryStream* out) {
298   int32 out_size = 0;
299   io::MemoryStream compressed;
300   // Flush the zlib stuff..
301   int z_err;
302   do {
303     void* const pout = &strm_.next_out;
304     compressed.GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
305     CHECK_GT(out_size, 0);
306     strm_.avail_out = out_size;
307     z_err = deflate(&strm_, Z_FINISH);
308     // some data has been written, but there is still more data to output
309     // ||
310     // all data has been written, there is no more data to flush in zlib
311     CHECK(z_err == Z_OK || z_err == Z_STREAM_END)
312           << " Bad z_err = " << z_err;
313     compressed.ConfirmScratch(out_size - strm_.avail_out);
314   } while ( z_err == Z_OK );
315
316   // Write the data - finish CRC and input_size_
317   out->AppendStream(&compressed);
318   NumStreamer::WriteInt32(out, static_cast<int32>(crc_), common::LILENDIAN);
319   NumStreamer::WriteInt32(out, input_size_, common::LILENDIAN);
320
321   // Reset the internal zip data
322   deflateEnd(&strm_);
323 }
324
325
326 void ZlibGzipEncodeWrapper::Encode(io::MemoryStream* in,
327                                    io::MemoryStream* out) {
328   BeginEncoding(out);
329   ContinueEncoding(in, out);
330   EndEncoding(out);
331 }
332
333 //////////////////////////////////////////////////////////////////////
334
335 ZlibGzipDecodeWrapper::ZlibGzipDecodeWrapper(
336   bool strict_trailer_checking)
337   : strict_trailer_checking_(strict_trailer_checking) {
338   InitStream();
339 }
340
341 ZlibGzipDecodeWrapper::~ZlibGzipDecodeWrapper() {
342   inflateEnd(&strm_);
343 }
344
345 void ZlibGzipDecodeWrapper::InitStream() {
346   state_ = INITIALIZED;
347   strm_.zalloc = Z_NULL;
348   strm_.zfree = Z_NULL;
349   strm_.opaque = Z_NULL;
350   strm_.avail_in = 0;
351   strm_.next_in = Z_NULL;
352
353   inflateInit2(&strm_, -MAX_WBITS);
354   running_crc_ = crc32(0L, Z_NULL, 0);
355   running_size_ = 0;
356 }
357
358 int ZlibGzipDecodeWrapper::Decode(io::MemoryStream* in,
359                                   io::MemoryStream* out) {
360   if ( state_ == FINALIZED ) {
361     InitStream();
362   }
363   if ( state_ == INITIALIZED ) {
364     if ( in->Size() < sizeof(kGzipHeader) + 2 * sizeof(int32) ) {
365       return Z_OK;  // not an error - waiting for more data though..
366     }
367     // Consume the header, crc and size..
368     uint8 header[sizeof(kGzipHeader)];
369     CHECK_EQ(in->Read(header, sizeof(header)), sizeof(header));
370     if ( header[0] != kGzipHeader[0] || header[1] != kGzipHeader[1] ) {
371       // ERROR - invalid magic:
372       return Z_DATA_ERROR;
373     }
374     if ( (header[3] & 0xfe) != 0 ) {
375       // ERROR - we don't know these things
376       LOG_ERROR << " Error - we don't know to decode complicated gzip headers";
377       return Z_VERSION_ERROR;
378     }
379     state_ = DECODING;
380   }
381   int zlib_err = Z_OK;
382   while ( state_ == DECODING && zlib_err != Z_STREAM_END ) {
383     int32 crt_size = 0;
384     const char* crt_buf = NULL;
385     in->MarkerSet();
386     if ( !in->ReadNext(&crt_buf, &crt_size) ) {
387       in->MarkerClear();
388       return zlib_err;
389     }
390
391     strm_.avail_in = crt_size;
392     strm_.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(crt_buf));
393     do {
394       int32 out_size = 0;
395       void* const pout = &strm_.next_out;
396       out->GetScratchSpace(reinterpret_cast<char**>(pout), &out_size);
397       const Bytef* out_buf = strm_.next_out;
398       strm_.avail_out = out_size;
399       zlib_err = inflate(&strm_, Z_NO_FLUSH);
400       DCHECK_NE(zlib_err, Z_STREAM_ERROR);   // state not clobbered
401       if ( zlib_err == Z_NEED_DICT ||
402            zlib_err == Z_DATA_ERROR ||
403            zlib_err == Z_MEM_ERROR ) {
404         out->ConfirmScratch(0);
405         state_ = FINALIZED;
406         return zlib_err;
407       }
408       running_size_ += out_size - strm_.avail_out;
409       running_crc_ = crc32(running_crc_, out_buf, out_size - strm_.avail_out);
410       out->ConfirmScratch(out_size - strm_.avail_out);
411     } while ( zlib_err == Z_OK && strm_.avail_in > 0 );
412     if ( strm_.avail_in > 0 ) {
413       in->MarkerRestore();
414       in->Skip(crt_size - strm_.avail_in);
415     } else {
416       in->MarkerClear();
417     }
418   }
419   if ( zlib_err == Z_STREAM_END && state_ == DECODING ) {
420     state_ = CHECKING;
421   }
422   if ( state_ == CHECKING ) {
423     if ( in->Size() < 2 * sizeof(int32) ) {
424       return Z_OK;
425     }
426     // everything should be OK
427     state_ = FINALIZED;
428     zlib_err = Z_STREAM_END;
429     const int32 expected_crc = NumStreamer::ReadInt32(in, common::LILENDIAN);
430     const int32 expected_size = NumStreamer::ReadInt32(in, common::LILENDIAN);
431     if ( expected_crc != running_crc_ && strict_trailer_checking_ ) {
432       LOG_WARNING << " Invalid CRC - got: 0x" << hex << running_crc_
433                   << " excpected: 0x" << hex << expected_crc << dec;
434       zlib_err = Z_DATA_ERROR;
435     }
436     if ( expected_size != running_size_ && strict_trailer_checking_ ) {
437       LOG_ERROR << "Got a stream end before declared size: "
438                 << " expected: " << expected_size << " got: " << running_size_;
439       zlib_err = Z_DATA_ERROR;
440     }
441   }
442   return zlib_err;
443 }
444 }
Note: See TracBrowser for help on using the browser.