KSquare Utilities
Compressor.cpp
Go to the documentation of this file.
1 /* Compressor.cpp -- Compresses and decompresses data using zLib Library
2  * Copyright (C) 1994-2011 Kurt Kramer
3  * For conditions of distribution and use, see copyright notice in KKB.h
4  */
5 #include "FirstIncludes.h"
6 #include <iostream>
7 #include <fstream>
8 #include <vector>
9 #include <string.h>
10 #include "MemoryDebug.h"
11 using namespace std;
12 
13 #include "zlib.h"
14 
15 
16 #include "Compressor.h"
17 #include "GlobalGoalKeeper.h"
18 #include "OSservices.h"
19 using namespace KKB;
20 
21 
22 void* Compressor::CreateCompressedBuffer (void* source,
23  kkuint32 sourceLen,
24  kkuint32& compressedBuffLen
25  )
26 {
27 #ifdef ZLIB_H
28 
29  // following code was lifted from example provided by zlib "zpipe.c"
30  kkint32 ret;
31  z_stream strm;
32 
33 
34  Bytef* outputBuffer = NULL;
35  kkuint32 outputBufferSize = sourceLen * 2;
36 
37  outputBuffer = new Bytef[outputBufferSize];
38 
39  uchar* compressedBuff = NULL;
40  compressedBuffLen = 0;
41 
42  // The first thing we do is to initialize the zlib state for compression using deflateInit(). This must be done
43  // before the first use of deflate(). The zalloc, zfree, and opaque fields in the strm structure must be initialized
44  // before calling deflateInit(). Here they are set to the zlib constant Z_NULL to request that zlib use the default
45  // memory allocation routines. An application may also choose to provide custom memory allocation routines here.
46  // deflateInit() will allocate on the order of 256K bytes for the internal state. (See zlib Technical Details.)
47  // deflateInit() is called with a pointer to the structure to be initialized and the compression level, which is
48  // an integer in the range of -1 to 9. Lower compression levels result in faster execution, but less compression.
49  // Higher levels result in greater compression, but slower execution. The zlib constant Z_DEFAULT_COMPRESSION, equal
50  // to -1, provides a good compromise between compression and speed and is equivalent to level 6. Level 0 actually
51  // does no compression at all, and in fact expands the data slightly to produce the zlib format (it is not a
52  // byte-for-byte copy of the input). More advanced applications of zlib may use deflateInit2() here instead. Such an
53  // application may want to reduce how much memory will be used, at some price in compression. Or it may need to request
54  // a gzip header and trailer instead of a zlib header and trailer, or raw encoding with no header or trailer at all.
55 
56  // We must check the return value of deflateInit() against the zlib constant Z_OK to make sure that it was able to
57  // allocate memory for the internal state, and that the provided arguments were valid. deflateInit() will also check
58  // that the version of zlib that the zlib.h file came from matches the version of zlib actually linked with the
59  // program. This is especially important for environments in which zlib is a shared library.
60 
61  // Note that an application can initialize multiple, independent zlib streams, which can operate in parallel. The state
62  // information maintained in the structure allows the zlib routines to be reentrant.
63 
64 
65  /* allocate deflate state */
66  strm.zalloc = Z_NULL;
67  strm.zfree = Z_NULL;
68  strm.opaque = Z_NULL;
69  ret = deflateInit (&strm, Z_DEFAULT_COMPRESSION); // 'Z_DEFAULT_COMPRESSION' will balance between speed and compression; 0 = no compression, 9 = best compression
70  if (ret != Z_OK)
71  {
72  // Initialization Failed
73  compressedBuffLen = 0;
74  return NULL;
75  }
76 
77  // With the pleasantries out of the way, now we can get down to business. The outer do-loop reads all of the input
78  // file and exits at the bottom of the loop once end-of-file is reached. This loop contains the only call of deflate().
79  // So we must make sure that all of the input data has been processed and that all of the output data has been generated
80  // and consumed before we fall out of the loop at the bottom.
81 
82  {
83  // This was originally a a loop that read from a file; but now we have a input buffer with a known amount of data.
84  strm.avail_in = sourceLen; // Number of bytes in 'source' to compress.
85  strm.next_in = (Bytef*)source; // pointer to data that needs to be compressed.
86 
87  do
88  {
89  // We will stay in this loop until all data in source is processed. If the output buffer is not large enough
90  // we may need to make more than one iteration.
91 
92  strm.avail_out = outputBufferSize;
93  strm.next_out = outputBuffer;
94 
95  // Now we call the compression engine itself, deflate(). It takes as many of the avail_in bytes at next_in as it can
96  // process, and writes as many as avail_out bytes to next_out. Those counters and pointers are then updated past the
97  // input data consumed and the output data written. It is the amount of output space available that may limit how much
98  // input is consumed. Hence the inner loop to make sure that all of the input is consumed by providing more output
99  // space each time. Since avail_in and next_in are updated by deflate(), we don't have to mess with those between
100  // deflate() calls until it's all used up.
101 
102  // The parameters to deflate() are a pointer to the stream structure containing the input and output information and
103  // the internal compression engine state, and a parameter indicating whether and how to flush data to the output.
104  // Normally deflate will consume several K bytes of input data before producing any output (except for the header),
105  // in order to accumulate statistics on the data for optimum compression. It will then put out a burst of compressed
106  // data, and proceed to consume more input before the next burst. Eventually, deflate() must be told to terminate the
107  // stream, complete the compression with provided input data, and write out the trailer check value. deflate() will
108  // continue to compress normally as long as the flush parameter is Z_NO_FLUSH. Once the Z_FINISH parameter is provided,
109  // deflate() will begin to complete the compressed output stream. However depending on how much output space is
110  // provided, deflate() may have to be called several times until it has provided the complete compressed stream, even
111  // after it has consumed all of the input. The flush parameter must continue to be Z_FINISH for those subsequent calls.
112 
113  ret = deflate (&strm, Z_FINISH);
114  if (ret == Z_STREAM_ERROR)
115  {
116  cerr << "Compressor::CreateCompressedBuffer ***ERROR** deflate returned error[" << ret << "]." << std::endl;
117  compressedBuffLen = 0;
118  return NULL;
119  }
120 
121  // Now we compute how much output deflate() provided on the last call, which is the difference between how much space
122  // was provided before the call, and how much output space is still available after the call. Then that data, if any,
123  // is written to the output file. We can then reuse the output buffer for the next call of deflate(). Again if there
124  // is a file i/o error, we call deflateEnd() before returning to avoid a memory leak.
125  kkint32 have = outputBufferSize - strm.avail_out;
126  {
127  if (compressedBuff == NULL)
128  {
129  compressedBuff = new uchar[have];
130  compressedBuffLen = have;
131  memcpy (compressedBuff, outputBuffer, have);
132  }
133  else
134  {
135  kkuint32 newCompressedBuffLen = compressedBuffLen + have;
136  uchar* newCompressedBuff = new uchar[newCompressedBuffLen];
137  memcpy (newCompressedBuff, compressedBuff, compressedBuffLen);
138  memcpy (newCompressedBuff + compressedBuffLen, outputBuffer, have);
139  delete[] compressedBuff;
140  compressedBuff = newCompressedBuff;
141  compressedBuffLen = newCompressedBuffLen;
142  }
143  }
144  } while (strm.avail_out == 0);
145 
146 
147  if (strm.avail_in != 0)
148  {
149  // There is still input data; so something went wrong. We will prompt a diagnostic message and return NULL
150  cerr << "Compressor::CreateCompressedBuffer - The input buffer was not fully processed." << std::endl;
151  delete[] compressedBuff; compressedBuff = NULL;
152  delete[] outputBuffer; outputBuffer = NULL;
153  return NULL;
154  }
155  }
156 
157  // The process is complete, but we still need to deallocate the state to avoid a memory leak (or rather more like a memory
158  // hemorrhage if you didn't do this). Then finally we can return with a happy return value.
159  /* clean up and return */
160  (void)deflateEnd (&strm);
161 
162  delete[] outputBuffer; outputBuffer = NULL;
163 
164  return compressedBuff;
165 
166 #else
167  // For right now the linux version will not be doing compression.
168  return NULL;
169 #endif
170 } /* CreateCompressedBuffer*/
171 
172 
173 
174 
175 
176 
177 void* Compressor::Decompress (const void* compressedBuff,
178  kkuint32 compressedBuffLen,
179  kkuint32& unCompressedLen
180  )
181 {
182 #ifdef ZLIB_H
183  if (compressedBuff == NULL)
184  return NULL;
185 
187 
188  kkuint32 have = 0;
189  uchar* unCompressedBuff = NULL;
190 
191  Bytef* outBuffer = NULL;
192  kkint32 outBufferLen = 0;
193 
194  kkint32 ret;
195  z_stream strm;
196 
197  /* allocate inflate state */
198  strm.zalloc = Z_NULL;
199  strm.zfree = Z_NULL;
200  strm.opaque = Z_NULL;
201  strm.avail_in = 0;
202  strm.next_in = Z_NULL;
203 
204  ret = inflateInit (&strm);
205  if (ret != Z_OK)
206  {
207  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflateInit' failed." << std::endl;
208  unCompressedLen = 0;
210  return NULL;
211  }
212 
213 
214  outBufferLen = compressedBuffLen * 4;
215  outBuffer = new Bytef[outBufferLen];
216 
217  strm.avail_in = compressedBuffLen;
218  strm.next_in = (Bytef*)compressedBuff; // Bytef = a far pointer to a unsigned char.
219  // Not sure if on some platforms this may cause a problem.
220 
221  /* run inflate() on input until output buffer not full */
222  do
223  {
224  strm.avail_out = outBufferLen;
225  strm.next_out = outBuffer;
226 
227  ret = inflate (&strm, Z_NO_FLUSH);
228  if (ret == Z_STREAM_ERROR)
229  {
230  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflate' failed." << std::endl;
231  delete[] outBuffer; outBuffer = NULL;
232  delete[] unCompressedBuff; unCompressedBuff = NULL;
233  unCompressedLen = 0;
235  return NULL;
236  }
237 
238  switch (ret)
239  {
240  case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */
241  case Z_DATA_ERROR:
242  case Z_MEM_ERROR:
243  {
244  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflate' failed." << std::endl;
245  (void)inflateEnd (&strm);
246  delete[] outBuffer; outBuffer = NULL;
247  delete[] unCompressedBuff; unCompressedBuff = NULL;
248  unCompressedLen = 0;
250  return NULL;
251  }
252  }
253 
254  have = outBufferLen - strm.avail_out;
255  if (unCompressedBuff == NULL)
256  {
257  unCompressedBuff = new uchar[have];
258  unCompressedLen = have;
259  memcpy (unCompressedBuff, outBuffer, have);
260  }
261  else
262  {
263  kkint32 newUnCompressedLen = unCompressedLen + have;
264  uchar* newUnCompressedBuff = new uchar[newUnCompressedLen];
265  memcpy (newUnCompressedBuff, unCompressedBuff, unCompressedLen);
266  memcpy (newUnCompressedBuff + unCompressedLen, outBuffer, have);
267  delete[] unCompressedBuff;
268  unCompressedBuff = newUnCompressedBuff;
269  unCompressedLen = newUnCompressedLen;
270  newUnCompressedBuff = NULL;
271  }
272  }
273  while (strm.avail_out == 0);
274 
275  /* clean up and return */
276  (void)inflateEnd(&strm);
277 
278  delete[] outBuffer;
279  outBuffer = NULL;
280 
282 
283  return unCompressedBuff;
284 #else
285  return NULL;
286 #endif
287 } /* Decompress */
288 
289 
290 
291 
292 
293 
294 void Compressor::Decompress (const void* compressedBuff,
295  kkuint32 compressedBuffLen,
296  uchar*& unCompressedBuff,
297  kkuint32& unCompressedBuffSize,
298  kkuint32& unCompressedBuffLen
299  )
300 {
301 #ifdef ZLIB_H
302  if (compressedBuff == NULL)
303  return;
304 
305  unCompressedBuffLen = 0;
306 
307  //kkuint32 have = 0;
308 
309  kkint32 ret;
310  z_stream strm;
311 
312  /* allocate inflate state */
313  strm.zalloc = Z_NULL;
314  strm.zfree = Z_NULL;
315  strm.opaque = Z_NULL;
316  strm.avail_in = 0;
317  strm.next_in = Z_NULL;
318 
319  ret = inflateInit (&strm);
320  if (ret != Z_OK)
321  {
322  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflateInit' failed." << std::endl;
323  unCompressedBuffLen = 0;
324  return;
325  }
326 
327 
328  if (unCompressedBuff == NULL)
329  {
330  unCompressedBuffSize = compressedBuffLen * 4;
331  unCompressedBuff = new uchar[unCompressedBuffSize];
332  }
333 
334  strm.avail_in = compressedBuffLen;
335  strm.next_in = (Bytef*)compressedBuff; // Bytef = a far pointer to a unsigned char.
336  // Not sure if on some platforms this may cause a problem.
337 
338  /* run inflate() on input until output buffer not full */
339  do
340  {
341  kkuint32 unCompressedBuffLeft = unCompressedBuffSize - unCompressedBuffLen;
342  if ((strm.avail_in * 1.2) > unCompressedBuffLeft)
343  {
344  kkuint32 increaseBy = (kkuint32)((strm.avail_in * 1.2) - unCompressedBuffLeft);
345  increaseBy = Max (increaseBy, (kkuint32)10240);
346 
347  kkuint32 newUncompressedBuffSize = unCompressedBuffSize + increaseBy;
348  uchar* newUnCompressedBuff = new uchar[newUncompressedBuffSize];
349  memcpy (newUnCompressedBuff, unCompressedBuff, unCompressedBuffSize);
350  delete unCompressedBuff;
351  unCompressedBuff = newUnCompressedBuff;
352  newUnCompressedBuff = NULL;
353  unCompressedBuffSize = newUncompressedBuffSize;
354  }
355 
356  strm.avail_out = unCompressedBuffSize - unCompressedBuffLen;
357  strm.next_out = (Bytef*)unCompressedBuff + unCompressedBuffLen;
358 
359  ret = inflate (&strm, Z_NO_FLUSH);
360  if (ret == Z_STREAM_ERROR)
361  {
362  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflate' failed." << std::endl;
363  unCompressedBuffLen = 0;
364  return;
365  }
366 
367  switch (ret)
368  {
369  case Z_NEED_DICT: ret = Z_DATA_ERROR; /* and fall through */
370  case Z_DATA_ERROR:
371  case Z_MEM_ERROR:
372  {
373  cerr << "Compressor::Decompress ***ERROR*** zlib function call 'inflate' failed." << std::endl;
374  (void)inflateEnd (&strm);
375  unCompressedBuffLen = 0;
376  return;
377  }
378  }
379 
380  unCompressedBuffLen = strm.total_out;
381  }
382  while (strm.avail_in > 0);
383 
384  /* clean up and return */
385  (void)inflateEnd(&strm);
386 
387  return;
388 #else
389  return;
390 #endif
391 } /* Decompress */
Simple class that will compress and decompress specified buffers using the routines provided in zlib...
Definition: Compressor.h:17
__int32 kkint32
Definition: KKBaseTypes.h:88
unsigned __int32 kkuint32
Definition: KKBaseTypes.h:89
static void * Decompress(const void *compressedBuff, kkuint32 compressedBuffLen, kkuint32 &unCompressedLen)
Definition: Compressor.cpp:177
static void * CreateCompressedBuffer(void *source, kkuint32 sourceLen, kkuint32 &compressedBuffLen)
Definition: Compressor.cpp:22
KKTHread * KKTHreadPtr
static void Decompress(const void *compressedBuff, kkuint32 compressedBuffLen, uchar *&unCompressedBuff, kkuint32 &unCompressedBuffSize, kkuint32 &unCompressedBuffLen)
Definition: Compressor.cpp:294
unsigned char uchar
Unsigned character.
Definition: KKBaseTypes.h:77
Maintains one instance of a GoalKeeper object that can be used anywhere in the application.