Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
codec_zlib.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "precompiled.h"
25 
26 #include "lib/alignment.h"
27 #include "lib/file/archive/codec.h"
29 
30 #include "lib/sysdep/cpu.h"
31 
32 
33 class Codec_ZLib : public ICodec
34 {
35 public:
36  u32 UpdateChecksum(u32 checksum, const u8* in, size_t inSize) const
37  {
38 #if CODEC_COMPUTE_CHECKSUM
39  return (u32)crc32(checksum, in, (uInt)inSize);
40 #else
41  UNUSED2(checksum);
42  UNUSED2(in);
43  UNUSED2(inSize);
44  return 0;
45 #endif
46  }
47 
48 protected:
50  {
51 #if CODEC_COMPUTE_CHECKSUM
52  return crc32(0, 0, 0);
53 #else
54  return 0;
55 #endif
56  }
57 };
58 
59 
60 //-----------------------------------------------------------------------------
61 
62 class Codec_ZLibNone : public Codec_ZLib
63 {
64 public:
66  {
67  Reset();
68  }
69 
70  virtual ~Codec_ZLibNone()
71  {
72  }
73 
74  virtual size_t MaxOutputSize(size_t inSize) const
75  {
76  return inSize;
77  }
78 
79  virtual Status Reset()
80  {
82  return INFO::OK;
83  }
84 
85  virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
86  {
87  const size_t transferSize = std::min(inSize, outSize);
88  memcpy(out, in, transferSize);
89  inConsumed = outProduced = transferSize;
90  m_checksum = UpdateChecksum(m_checksum, out, outProduced);
91  return INFO::OK;
92  }
93 
94  virtual Status Finish(u32& checksum, size_t& outProduced)
95  {
96  outProduced = 0;
97  checksum = m_checksum;
98  return INFO::OK;
99  }
100 
101 private:
103 };
104 
105 
106 //-----------------------------------------------------------------------------
107 
109 {
110 protected:
112  {
113  memset(&m_zs, 0, sizeof(m_zs));
115  }
116 
117  static Status LibError_from_zlib(int zlib_ret)
118  {
119  switch(zlib_ret)
120  {
121  case Z_OK:
122  return INFO::OK;
123  case Z_STREAM_END:
125  case Z_MEM_ERROR:
127  case Z_DATA_ERROR:
129  case Z_STREAM_ERROR:
131  default:
133  }
134  }
135 
136  static void WarnIfZLibError(int zlib_ret)
137  {
138  (void)LibError_from_zlib(zlib_ret);
139  }
140 
141  typedef int ZEXPORT (*ZLibFunc)(z_streamp strm, int flush);
142 
143  Status CallStreamFunc(ZLibFunc func, int flush, const u8* in, const size_t inSize, u8* out, const size_t outSize, size_t& inConsumed, size_t& outProduced)
144  {
145  m_zs.next_in = (Byte*)in;
146  m_zs.avail_in = (uInt)inSize;
147  m_zs.next_out = (Byte*)out;
148  m_zs.avail_out = (uInt)outSize;
149 
150  int ret = func(&m_zs, flush);
151  // sanity check: if ZLib reports end of stream, all input data
152  // must have been consumed.
153  if(ret == Z_STREAM_END)
154  {
155  ENSURE(m_zs.avail_in == 0);
156  ret = Z_OK;
157  }
158 
159  ENSURE(inSize >= m_zs.avail_in && outSize >= m_zs.avail_out);
160  inConsumed = inSize - m_zs.avail_in;
161  outProduced = outSize - m_zs.avail_out;
162 
163  return LibError_from_zlib(ret);
164  }
165 
166  mutable z_stream m_zs;
167 
168  // note: z_stream does contain an 'adler' checksum field, but that's
169  // not updated in streams lacking a gzip header, so we'll have to
170  // calculate a checksum ourselves.
171  // adler32 is somewhat weaker than CRC32, but a more important argument
172  // is that we should use the latter for compatibility with Zip archives.
173  mutable u32 m_checksum;
174 };
175 
176 
177 //-----------------------------------------------------------------------------
178 
180 {
181 public:
183  {
184  // note: with Z_BEST_COMPRESSION, 78% percent of
185  // archive builder CPU time is spent in ZLib, even though
186  // that is interleaved with IO; everything else is negligible.
187  // we prefer faster speed at the cost of 1.5% larger archives.
188  const int level = Z_BEST_SPEED;
189  const int windowBits = -MAX_WBITS; // max window size; omit ZLib header
190  const int memLevel = 9; // max speed; total mem ~= 384KiB
191  const int strategy = Z_DEFAULT_STRATEGY; // normal data - not RLE
192  const int ret = deflateInit2(&m_zs, level, Z_DEFLATED, windowBits, memLevel, strategy);
193  ENSURE(ret == Z_OK);
194  }
195 
197  {
198  const int ret = deflateEnd(&m_zs);
199  WarnIfZLibError(ret);
200  }
201 
202  virtual size_t MaxOutputSize(size_t inSize) const
203  {
204  return (size_t)deflateBound(&m_zs, (uLong)inSize);
205  }
206 
207  virtual Status Reset()
208  {
210  const int ret = deflateReset(&m_zs);
211  return LibError_from_zlib(ret);
212  }
213 
214  virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
215  {
216  m_checksum = UpdateChecksum(m_checksum, in, inSize);
217  return CodecZLibStream::CallStreamFunc(deflate, 0, in, inSize, out, outSize, inConsumed, outProduced);
218  }
219 
220  virtual Status Finish(u32& checksum, size_t& outProduced)
221  {
222  const uInt availOut = m_zs.avail_out;
223 
224  // notify zlib that no more data is forthcoming and have it flush output.
225  // our output buffer has enough space due to use of deflateBound;
226  // therefore, deflate must return Z_STREAM_END.
227  const int ret = deflate(&m_zs, Z_FINISH);
228  ENSURE(ret == Z_STREAM_END);
229 
230  outProduced = size_t(availOut - m_zs.avail_out);
231 
232  checksum = m_checksum;
233  return INFO::OK;
234  }
235 };
236 
237 
238 //-----------------------------------------------------------------------------
239 
241 {
242 public:
244  {
245  const int windowBits = -MAX_WBITS; // max window size; omit ZLib header
246  const int ret = inflateInit2(&m_zs, windowBits);
247  ENSURE(ret == Z_OK);
248  }
249 
251  {
252  const int ret = inflateEnd(&m_zs);
253  WarnIfZLibError(ret);
254  }
255 
256  virtual size_t MaxOutputSize(size_t inSize) const
257  {
258  // relying on an upper bound for the output is a really bad idea for
259  // large files. archive formats store the uncompressed file sizes,
260  // so callers should use that when allocating the output buffer.
261  ENSURE(inSize < 1*MiB);
262 
263  return inSize*1032; // see http://www.zlib.org/zlib_tech.html
264  }
265 
266  virtual Status Reset()
267  {
269  const int ret = inflateReset(&m_zs);
270  return LibError_from_zlib(ret);
271  }
272 
273  virtual Status Process(const u8* in, size_t inSize, u8* out, size_t outSize, size_t& inConsumed, size_t& outProduced)
274  {
275  const Status ret = CodecZLibStream::CallStreamFunc(inflate, Z_SYNC_FLUSH, in, inSize, out, outSize, inConsumed, outProduced);
276  m_checksum = UpdateChecksum(m_checksum, out, outProduced);
277  return ret;
278  }
279 
280  virtual Status Finish(u32& checksum, size_t& outProduced)
281  {
282  // no action needed - decompression always flushes immediately.
283  outProduced = 0;
284 
285  checksum = m_checksum;
286  return INFO::OK;
287  }
288 };
289 
290 
291 //-----------------------------------------------------------------------------
292 
294 {
295  return PICodec(new Codec_ZLibNone);
296 }
297 
299 {
300  return PICodec(new Compressor_ZLib);
301 }
302 
304 {
305  return PICodec (new Decompressor_ZLib);
306 }
Status CallStreamFunc(ZLibFunc func, int flush, const u8 *in, const size_t inSize, u8 *out, const size_t outSize, size_t &inConsumed, size_t &outProduced)
Definition: codec_zlib.cpp:143
Definition: codec.h:34
#define u8
Definition: types.h:39
const Status OK
Definition: status.h:386
virtual ~Compressor_ZLib()
Definition: codec_zlib.cpp:196
virtual ~Codec_ZLibNone()
Definition: codec_zlib.cpp:70
static void out(const wchar_t *fmt,...)
Definition: wdbg_sym.cpp:419
PICodec CreateCodec_ZLibNone()
Definition: codec_zlib.cpp:293
const Status CORRUPTED
Definition: status.h:413
virtual Status Process(const u8 *in, size_t inSize, u8 *out, size_t outSize, size_t &inConsumed, size_t &outProduced)
process (i.e.
Definition: codec_zlib.cpp:273
PICodec CreateDecompressor_ZLibDeflate()
Definition: codec_zlib.cpp:303
int ZEXPORT(* ZLibFunc)(z_streamp strm, int flush)
Definition: codec_zlib.cpp:141
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
#define UNUSED2(param)
mark a function local variable or parameter as unused and avoid the corresponding compiler warning...
virtual Status Process(const u8 *in, size_t inSize, u8 *out, size_t outSize, size_t &inConsumed, size_t &outProduced)
process (i.e.
Definition: codec_zlib.cpp:214
virtual ~Decompressor_ZLib()
Definition: codec_zlib.cpp:250
virtual Status Process(const u8 *in, size_t inSize, u8 *out, size_t outSize, size_t &inConsumed, size_t &outProduced)
process (i.e.
Definition: codec_zlib.cpp:85
virtual size_t MaxOutputSize(size_t inSize) const
Definition: codec_zlib.cpp:202
virtual Status Finish(u32 &checksum, size_t &outProduced)
Flush buffers and make sure all output has been produced.
Definition: codec_zlib.cpp:94
static const size_t MiB
Definition: alignment.h:72
virtual size_t MaxOutputSize(size_t inSize) const
Definition: codec_zlib.cpp:74
virtual size_t MaxOutputSize(size_t inSize) const
Definition: codec_zlib.cpp:256
const Status INVALID_PARAM
Definition: status.h:423
virtual Status Reset()
clear all previous state and prepare for reuse.
Definition: codec_zlib.cpp:79
static Status LibError_from_zlib(int zlib_ret)
Definition: codec_zlib.cpp:117
static void WarnIfZLibError(int zlib_ret)
Definition: codec_zlib.cpp:136
i64 Status
Error handling system.
Definition: status.h:171
u32 UpdateChecksum(u32 checksum, const u8 *in, size_t inSize) const
update a checksum to reflect the contents of a buffer.
Definition: codec_zlib.cpp:36
virtual Status Finish(u32 &checksum, size_t &outProduced)
Flush buffers and make sure all output has been produced.
Definition: codec_zlib.cpp:280
virtual Status Reset()
clear all previous state and prepare for reuse.
Definition: codec_zlib.cpp:207
u32 InitializeChecksum()
Definition: codec_zlib.cpp:49
virtual Status Finish(u32 &checksum, size_t &outProduced)
Flush buffers and make sure all output has been produced.
Definition: codec_zlib.cpp:220
#define u32
Definition: types.h:41
#define WARN_RETURN(status)
Definition: status.h:255
const Status FAIL
Definition: status.h:406
shared_ptr< ICodec > PICodec
Definition: codec.h:92
PICodec CreateCompressor_ZLibDeflate()
Definition: codec_zlib.cpp:298
const Status NO_MEM
Definition: status.h:430
virtual Status Reset()
clear all previous state and prepare for reuse.
Definition: codec_zlib.cpp:266