1 // miniz_tester.cpp
2 // Note: This module is not intended to make a good example, or be used for anything other than testing.
3 // It's something quick I put together last year to help regression test miniz/tinfl under Linux/Win32/Mac. It's derived from LZHAM's test module.
4 #ifdef _MSC_VER
5 #pragma warning (disable:4127) //  warning C4127: conditional expression is constant
6 #endif
7 
8 #if defined(__GNUC__)
9   // Ensure we get the 64-bit variants of the CRT's file I/O calls
10   #ifndef _FILE_OFFSET_BITS
11     #define _FILE_OFFSET_BITS 64
12   #endif
13   #ifndef _LARGEFILE64_SOURCE
14     #define _LARGEFILE64_SOURCE 1
15   #endif
16 #endif
17 
18 #include "miniz.h"
19 #include "miniz_zip.h"
20 
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <assert.h>
24 #include <memory.h>
25 #include <stdarg.h>
26 #include <malloc.h>
27 #include <vector>
28 #include <string>
29 #include <limits.h>
30 #include <sys/stat.h>
31 
32 #include "timer.h"
33 
34 #define my_max(a,b) (((a) > (b)) ? (a) : (b))
35 #define my_min(a,b) (((a) < (b)) ? (a) : (b))
36 
37 typedef unsigned char uint8;
38 typedef unsigned short uint16;
39 typedef unsigned int uint;
40 
41 #define TDEFL_PRINT_OUTPUT_PROGRESS
42 
43 #if defined(WIN32)
44    #define WIN32_LEAN_AND_MEAN
45    #include <windows.h>
46    #define FILE_STAT_STRUCT _stat
47    #define FILE_STAT _stat
48 #else
49    #include <unistd.h>
50    #define Sleep(ms) usleep(ms*1000)
51    #define _aligned_malloc(size, alignment) memalign(alignment, size)
52    #define _aligned_free free
53    #define fopen fopen64
54    #define _fseeki64 fseeko64
55    #define _ftelli64 ftello64
56    #define _stricmp strcasecmp
57    #define FILE_STAT_STRUCT stat64
58    #define FILE_STAT stat64
59 #endif
60 
61 #ifdef WIN32
62 #define QUAD_INT_FMT "%I64u"
63 #else
64 #define QUAD_INT_FMT "%llu"
65 #endif
66 
67 #ifdef _DEBUG
68 const bool g_is_debug = true;
69 #else
70 const bool g_is_debug = false;
71 #endif
72 
73 typedef unsigned char uint8;
74 typedef unsigned int uint;
75 typedef unsigned int uint32;
76 typedef unsigned long long uint64;
77 typedef long long int64;
78 
79 #define TDEFLTEST_COMP_INPUT_BUFFER_SIZE 1024*1024*2
80 #define TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE 1024*1024*2
81 #define TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE 1024*1024*2
82 
83 static float s_max_small_comp_ratio, s_max_large_comp_ratio;
84 
85 struct comp_options
86 {
comp_optionscomp_options87   comp_options() :
88     m_level(7),
89     m_unbuffered_decompression(false),
90     m_verify_compressed_data(false),
91     m_randomize_params(false),
92     m_randomize_buffer_sizes(false),
93     m_z_strat(Z_DEFAULT_STRATEGY),
94     m_random_z_flushing(false),
95     m_write_zlib_header(true),
96     m_archive_test(false),
97     m_write_archives(false)
98   {
99   }
100 
printcomp_options101   void print()
102   {
103     printf("Level: %u\n", m_level);
104     printf("Write zlib header: %u\n", (uint)m_write_zlib_header);
105     printf("Unbuffered decompression: %u\n", (uint)m_unbuffered_decompression);
106     printf("Verify compressed data: %u\n", (uint)m_verify_compressed_data);
107     printf("Randomize parameters: %u\n", m_randomize_params);
108     printf("Randomize buffer sizes: %u\n", m_randomize_buffer_sizes);
109     printf("Deflate strategy: %u\n", m_z_strat);
110     printf("Random Z stream flushing: %u\n", m_random_z_flushing);
111     printf("Archive test: %u\n", m_archive_test);
112     printf("Write archives: %u\n", m_write_archives);
113   }
114 
115   uint m_level;
116   bool m_unbuffered_decompression;
117   bool m_verify_compressed_data;
118   bool m_randomize_params;
119   bool m_randomize_buffer_sizes;
120   uint m_z_strat;
121   bool m_random_z_flushing;
122   bool m_write_zlib_header;
123   bool m_archive_test;
124   bool m_write_archives;
125 };
126 
127 #define RND_SHR3(x)  (x ^= (x << 17), x ^= (x >> 13), x ^= (x << 5))
128 
129 #if 0
130 static void random_fill(uint8 *pDst, size_t len, uint32 x)
131 {
132   x ^= (x << 16);
133   if (!x) x++;
134 
135   while (len)
136   {
137     RND_SHR3(x); uint64 l0 = x & 0xFFF;
138     RND_SHR3(x); uint64 l1 = x & 0xFFF;
139     RND_SHR3(x); uint64 l2 = x & 0xFFF;
140     RND_SHR3(x); uint c = x;
141 
142     uint l = (uint)(((l0*l1*l2)/(16769025ULL) * 32) / 4095);
143     l = (uint)my_max(1,my_min(l, len));
144     len -= l;
145 
146     while (l--)
147     {
148       *pDst++ = (uint8)c;
149     }
150 
151     if (((int)x < 0) && len)
152     {
153       *pDst++ = 0;
154       len--;
155     }
156   }
157 }
158 #endif
159 
print_usage()160 static void print_usage()
161 {
162   printf("Usage: [options] [mode] inpath/infile [outfile]\n");
163   printf("\n");
164   printf("Modes:\n");
165   printf("c - Compress \"infile\" to \"outfile\"\n");
166   printf("d - Decompress \"infile\" to \"outfile\"\n");
167   printf("a - Recursively compress all files under \"inpath\"\n");
168   printf("r - Archive decompression test\n");
169   printf("\n");
170   printf("Options:\n");
171   printf("-m[0-10] - Compression level: 0=fastest (Huffman only), 9=best (10=uber)\n");
172   printf("-u - Use unbuffered decompression on files that can fit into memory.\n");
173   printf("     Unbuffered decompression is faster, but may have more I/O overhead.\n");
174   printf("-v - Immediately decompress compressed file after compression for verification.\n");
175   printf("-z - Do not write zlib header\n");
176   printf("-r - Randomize parameters during recursive testing\n");
177   printf("-b - Randomize input/output buffer sizes\n");
178   printf("-h - Use random z_flushing\n");
179   printf("-x# - Set rand() seed to value\n");
180   printf("-t# - Set z_strategy to value [0-4]\n");
181   printf("-a - Create single-file archives instead of files during testing\n");
182   printf("-w - Test archive cloning\n");
183 }
184 
print_error(const char * pMsg,...)185 static void print_error(const char *pMsg, ...)
186 {
187   char buf[1024];
188 
189   va_list args;
190   va_start(args, pMsg);
191   vsnprintf(buf, sizeof(buf), pMsg, args);
192   va_end(args);
193 
194   buf[sizeof(buf) - 1] = '\0';
195 
196   fprintf(stderr, "Error: %s", buf);
197 }
198 
open_file_with_retries(const char * pFilename,const char * pMode)199 static FILE* open_file_with_retries(const char *pFilename, const char* pMode)
200 {
201   const uint cNumRetries = 8;
202   for (uint i = 0; i < cNumRetries; i++)
203   {
204     FILE* pFile = fopen(pFilename, pMode);
205     if (pFile)
206       return pFile;
207     Sleep(250);
208   }
209   return NULL;
210 }
211 
ensure_file_exists_and_is_readable(const char * pFilename)212 static bool ensure_file_exists_and_is_readable(const char *pFilename)
213 {
214   FILE *p = fopen(pFilename, "rb");
215   if (!p)
216     return false;
217 
218   _fseeki64(p, 0, SEEK_END);
219   uint64 src_file_size = _ftelli64(p);
220   _fseeki64(p, 0, SEEK_SET);
221 
222   if (src_file_size)
223   {
224     char buf[1];
225     if (fread(buf, 1, 1, p) != 1)
226     {
227       fclose(p);
228       return false;
229     }
230   }
231   fclose(p);
232   return true;
233 }
234 
ensure_file_is_writable(const char * pFilename)235 static bool ensure_file_is_writable(const char *pFilename)
236 {
237   const int cNumRetries = 8;
238   for (int i = 0; i < cNumRetries; i++)
239   {
240     FILE *pFile = fopen(pFilename, "wb");
241     if (pFile)
242     {
243       fclose(pFile);
244       return true;
245     }
246     Sleep(250);
247   }
248   return false;
249 }
250 
simple_test1(const comp_options & options)251 static int simple_test1(const comp_options &options)
252 {
253   (void)options;
254 
255   size_t cmp_len = 0;
256 
257   const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456";
258   size_t uncomp_len = strlen(p);
259 
260   void *pComp_data = tdefl_compress_mem_to_heap(p, uncomp_len, &cmp_len, TDEFL_WRITE_ZLIB_HEADER);
261   if (!pComp_data)
262   {
263     free(pComp_data);
264     print_error("Compression test failed!\n");
265     return EXIT_FAILURE;
266   }
267 
268   printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len);
269 
270   size_t decomp_len = 0;
271   void *pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, cmp_len, &decomp_len, TINFL_FLAG_PARSE_ZLIB_HEADER);
272 
273   if ((!pDecomp_data) || (decomp_len != uncomp_len) || (memcmp(pDecomp_data, p, uncomp_len)))
274   {
275     free(pComp_data);
276     free(pDecomp_data);
277     print_error("Compression test failed!\n");
278     return EXIT_FAILURE;
279   }
280 
281   printf("Low-level API compression test succeeded.\n");
282 
283   free(pComp_data);
284   free(pDecomp_data);
285 
286   return EXIT_SUCCESS;
287 }
288 
simple_test2(const comp_options & options)289 static int simple_test2(const comp_options &options)
290 {
291   (void)options;
292 
293   uint8 cmp_buf[1024], decomp_buf[1024];
294   uLong cmp_len = sizeof(cmp_buf);
295 
296   const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456";
297   uLong uncomp_len = (uLong)strlen(p);
298 
299   int status = compress(cmp_buf, &cmp_len, (const uint8*)p, uncomp_len);
300   if (status != Z_OK)
301   {
302     print_error("Compression test failed!\n");
303     return EXIT_FAILURE;
304   }
305 
306   printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len);
307 
308   if (cmp_len > compressBound(uncomp_len))
309   {
310     print_error("compressBound() returned bogus result\n");
311     return EXIT_FAILURE;
312   }
313 
314   uLong decomp_len = sizeof(decomp_buf);
315   status = uncompress(decomp_buf, &decomp_len, cmp_buf, cmp_len);;
316 
317   if ((status != Z_OK) || (decomp_len != uncomp_len) || (memcmp(decomp_buf, p, uncomp_len)))
318   {
319     print_error("Compression test failed!\n");
320     return EXIT_FAILURE;
321   }
322 
323   printf("zlib API compression test succeeded.\n");
324 
325   return EXIT_SUCCESS;
326 }
327 
compress_file_zlib(const char * pSrc_filename,const char * pDst_filename,const comp_options & options)328 static bool compress_file_zlib(const char* pSrc_filename, const char *pDst_filename, const comp_options &options)
329 {
330   printf("Testing: Streaming zlib compression\n");
331 
332   FILE *pInFile = fopen(pSrc_filename, "rb");
333   if (!pInFile)
334   {
335     print_error("Unable to read file: %s\n", pSrc_filename);
336     return false;
337   }
338 
339   FILE *pOutFile = fopen(pDst_filename, "wb");
340   if (!pOutFile)
341   {
342     print_error("Unable to create file: %s\n", pDst_filename);
343     return false;
344   }
345 
346   _fseeki64(pInFile, 0, SEEK_END);
347   uint64 src_file_size = _ftelli64(pInFile);
348   _fseeki64(pInFile, 0, SEEK_SET);
349 
350   fputc('D', pOutFile); fputc('E', pOutFile); fputc('F', pOutFile); fputc('0', pOutFile);
351   fputc(options.m_write_zlib_header, pOutFile);
352 
353   for (uint i = 0; i < 8; i++)
354     fputc(static_cast<int>((src_file_size >> (i * 8)) & 0xFF), pOutFile);
355 
356   uint cInBufSize = TDEFLTEST_COMP_INPUT_BUFFER_SIZE;
357   uint cOutBufSize = TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE;
358   if (options.m_randomize_buffer_sizes)
359   {
360     cInBufSize = 1 + (rand() % 4096);
361     cOutBufSize = 1 + (rand() % 4096);
362   }
363   printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, cOutBufSize);
364 
365   uint8 *in_file_buf = static_cast<uint8*>(_aligned_malloc(cInBufSize, 16));
366   uint8 *out_file_buf = static_cast<uint8*>(_aligned_malloc(cOutBufSize, 16));
367   if ((!in_file_buf) || (!out_file_buf))
368   {
369     print_error("Out of memory!\n");
370     _aligned_free(in_file_buf);
371     _aligned_free(out_file_buf);
372     fclose(pInFile);
373     fclose(pOutFile);
374     return false;
375   }
376 
377   uint64 src_bytes_left = src_file_size;
378 
379   uint in_file_buf_size = 0;
380   uint in_file_buf_ofs = 0;
381 
382   uint64 total_output_bytes = 0;
383 
384   timer_ticks start_time = timer::get_ticks();
385 
386   z_stream zstream;
387   memset(&zstream, 0, sizeof(zstream));
388 
389   timer_ticks init_start_time = timer::get_ticks();
390   int status = deflateInit2(&zstream, options.m_level, Z_DEFLATED, options.m_write_zlib_header ? Z_DEFAULT_WINDOW_BITS : -Z_DEFAULT_WINDOW_BITS, 9, options.m_z_strat);
391   timer_ticks total_init_time = timer::get_ticks() - init_start_time;
392 
393   if (status != Z_OK)
394   {
395     print_error("Failed initializing compressor!\n");
396     _aligned_free(in_file_buf);
397     _aligned_free(out_file_buf);
398     fclose(pInFile);
399     fclose(pOutFile);
400     return false;
401   }
402 
403   printf("deflateInit2() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f);
404 
405   uint32 x = my_max(1, (uint32)(src_file_size ^ (src_file_size >> 32)));
406 
407   for ( ; ; )
408   {
409     if (src_file_size)
410     {
411       double total_elapsed_time = timer::ticks_to_secs(timer::get_ticks() - start_time);
412       double total_bytes_processed = static_cast<double>(src_file_size - src_bytes_left);
413       double comp_rate = (total_elapsed_time > 0.0f) ? total_bytes_processed / total_elapsed_time : 0.0f;
414 
415 #ifdef TDEFL_PRINT_OUTPUT_PROGRESS
416       for (int i = 0; i < 15; i++)
417         printf("\b\b\b\b");
418       printf("Progress: %3.1f%%, Bytes Remaining: %3.1fMB, %3.3fMB/sec", (1.0f - (static_cast<float>(src_bytes_left) / src_file_size)) * 100.0f, src_bytes_left / 1048576.0f, comp_rate / (1024.0f * 1024.0f));
419       printf("                \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
420 #endif
421     }
422 
423     if (in_file_buf_ofs == in_file_buf_size)
424     {
425       in_file_buf_size = static_cast<uint>(my_min(cInBufSize, src_bytes_left));
426 
427       if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size)
428       {
429         printf("\n");
430         print_error("Failure reading from source file!\n");
431         _aligned_free(in_file_buf);
432         _aligned_free(out_file_buf);
433         fclose(pInFile);
434         fclose(pOutFile);
435         deflateEnd(&zstream);
436         return false;
437       }
438 
439       src_bytes_left -= in_file_buf_size;
440 
441       in_file_buf_ofs = 0;
442     }
443 
444     zstream.next_in = &in_file_buf[in_file_buf_ofs];
445     zstream.avail_in = in_file_buf_size - in_file_buf_ofs;
446     zstream.next_out = out_file_buf;
447     zstream.avail_out = cOutBufSize;
448 
449     int flush = !src_bytes_left ? Z_FINISH : Z_NO_FLUSH;
450     if ((flush == Z_NO_FLUSH) && (options.m_random_z_flushing))
451     {
452       RND_SHR3(x);
453       if ((x & 15) == 0)
454       {
455         RND_SHR3(x);
456         flush = (x & 31) ? Z_SYNC_FLUSH : Z_FULL_FLUSH;
457       }
458     }
459     status = deflate(&zstream, flush);
460 
461     uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs) - zstream.avail_in;
462     uint num_out_bytes = cOutBufSize - zstream.avail_out;
463     if (num_in_bytes)
464     {
465       in_file_buf_ofs += (uint)num_in_bytes;
466       assert(in_file_buf_ofs <= in_file_buf_size);
467     }
468 
469     if (num_out_bytes)
470     {
471       if (fwrite(out_file_buf, 1, static_cast<uint>(num_out_bytes), pOutFile) != num_out_bytes)
472       {
473         printf("\n");
474         print_error("Failure writing to destination file!\n");
475         _aligned_free(in_file_buf);
476         _aligned_free(out_file_buf);
477         fclose(pInFile);
478         fclose(pOutFile);
479         deflateEnd(&zstream);
480         return false;
481       }
482 
483       total_output_bytes += num_out_bytes;
484     }
485 
486     if (status != Z_OK)
487       break;
488   }
489 
490 #ifdef TDEFL_PRINT_OUTPUT_PROGRESS
491   for (int i = 0; i < 15; i++)
492   {
493     printf("\b\b\b\b    \b\b\b\b");
494   }
495 #endif
496 
497   src_bytes_left += (in_file_buf_size - in_file_buf_ofs);
498 
499   uint32 adler32 = zstream.adler;
500   deflateEnd(&zstream);
501 
502   timer_ticks end_time = timer::get_ticks();
503   double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time));
504 
505   uint64 cmp_file_size = _ftelli64(pOutFile);
506 
507   _aligned_free(in_file_buf);
508   in_file_buf = NULL;
509   _aligned_free(out_file_buf);
510   out_file_buf = NULL;
511 
512   fclose(pInFile);
513   pInFile = NULL;
514   fclose(pOutFile);
515   pOutFile = NULL;
516 
517   if (status != Z_STREAM_END)
518   {
519     print_error("Compression failed with status %i\n", status);
520     return false;
521   }
522 
523   if (src_bytes_left)
524   {
525     print_error("Compressor failed to consume entire input file!\n");
526     return false;
527   }
528 
529   printf("Success\n");
530   printf("Input file size: " QUAD_INT_FMT ", Compressed file size: " QUAD_INT_FMT ", Ratio: %3.2f%%\n", src_file_size, cmp_file_size, src_file_size ? ((1.0f - (static_cast<float>(cmp_file_size) / src_file_size)) * 100.0f) : 0.0f);
531   printf("Compression time: %3.6f\nConsumption rate: %9.1f bytes/sec, Emission rate: %9.1f bytes/sec\n", total_time, src_file_size / total_time, cmp_file_size / total_time);
532   printf("Input file adler32: 0x%08X\n", adler32);
533   if (src_file_size)
534   {
535     if (src_file_size >= 256)
536       s_max_large_comp_ratio = my_max(s_max_large_comp_ratio, cmp_file_size / (float)src_file_size);
537     else
538       s_max_small_comp_ratio = my_max(s_max_small_comp_ratio, cmp_file_size / (float)src_file_size);
539   }
540   //printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio);
541 
542   return true;
543 }
544 
decompress_file_zlib(const char * pSrc_filename,const char * pDst_filename,comp_options options)545 static bool decompress_file_zlib(const char* pSrc_filename, const char *pDst_filename, comp_options options)
546 {
547   FILE *pInFile = fopen(pSrc_filename, "rb");
548   if (!pInFile)
549   {
550     print_error("Unable to read file: %s\n", pSrc_filename);
551     return false;
552   }
553 
554   _fseeki64(pInFile, 0, SEEK_END);
555   uint64 src_file_size = _ftelli64(pInFile);
556   _fseeki64(pInFile, 0, SEEK_SET);
557   if (src_file_size < (5+9))
558   {
559     print_error("Compressed file is too small!\n");
560     fclose(pInFile);
561     return false;
562   }
563 
564   int h0 = fgetc(pInFile);
565   int h1 = fgetc(pInFile);
566   int h2 = fgetc(pInFile);
567   int h3 = fgetc(pInFile);
568   int zlib_header = fgetc(pInFile);
569   if ((h0 != 'D') | (h1 != 'E') || (h2 != 'F') || (h3 != '0'))
570   {
571     print_error("Unrecognized/invalid header in file: %s\n", pSrc_filename);
572     fclose(pInFile);
573     return false;
574   }
575 
576   FILE *pOutFile = fopen(pDst_filename, "wb");
577   if (!pOutFile)
578   {
579     print_error("Unable to create file: %s\n", pDst_filename);
580     fclose(pInFile);
581     return false;
582   }
583 
584   uint64 orig_file_size = 0;
585   for (uint i = 0; i < 8; i++)
586     orig_file_size |= (static_cast<uint64>(fgetc(pInFile)) << (i * 8));
587 
588   int total_header_bytes = ftell(pInFile);
589 
590   // Avoid running out of memory on large files when using unbuffered decompression.
591   if ((options.m_unbuffered_decompression) && (orig_file_size > 768*1024*1024))
592   {
593     printf("Output file is too large for unbuffered decompression - switching to streaming decompression.\n");
594     options.m_unbuffered_decompression = false;
595   }
596 
597   if (options.m_unbuffered_decompression)
598     printf("Testing: Unbuffered decompression\n");
599   else
600     printf("Testing: Streaming decompression\n");
601 
602   uint cInBufSize = options.m_unbuffered_decompression ? static_cast<uint>(src_file_size) : TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE;
603   uint out_buf_size = options.m_unbuffered_decompression ? static_cast<uint>(orig_file_size) : TINFL_LZ_DICT_SIZE;
604 
605   if ((options.m_randomize_buffer_sizes) && (!options.m_unbuffered_decompression))
606   {
607     cInBufSize = 1 + (rand() % 4096);
608   }
609 
610   printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, out_buf_size);
611 
612   uint8 *in_file_buf = static_cast<uint8*>(_aligned_malloc(cInBufSize, 16));
613   uint8 *out_file_buf = static_cast<uint8*>(_aligned_malloc(out_buf_size, 16));
614   if ((!in_file_buf) || (!out_file_buf))
615   {
616     print_error("Failed allocating output buffer!\n");
617     _aligned_free(in_file_buf);
618     fclose(pInFile);
619     fclose(pOutFile);
620     return false;
621   }
622 
623   uint64 src_bytes_left = src_file_size - total_header_bytes;
624   uint64 dst_bytes_left = orig_file_size;
625 
626   uint in_file_buf_size = 0;
627   uint in_file_buf_ofs = 0;
628   uint out_file_buf_ofs = 0;
629 
630   timer_ticks start_time = timer::get_ticks();
631   double decomp_only_time = 0;
632 
633   z_stream zstream;
634   memset(&zstream, 0, sizeof(zstream));
635 
636   timer_ticks init_start_time = timer::get_ticks();
637   int status = zlib_header ? inflateInit(&zstream) : inflateInit2(&zstream, -Z_DEFAULT_WINDOW_BITS);
638   timer_ticks total_init_time = timer::get_ticks() - init_start_time;
639   if (status != Z_OK)
640   {
641     print_error("Failed initializing decompressor!\n");
642     _aligned_free(in_file_buf);
643     _aligned_free(out_file_buf);
644     fclose(pInFile);
645     fclose(pOutFile);
646     return false;
647   }
648 
649   printf("inflateInit() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f);
650 
651   for ( ; ; )
652   {
653     if (in_file_buf_ofs == in_file_buf_size)
654     {
655       in_file_buf_size = static_cast<uint>(my_min(cInBufSize, src_bytes_left));
656 
657       if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size)
658       {
659         print_error("Failure reading from source file!\n");
660         _aligned_free(in_file_buf);
661         _aligned_free(out_file_buf);
662         deflateEnd(&zstream);
663         fclose(pInFile);
664         fclose(pOutFile);
665         return false;
666       }
667 
668       src_bytes_left -= in_file_buf_size;
669 
670       in_file_buf_ofs = 0;
671     }
672 
673     uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs);
674     uint num_out_bytes = (out_buf_size - out_file_buf_ofs);
675     zstream.next_in = in_file_buf + in_file_buf_ofs;
676     zstream.avail_in = num_in_bytes;
677     zstream.next_out = out_file_buf + out_file_buf_ofs;
678     zstream.avail_out = num_out_bytes;
679 
680     {
681       timer decomp_only_timer;
682       decomp_only_timer.start();
683       status = inflate(&zstream, options.m_unbuffered_decompression ? Z_FINISH : Z_SYNC_FLUSH);
684       decomp_only_time += decomp_only_timer.get_elapsed_secs();
685     }
686     num_in_bytes -= zstream.avail_in;
687     num_out_bytes -= zstream.avail_out;
688 
689     if (num_in_bytes)
690     {
691       in_file_buf_ofs += (uint)num_in_bytes;
692       assert(in_file_buf_ofs <= in_file_buf_size);
693     }
694 
695     out_file_buf_ofs += (uint)num_out_bytes;
696 
697     if ((out_file_buf_ofs == out_buf_size) || (status == Z_STREAM_END))
698     {
699       if (fwrite(out_file_buf, 1, static_cast<uint>(out_file_buf_ofs), pOutFile) != out_file_buf_ofs)
700       {
701         print_error("Failure writing to destination file!\n");
702         _aligned_free(in_file_buf);
703         _aligned_free(out_file_buf);
704         inflateEnd(&zstream);
705         fclose(pInFile);
706         fclose(pOutFile);
707         return false;
708       }
709       out_file_buf_ofs = 0;
710     }
711 
712     if (num_out_bytes > dst_bytes_left)
713     {
714       print_error("Decompressor wrote too many bytes to destination file!\n");
715       _aligned_free(in_file_buf);
716       _aligned_free(out_file_buf);
717       inflateEnd(&zstream);
718       fclose(pInFile);
719       fclose(pOutFile);
720       return false;
721     }
722     dst_bytes_left -= num_out_bytes;
723 
724     if (status != Z_OK)
725       break;
726   }
727   _aligned_free(in_file_buf);
728   in_file_buf = NULL;
729 
730   _aligned_free(out_file_buf);
731   out_file_buf = NULL;
732 
733   src_bytes_left += (in_file_buf_size - in_file_buf_ofs);
734 
735   uint32 adler32 = zstream.adler;
736   inflateEnd(&zstream);
737 
738   timer_ticks end_time = timer::get_ticks();
739   double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time));
740 
741   fclose(pInFile);
742   pInFile = NULL;
743 
744   fclose(pOutFile);
745   pOutFile = NULL;
746 
747   if (status != Z_STREAM_END)
748   {
749     print_error("Decompression FAILED with status %i\n", status);
750     return false;
751   }
752 
753   if ((src_file_size < UINT_MAX) && (orig_file_size < UINT_MAX))
754   {
755     if ((((size_t)zstream.total_in + total_header_bytes) != src_file_size) || (zstream.total_out != orig_file_size))
756     {
757       print_error("Decompression FAILED to consume all input or write all expected output!\n");
758       return false;
759     }
760   }
761 
762   if (dst_bytes_left)
763   {
764     print_error("Decompressor FAILED to output the entire output file!\n");
765     return false;
766   }
767 
768   if (src_bytes_left)
769   {
770     print_error("Decompressor FAILED to read " QUAD_INT_FMT " bytes from input buffer\n", src_bytes_left);
771   }
772 
773   printf("Success\n");
774   printf("Source file size: " QUAD_INT_FMT ", Decompressed file size: " QUAD_INT_FMT "\n", src_file_size, orig_file_size);
775   if (zlib_header) printf("Decompressed adler32: 0x%08X\n", adler32);
776   printf("Overall decompression time (decompression init+I/O+decompression): %3.6f\n  Consumption rate: %9.1f bytes/sec, Decompression rate: %9.1f bytes/sec\n", total_time, src_file_size / total_time, orig_file_size / total_time);
777   printf("Decompression only time (not counting decompression init or I/O): %3.6f\n  Consumption rate: %9.1f bytes/sec, Decompression rate: %9.1f bytes/sec\n", decomp_only_time, src_file_size / decomp_only_time, orig_file_size / decomp_only_time);
778 
779   return true;
780 }
781 
compare_files(const char * pFilename1,const char * pFilename2)782 static bool compare_files(const char *pFilename1, const char* pFilename2)
783 {
784   FILE* pFile1 = open_file_with_retries(pFilename1, "rb");
785   if (!pFile1)
786   {
787     print_error("Failed opening file: %s\n", pFilename1);
788     return false;
789   }
790 
791   FILE* pFile2 = open_file_with_retries(pFilename2, "rb");
792   if (!pFile2)
793   {
794     print_error("Failed opening file: %s\n", pFilename2);
795     fclose(pFile1);
796     return false;
797   }
798 
799   _fseeki64(pFile1, 0, SEEK_END);
800   int64 fileSize1 = _ftelli64(pFile1);
801   _fseeki64(pFile1, 0, SEEK_SET);
802 
803   _fseeki64(pFile2, 0, SEEK_END);
804   int64 fileSize2 = _ftelli64(pFile2);
805   _fseeki64(pFile2, 0, SEEK_SET);
806 
807   if (fileSize1 != fileSize2)
808   {
809     print_error("Files to compare are not the same size: %I64i vs. %I64i.\n", fileSize1, fileSize2);
810     fclose(pFile1);
811     fclose(pFile2);
812     return false;
813   }
814 
815   const uint cBufSize = 1024 * 1024;
816   std::vector<uint8> buf1(cBufSize);
817   std::vector<uint8> buf2(cBufSize);
818 
819   int64 bytes_remaining = fileSize1;
820   while (bytes_remaining)
821   {
822     const uint bytes_to_read = static_cast<uint>(my_min(cBufSize, bytes_remaining));
823 
824     if (fread(&buf1.front(), bytes_to_read, 1, pFile1) != 1)
825     {
826       print_error("Failed reading from file: %s\n", pFilename1);
827       fclose(pFile1);
828       fclose(pFile2);
829       return false;
830     }
831 
832     if (fread(&buf2.front(), bytes_to_read, 1, pFile2) != 1)
833     {
834       print_error("Failed reading from file: %s\n", pFilename2);
835       fclose(pFile1);
836       fclose(pFile2);
837       return false;
838     }
839 
840     if (memcmp(&buf1.front(), &buf2.front(), bytes_to_read) != 0)
841     {
842       print_error("File data comparison failed!\n");
843       fclose(pFile1);
844       fclose(pFile2);
845       return false;
846     }
847 
848     bytes_remaining -= bytes_to_read;
849   }
850 
851   fclose(pFile1);
852   fclose(pFile2);
853   return true;
854 }
855 
zip_create(const char * pZip_filename,const char * pSrc_filename)856 static bool zip_create(const char *pZip_filename, const char *pSrc_filename)
857 {
858   mz_zip_archive zip;
859   memset(&zip, 0, sizeof(zip));
860   if ((rand() % 100) >= 10)
861     zip.m_file_offset_alignment = 1 << (rand() & 15);
862   if (!mz_zip_writer_init_file(&zip, pZip_filename, 65537))
863   {
864     print_error("Failed creating zip archive \"%s\" (1)!\n", pZip_filename);
865     return false;
866   }
867 
868   mz_bool success = MZ_TRUE;
869 
870   const char *pStr = "This is a test!This is a test!This is a test!\n";
871   size_t comp_size;
872   void *pComp_data = tdefl_compress_mem_to_heap(pStr, strlen(pStr), &comp_size, 256);
873   success &= mz_zip_writer_add_mem_ex(&zip, "precomp.txt", pComp_data, comp_size, "Comment", (uint16)strlen("Comment"), MZ_ZIP_FLAG_COMPRESSED_DATA, strlen(pStr), mz_crc32(MZ_CRC32_INIT, (const uint8 *)pStr, strlen(pStr)));
874 
875   success &= mz_zip_writer_add_mem(&zip, "cool/", NULL, 0, 0);
876 
877   success &= mz_zip_writer_add_mem(&zip, "1.txt", pStr, strlen(pStr), 9);
878   int n = rand() & 4095;
879   for (int i = 0; i < n; i++)
880   {
881     char name[256], buf[256], comment[256];
882     sprintf(name, "t%u.txt", i);
883     sprintf(buf, "%u\n", i*5377);
884     sprintf(comment, "comment: %u\n", i);
885     success &= mz_zip_writer_add_mem_ex(&zip, name, buf, strlen(buf), comment, (uint16)strlen(comment), i % 10, 0, 0);
886   }
887 
888   const char *pTestComment = "test comment";
889   success &= mz_zip_writer_add_file(&zip, "test.bin", pSrc_filename, pTestComment, (uint16)strlen(pTestComment), 9);
890 
891   if (ensure_file_exists_and_is_readable("changelog.txt"))
892     success &= mz_zip_writer_add_file(&zip, "changelog.txt", "changelog.txt", "This is a comment", (uint16)strlen("This is a comment"), 9);
893 
894   if (!success)
895   {
896     mz_zip_writer_end(&zip);
897     remove(pZip_filename);
898     print_error("Failed creating zip archive \"%s\" (2)!\n", pZip_filename);
899     return false;
900   }
901 
902   if (!mz_zip_writer_finalize_archive(&zip))
903   {
904     mz_zip_writer_end(&zip);
905     remove(pZip_filename);
906     print_error("Failed creating zip archive \"%s\" (3)!\n", pZip_filename);
907     return false;
908   }
909 
910   mz_zip_writer_end(&zip);
911 
912   struct FILE_STAT_STRUCT stat_buf;
913   FILE_STAT(pZip_filename, &stat_buf);
914   uint64 actual_file_size = stat_buf.st_size;
915   if (zip.m_archive_size != actual_file_size)
916   {
917     print_error("Archive's actual size and zip archive object's size differ for file \"%s\"!\n", pZip_filename);
918     return false;
919   }
920 
921   printf("Created zip file \"%s\", file size: " QUAD_INT_FMT "\n", pZip_filename, zip.m_archive_size);
922   return true;
923 }
924 
zip_write_callback(void * pOpaque,mz_uint64 ofs,const void * pBuf,size_t n)925 static size_t zip_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
926 {
927   (void)pOpaque, (void)ofs, (void)pBuf, (void)n;
928   return n;
929 }
930 
zip_extract(const char * pZip_filename,const char * pDst_filename)931 static bool zip_extract(const char *pZip_filename, const char *pDst_filename)
932 {
933   mz_zip_archive zip;
934   memset(&zip, 0, sizeof(zip));
935   if (!mz_zip_reader_init_file(&zip, pZip_filename, 0))
936   {
937     print_error("Failed opening zip archive \"%s\"!\n", pZip_filename);
938     return false;
939   }
940 
941   int file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment", 0);
942   int alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment e", 0);
943   if ((file_index < 0) || (alt_file_index >= 0))
944   {
945     print_error("Archive \"%s\" is missing test.bin file!\n", pZip_filename);
946     mz_zip_reader_end(&zip);
947     return false;
948   }
949 
950   alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", NULL, 0);
951   if (alt_file_index != file_index)
952   {
953     print_error("mz_zip_reader_locate_file() failed!\n", pZip_filename);
954     mz_zip_reader_end(&zip);
955     return false;
956   }
957 
958   if (!mz_zip_reader_extract_to_file(&zip, file_index, pDst_filename, 0))
959   {
960     print_error("Failed extracting test.bin from archive \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
961     mz_zip_reader_end(&zip);
962     return false;
963   }
964 
965   for (uint i = 0; i < mz_zip_reader_get_num_files(&zip); i++)
966   {
967     mz_zip_archive_file_stat stat;
968     if (!mz_zip_reader_file_stat(&zip, i, &stat))
969     {
970       print_error("Failed testing archive -1 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
971       mz_zip_reader_end(&zip);
972       return false;
973     }
974     //printf("\"%s\" %I64u %I64u\n", stat.m_filename, stat.m_comp_size, stat.m_uncomp_size);
975     size_t size = 0;
976 
977     mz_bool status = mz_zip_reader_extract_to_callback(&zip, i, zip_write_callback, NULL, 0);
978     if (!status)
979     {
980       print_error("Failed testing archive -2 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
981       mz_zip_reader_end(&zip);
982       return false;
983     }
984 
985     if (stat.m_uncomp_size<100*1024*1024)
986     {
987         void *p = mz_zip_reader_extract_to_heap(&zip, i, &size, 0);
988         if (!p)
989         {
990             print_error("Failed testing archive -3 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
991             mz_zip_reader_end(&zip);
992             return false;
993         }
994         free(p);
995     }
996 
997     if (stat.m_uncomp_size<100*1024*1024)
998     {
999         /* Use iterative reader to read onto heap in one big chunk */
1000         mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_iter_new(&zip, i, 0);
1001         void *p = malloc(stat.m_uncomp_size);
1002         if ((!pIter) && (0 != stat.m_uncomp_size))
1003         {
1004             print_error("Failed testing archive -4 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1005             free(p);
1006             mz_zip_reader_end(&zip);
1007             return false;
1008         }
1009         if (pIter)
1010         {
1011             if (stat.m_uncomp_size != mz_zip_reader_extract_iter_read(pIter, p, stat.m_uncomp_size) )
1012             {
1013                 print_error("Failed testing archive -5 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1014                 free(p);
1015                 mz_zip_reader_extract_iter_free(pIter);
1016                 mz_zip_reader_end(&zip);
1017                 return false;
1018             }
1019             if (MZ_TRUE != mz_zip_reader_extract_iter_free(pIter))
1020             {
1021                 print_error("Failed testing archive -6 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1022                 free(p);
1023                 mz_zip_reader_end(&zip);
1024                 return false;
1025             }
1026         }
1027         free(p);
1028     }
1029 
1030     if (stat.m_uncomp_size<100*1024)
1031     {
1032         /* Use iterative reader to read file one byte at a time */
1033         mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_iter_new(&zip, i, 0);
1034         uint8_t byBuffer;
1035         if ((!pIter) && (0 != stat.m_uncomp_size))
1036         {
1037             print_error("Failed testing archive -7 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1038             mz_zip_reader_end(&zip);
1039             return false;
1040         }
1041         if (pIter)
1042         {
1043             for ( uint64_t uiIndex = 0; uiIndex < stat.m_uncomp_size; uiIndex++ )
1044             {
1045                 if (sizeof(byBuffer) != mz_zip_reader_extract_iter_read(pIter, &byBuffer, sizeof(byBuffer)))
1046                 {
1047                     print_error("Failed testing archive -8 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1048                     mz_zip_reader_extract_iter_free(pIter);
1049                     mz_zip_reader_end(&zip);
1050                     return false;
1051                 }
1052             }
1053             if (MZ_TRUE != mz_zip_reader_extract_iter_free(pIter))
1054             {
1055                 print_error("Failed testing archive -9 \"%s\" err: %s!\n", pZip_filename, mz_zip_get_error_string(mz_zip_get_last_error(&zip)));
1056                 mz_zip_reader_end(&zip);
1057                 return false;
1058             }
1059         }
1060     }
1061   }
1062   printf("Verified %u files\n",  mz_zip_reader_get_num_files(&zip));
1063 
1064   mz_zip_reader_end(&zip);
1065 
1066   printf("Extracted file \"%s\"\n", pDst_filename);
1067   return true;
1068 }
1069 
1070 typedef std::vector< std::string > string_array;
1071 
1072 #if defined(WIN32)
find_files(std::string pathname,const std::string & filename,string_array & files,bool recursive,int depth=0)1073 static bool find_files(std::string pathname, const std::string &filename, string_array &files, bool recursive, int depth = 0)
1074 {
1075   if (!pathname.empty())
1076   {
1077     char c = pathname[pathname.size() - 1];
1078     if ((c != ':') && (c != '\\') && (c != '/'))
1079       pathname += "\\";
1080   }
1081 
1082   WIN32_FIND_DATAA find_data;
1083 
1084   HANDLE findHandle = FindFirstFileA((pathname + filename).c_str(), &find_data);
1085   if (findHandle == INVALID_HANDLE_VALUE)
1086   {
1087     HRESULT hres = GetLastError();
1088     if ((!depth) && (hres != NO_ERROR) && (hres != ERROR_FILE_NOT_FOUND))
1089       return false;
1090   }
1091   else
1092   {
1093     do
1094     {
1095       const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1096       const bool is_system =  (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0;
1097       const bool is_hidden =  false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
1098 
1099       std::string filename(find_data.cFileName);
1100 
1101       if ((!is_directory) && (!is_system) && (!is_hidden))
1102         files.push_back(pathname + filename);
1103 
1104     } while (FindNextFileA(findHandle, &find_data));
1105 
1106     FindClose(findHandle);
1107   }
1108 
1109   if (recursive)
1110   {
1111     string_array paths;
1112 
1113     HANDLE findHandle = FindFirstFileA((pathname + "*").c_str(), &find_data);
1114     if (findHandle != INVALID_HANDLE_VALUE)
1115     {
1116       do
1117       {
1118         const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
1119         const bool is_system =  (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0;
1120         const bool is_hidden =  false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0;
1121 
1122         std::string filename(find_data.cFileName);
1123 
1124         if ((is_directory) && (!is_hidden) && (!is_system))
1125           paths.push_back(filename);
1126 
1127       } while (FindNextFileA(findHandle, &find_data));
1128 
1129       FindClose(findHandle);
1130 
1131       for (uint i = 0; i < paths.size(); i++)
1132       {
1133         const std::string &path = paths[i];
1134         if (path[0] == '.')
1135           continue;
1136 
1137         if (!find_files(pathname + path, filename, files, true, depth + 1))
1138           return false;
1139       }
1140     }
1141   }
1142 
1143   return true;
1144 }
1145 #else
1146 #include <dirent.h>
1147 #include <fnmatch.h>
find_files(std::string pathname,const std::string & pattern,string_array & files,bool recursive,int depth=0)1148 static bool find_files(std::string pathname, const std::string &pattern, string_array &files, bool recursive, int depth = 0)
1149 {
1150   if (!pathname.empty())
1151   {
1152     char c = pathname[pathname.size() - 1];
1153     if ((c != ':') && (c != '\\') && (c != '/'))
1154       pathname += "/";
1155   }
1156 
1157   DIR *dp = opendir(pathname.c_str());
1158 
1159   if (!dp)
1160     return depth ? true : false;
1161 
1162   string_array paths;
1163 
1164   for ( ; ; )
1165   {
1166     struct dirent *ep = readdir(dp);
1167     if (!ep)
1168       break;
1169 
1170     const bool is_directory = (ep->d_type & DT_DIR) != 0;
1171     const bool is_file =  (ep->d_type & DT_REG) != 0;
1172 
1173     if (ep->d_name[0] == '.')
1174       continue;
1175 
1176     std::string filename(ep->d_name);
1177 
1178     if (is_directory)
1179     {
1180       if (recursive)
1181         paths.push_back(filename);
1182     }
1183     else if (is_file)
1184     {
1185       if (0 == fnmatch(pattern.c_str(), filename.c_str(), 0))
1186          files.push_back(pathname + filename);
1187     }
1188   }
1189 
1190   closedir(dp);
1191   dp = NULL;
1192 
1193   if (recursive)
1194   {
1195     for (uint i = 0; i < paths.size(); i++)
1196     {
1197       const std::string &path = paths[i];
1198       if (!find_files(pathname + path, pattern, files, true, depth + 1))
1199         return false;
1200     }
1201   }
1202 
1203   return true;
1204 }
1205 #endif
1206 
test_recursive(const char * pPath,comp_options options)1207 static bool test_recursive(const char *pPath, comp_options options)
1208 {
1209   string_array files;
1210   if (!find_files(pPath, "*", files, true))
1211   {
1212     print_error("Failed finding files under path \"%s\"!\n", pPath);
1213     return false;
1214   }
1215 
1216   uint total_files_compressed = 0;
1217   uint64 total_source_size = 0;
1218   uint64 total_comp_size = 0;
1219 
1220 #ifdef WIN32
1221   MEMORYSTATUS initial_mem_status;
1222   GlobalMemoryStatus(&initial_mem_status);
1223 #endif
1224 
1225   timer_ticks start_tick_count = timer::get_ticks();
1226 
1227   const int first_file_index = 0;
1228 
1229   uint unique_id = static_cast<uint>(timer::get_init_ticks());
1230   char cmp_file[256], decomp_file[256];
1231 
1232   sprintf(cmp_file, "__comp_temp_%u__.tmp", unique_id);
1233   sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id);
1234 
1235   for (uint file_index = first_file_index; file_index < files.size(); file_index++)
1236   {
1237     const std::string &src_file = files[file_index];
1238 
1239     printf("***** [%u of %u] Compressing file \"%s\" to \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str(), cmp_file);
1240 
1241     if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL))
1242     {
1243       printf("Skipping temporary file \"%s\"\n", src_file.c_str());
1244       continue;
1245     }
1246 
1247     FILE *pFile = fopen(src_file.c_str(), "rb");
1248     if (!pFile)
1249     {
1250       printf("Skipping unreadable file \"%s\"\n", src_file.c_str());
1251       continue;
1252     }
1253     _fseeki64(pFile, 0, SEEK_END);
1254     int64 src_file_size = _ftelli64(pFile);
1255 
1256     if (src_file_size)
1257     {
1258        _fseeki64(pFile, 0, SEEK_SET);
1259        if (fgetc(pFile) == EOF)
1260        {
1261           printf("Skipping unreadable file \"%s\"\n", src_file.c_str());
1262           fclose(pFile);
1263           continue;
1264        }
1265     }
1266 
1267     fclose(pFile);
1268 
1269     if (!ensure_file_is_writable(cmp_file))
1270     {
1271       print_error("Unable to create file \"%s\"!\n", cmp_file);
1272       return false;
1273     }
1274 
1275     comp_options file_options(options);
1276     if (options.m_randomize_params)
1277     {
1278       file_options.m_level = rand() % 11;
1279       file_options.m_unbuffered_decompression = (rand() & 1) != 0;
1280       file_options.m_z_strat = rand() % (Z_FIXED + 1);
1281       file_options.m_write_zlib_header = (rand() & 1) != 0;
1282       file_options.m_random_z_flushing = (rand() & 1) != 0;
1283       file_options.print();
1284     }
1285 
1286     bool status;
1287     if (file_options.m_archive_test)
1288     {
1289       printf("Creating test archive with file \"%s\", size " QUAD_INT_FMT "\n", src_file.c_str(), src_file_size);
1290       status = zip_create(cmp_file, src_file.c_str());
1291     }
1292     else
1293       status = compress_file_zlib(src_file.c_str(), cmp_file, file_options);
1294     if (!status)
1295     {
1296       print_error("Failed compressing file \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file);
1297       return false;
1298     }
1299 
1300     if (file_options.m_verify_compressed_data)
1301     {
1302       printf("Decompressing file \"%s\" to \"%s\"\n", cmp_file, decomp_file);
1303 
1304       if (!ensure_file_is_writable(decomp_file))
1305       {
1306         print_error("Unable to create file \"%s\"!\n", decomp_file);
1307         return false;
1308       }
1309 
1310       if (file_options.m_archive_test)
1311         status = zip_extract(cmp_file, decomp_file);
1312       else
1313         status = decompress_file_zlib(cmp_file, decomp_file, file_options);
1314 
1315       if (!status)
1316       {
1317         print_error("Failed decompressing file \"%s\" to \"%s\"\n", src_file.c_str(), decomp_file);
1318         return false;
1319       }
1320 
1321       printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str());
1322 
1323       if (!compare_files(decomp_file, src_file.c_str()))
1324       {
1325         print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file);
1326         return false;
1327       }
1328       else
1329       {
1330         printf("Decompressed file compared OK to original file.\n");
1331       }
1332     }
1333 
1334     int64 cmp_file_size = 0;
1335     pFile = fopen(cmp_file, "rb");
1336     if (pFile)
1337     {
1338       _fseeki64(pFile, 0, SEEK_END);
1339       cmp_file_size = _ftelli64(pFile);
1340       fclose(pFile);
1341     }
1342 
1343     total_files_compressed++;
1344     total_source_size += src_file_size;
1345     total_comp_size += cmp_file_size;
1346 
1347 #ifdef WIN32
1348     MEMORYSTATUS mem_status;
1349     GlobalMemoryStatus(&mem_status);
1350 
1351     const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual;
1352 
1353     printf("Memory allocated relative to first file: %I64i\n", bytes_allocated);
1354 #endif
1355 
1356     printf("\n");
1357   }
1358 
1359   timer_ticks end_tick_count = timer::get_ticks();
1360 
1361   double total_elapsed_time = timer::ticks_to_secs(end_tick_count - start_tick_count);
1362 
1363   printf("Test successful: %f secs\n", total_elapsed_time);
1364   printf("Total files processed: %u\n", total_files_compressed);
1365   printf("Total source size: " QUAD_INT_FMT "\n", total_source_size);
1366   printf("Total compressed size: " QUAD_INT_FMT "\n", total_comp_size);
1367   printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio);
1368 
1369   remove(cmp_file);
1370   remove(decomp_file);
1371 
1372   return true;
1373 }
1374 
dummy_zip_file_write_callback(void * pOpaque,mz_uint64 ofs,const void * pBuf,size_t n)1375 static size_t dummy_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
1376 {
1377   (void)ofs; (void)pBuf;
1378   uint32 *pCRC = (uint32*)pOpaque;
1379   *pCRC = mz_crc32(*pCRC, (const uint8*)pBuf, n);
1380   return n;
1381 }
1382 
test_archives(const char * pPath,comp_options options)1383 static bool test_archives(const char *pPath, comp_options options)
1384 {
1385   (void)options;
1386 
1387   string_array files;
1388   if (!find_files(pPath, "*.zip", files, true))
1389   {
1390     print_error("Failed finding files under path \"%s\"!\n", pPath);
1391     return false;
1392   }
1393 
1394   uint total_archives = 0;
1395   uint64 total_bytes_processed = 0;
1396   uint64 total_files_processed = 0;
1397   uint total_errors = 0;
1398 
1399 #ifdef WIN32
1400   MEMORYSTATUS initial_mem_status;
1401   GlobalMemoryStatus(&initial_mem_status);
1402 #endif
1403 
1404   const int first_file_index = 0;
1405   uint unique_id = static_cast<uint>(timer::get_init_ticks());
1406   char cmp_file[256], decomp_file[256];
1407 
1408   sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id);
1409 
1410   string_array failed_archives;
1411 
1412   for (uint file_index = first_file_index; file_index < files.size(); file_index++)
1413   {
1414     const std::string &src_file = files[file_index];
1415 
1416     printf("***** [%u of %u] Testing archive file \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str());
1417 
1418     if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL))
1419     {
1420       printf("Skipping temporary file \"%s\"\n", src_file.c_str());
1421       continue;
1422     }
1423 
1424     FILE *pFile = fopen(src_file.c_str(), "rb");
1425     if (!pFile)
1426     {
1427       printf("Skipping unreadable file \"%s\"\n", src_file.c_str());
1428       continue;
1429     }
1430     _fseeki64(pFile, 0, SEEK_END);
1431     int64 src_file_size = _ftelli64(pFile);
1432     fclose(pFile);
1433 
1434     (void)src_file_size;
1435 
1436     sprintf(cmp_file, "__comp_temp_%u__.zip", file_index);
1437 
1438     mz_zip_archive src_archive;
1439     memset(&src_archive, 0, sizeof(src_archive));
1440 
1441     if (!mz_zip_reader_init_file(&src_archive, src_file.c_str(), 0))
1442     {
1443       failed_archives.push_back(src_file);
1444 
1445       print_error("Failed opening archive \"%s\"!\n", src_file.c_str());
1446       total_errors++;
1447       continue;
1448     }
1449 
1450     mz_zip_archive dst_archive;
1451     memset(&dst_archive, 0, sizeof(dst_archive));
1452     if (options.m_write_archives)
1453     {
1454       if (!ensure_file_is_writable(cmp_file))
1455       {
1456         print_error("Unable to create file \"%s\"!\n", cmp_file);
1457         return false;
1458       }
1459 
1460       if (!mz_zip_writer_init_file(&dst_archive, cmp_file, 0))
1461       {
1462         print_error("Failed creating archive \"%s\"!\n", cmp_file);
1463         total_errors++;
1464         continue;
1465       }
1466     }
1467 
1468     int i;
1469     //for (i = 0; i < mz_zip_reader_get_num_files(&src_archive); i++)
1470     for (i = mz_zip_reader_get_num_files(&src_archive) - 1; i >= 0; --i)
1471     {
1472       if (mz_zip_reader_is_file_encrypted(&src_archive, i))
1473         continue;
1474 
1475       mz_zip_archive_file_stat file_stat;
1476       bool status = mz_zip_reader_file_stat(&src_archive, i, &file_stat) != 0;
1477 
1478       int locate_file_index = mz_zip_reader_locate_file(&src_archive, file_stat.m_filename, NULL, 0);
1479       if (locate_file_index != i)
1480       {
1481         mz_zip_archive_file_stat locate_file_stat;
1482         mz_zip_reader_file_stat(&src_archive, locate_file_index, &locate_file_stat);
1483         if (_stricmp(locate_file_stat.m_filename, file_stat.m_filename) != 0)
1484         {
1485           print_error("mz_zip_reader_locate_file() failed!\n");
1486           return false;
1487         }
1488         else
1489         {
1490           printf("Warning: Duplicate filenames in archive!\n");
1491         }
1492       }
1493 
1494       if ((file_stat.m_method) && (file_stat.m_method != MZ_DEFLATED))
1495         continue;
1496 
1497       if (status)
1498       {
1499         char name[260];
1500         mz_zip_reader_get_filename(&src_archive, i, name, sizeof(name));
1501 
1502         size_t extracted_size = 0;
1503         void *p = mz_zip_reader_extract_file_to_heap(&src_archive, name, &extracted_size, 0);
1504         if (!p)
1505           status = false;
1506 
1507         uint32 extracted_crc32 = MZ_CRC32_INIT;
1508         if (!mz_zip_reader_extract_file_to_callback(&src_archive, name, dummy_zip_file_write_callback, &extracted_crc32, 0))
1509           status = false;
1510 
1511         if (mz_crc32(MZ_CRC32_INIT, (const uint8*)p, extracted_size) != extracted_crc32)
1512           status = false;
1513 
1514         mz_zip_reader_extract_iter_state *pIter = mz_zip_reader_extract_file_iter_new(&src_archive, name, 0);
1515         void *q = malloc(extracted_size);
1516         mz_zip_reader_extract_iter_read(pIter, q, extracted_size);
1517         mz_zip_reader_extract_iter_free(pIter);
1518 
1519         if (mz_crc32(MZ_CRC32_INIT, (const uint8*)q, extracted_size) != extracted_crc32)
1520             status = false;
1521 
1522         free(q);
1523         free(p);
1524 
1525         if (options.m_write_archives)
1526         {
1527           if ((status) && (!mz_zip_writer_add_from_zip_reader(&dst_archive, &src_archive, i)))
1528           {
1529             print_error("Failed adding new file to archive \"%s\"!\n", cmp_file);
1530             status = false;
1531           }
1532         }
1533 
1534         total_bytes_processed += file_stat.m_uncomp_size;
1535         total_files_processed++;
1536       }
1537 
1538       if (!status)
1539         break;
1540     }
1541 
1542     mz_zip_reader_end(&src_archive);
1543 
1544     //if (i < mz_zip_reader_get_num_files(&src_archive))
1545     if (i >= 0)
1546     {
1547       failed_archives.push_back(src_file);
1548 
1549       print_error("Failed processing archive \"%s\"!\n", src_file.c_str());
1550       total_errors++;
1551     }
1552 
1553     if (options.m_write_archives)
1554     {
1555       if (!mz_zip_writer_finalize_archive(&dst_archive) || !mz_zip_writer_end(&dst_archive))
1556       {
1557         failed_archives.push_back(src_file);
1558 
1559         print_error("Failed finalizing archive \"%s\"!\n", cmp_file);
1560         total_errors++;
1561       }
1562     }
1563 
1564     total_archives++;
1565 
1566 #ifdef WIN32
1567     MEMORYSTATUS mem_status;
1568     GlobalMemoryStatus(&mem_status);
1569 
1570     const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual;
1571 
1572     printf("Memory allocated relative to first file: %I64i\n", bytes_allocated);
1573 #endif
1574 
1575     printf("\n");
1576   }
1577 
1578   printf("Total archives processed: %u\n", total_archives);
1579   printf("Total errors: %u\n", total_errors);
1580   printf("Total bytes processed: " QUAD_INT_FMT "\n", total_bytes_processed);
1581   printf("Total archive files processed: " QUAD_INT_FMT "\n", total_files_processed);
1582 
1583   printf("List of failed archives:\n");
1584   for (uint i = 0; i < failed_archives.size(); ++i)
1585     printf("%s\n", failed_archives[i].c_str());
1586 
1587   remove(cmp_file);
1588   remove(decomp_file);
1589 
1590   return true;
1591 }
1592 
main_internal(string_array cmd_line)1593 int main_internal(string_array cmd_line)
1594 {
1595   comp_options options;
1596 
1597   if (!cmd_line.size())
1598   {
1599     print_usage();
1600     if (simple_test1(options) || simple_test2(options))
1601       return EXIT_FAILURE;
1602     return EXIT_SUCCESS;
1603   }
1604 
1605   enum op_mode_t
1606   {
1607     OP_MODE_INVALID = -1,
1608     OP_MODE_COMPRESS = 0,
1609     OP_MODE_DECOMPRESS = 1,
1610     OP_MODE_ALL = 2,
1611     OP_MODE_ARCHIVES = 3
1612   };
1613 
1614   op_mode_t op_mode = OP_MODE_INVALID;
1615 
1616   for (int i = 0; i < (int)cmd_line.size(); i++)
1617   {
1618     const std::string &str = cmd_line[i];
1619     if (str[0] == '-')
1620     {
1621       if (str.size() < 2)
1622       {
1623         print_error("Invalid option: %s\n", str.c_str());
1624         return EXIT_FAILURE;
1625       }
1626       switch (tolower(str[1]))
1627       {
1628         case 'u':
1629         {
1630           options.m_unbuffered_decompression = true;
1631           break;
1632         }
1633         case 'm':
1634         {
1635           int comp_level = atoi(str.c_str() + 2);
1636           if ((comp_level < 0) || (comp_level > (int)10))
1637           {
1638             print_error("Invalid compression level: %s\n", str.c_str());
1639             return EXIT_FAILURE;
1640           }
1641 
1642           options.m_level = comp_level;
1643           break;
1644         }
1645         case 'v':
1646         {
1647           options.m_verify_compressed_data = true;
1648           break;
1649         }
1650         case 'r':
1651         {
1652           options.m_randomize_params = true;
1653           break;
1654         }
1655         case 'b':
1656         {
1657           options.m_randomize_buffer_sizes = true;
1658           break;
1659         }
1660         case 'h':
1661         {
1662           options.m_random_z_flushing = true;
1663           break;
1664         }
1665         case 'x':
1666         {
1667           int seed = atoi(str.c_str() + 2);
1668           srand(seed);
1669           printf("Using random seed: %i\n", seed);
1670           break;
1671         }
1672         case 't':
1673         {
1674           options.m_z_strat = my_min(Z_FIXED, my_max(0, atoi(str.c_str() + 2)));
1675           break;
1676         }
1677         case 'z':
1678         {
1679           options.m_write_zlib_header = false;
1680           break;
1681         }
1682         case 'a':
1683         {
1684           options.m_archive_test = true;
1685           break;
1686         }
1687         case 'w':
1688         {
1689           options.m_write_archives = true;
1690           break;
1691         }
1692         default:
1693         {
1694           print_error("Invalid option: %s\n", str.c_str());
1695           return EXIT_FAILURE;
1696         }
1697       }
1698 
1699       cmd_line.erase(cmd_line.begin() + i);
1700       i--;
1701 
1702       continue;
1703     }
1704 
1705     if (str.size() != 1)
1706     {
1707       print_error("Invalid mode: %s\n", str.c_str());
1708       return EXIT_FAILURE;
1709     }
1710     switch (tolower(str[0]))
1711     {
1712       case 'c':
1713       {
1714         op_mode = OP_MODE_COMPRESS;
1715         break;
1716       }
1717       case 'd':
1718       {
1719         op_mode = OP_MODE_DECOMPRESS;
1720         break;
1721       }
1722       case 'a':
1723       {
1724         op_mode = OP_MODE_ALL;
1725         break;
1726       }
1727       case 'r':
1728       {
1729         op_mode = OP_MODE_ARCHIVES;
1730         break;
1731       }
1732       default:
1733       {
1734         print_error("Invalid mode: %s\n", str.c_str());
1735         return EXIT_FAILURE;
1736       }
1737     }
1738     cmd_line.erase(cmd_line.begin() + i);
1739     break;
1740   }
1741 
1742   if (op_mode == OP_MODE_INVALID)
1743   {
1744     print_error("No mode specified!\n");
1745     print_usage();
1746     return EXIT_FAILURE;
1747   }
1748 
1749   printf("Using options:\n");
1750   options.print();
1751   printf("\n");
1752 
1753   int exit_status = EXIT_FAILURE;
1754 
1755   switch (op_mode)
1756   {
1757     case OP_MODE_COMPRESS:
1758     {
1759       if (cmd_line.size() < 2)
1760       {
1761         print_error("Must specify input and output filenames!\n");
1762         return EXIT_FAILURE;
1763       }
1764       else if (cmd_line.size() > 2)
1765       {
1766         print_error("Too many filenames!\n");
1767         return EXIT_FAILURE;
1768       }
1769 
1770       const std::string &src_file = cmd_line[0];
1771       const std::string &cmp_file = cmd_line[1];
1772 
1773       bool comp_result = compress_file_zlib(src_file.c_str(), cmp_file.c_str(), options);
1774       if (comp_result)
1775         exit_status = EXIT_SUCCESS;
1776 
1777       if ((comp_result) && (options.m_verify_compressed_data))
1778       {
1779         char decomp_file[256];
1780 
1781         sprintf(decomp_file, "__decomp_temp_%u__.tmp", (uint)timer::get_ms());
1782 
1783         if (!decompress_file_zlib(cmp_file.c_str(), decomp_file, options))
1784         {
1785           print_error("Failed decompressing file \"%s\" to \"%s\"\n", cmp_file.c_str(), decomp_file);
1786           return EXIT_FAILURE;
1787         }
1788 
1789         printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str());
1790 
1791         if (!compare_files(decomp_file, src_file.c_str()))
1792         {
1793           print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file.c_str());
1794           return EXIT_FAILURE;
1795         }
1796         else
1797         {
1798           printf("Decompressed file compared OK to original file.\n");
1799         }
1800 
1801         remove(decomp_file);
1802       }
1803 
1804       break;
1805     }
1806     case OP_MODE_DECOMPRESS:
1807     {
1808       if (cmd_line.size() < 2)
1809       {
1810         print_error("Must specify input and output filenames!\n");
1811         return EXIT_FAILURE;
1812       }
1813       else if (cmd_line.size() > 2)
1814       {
1815         print_error("Too many filenames!\n");
1816         return EXIT_FAILURE;
1817       }
1818       if (decompress_file_zlib(cmd_line[0].c_str(), cmd_line[1].c_str(), options))
1819         exit_status = EXIT_SUCCESS;
1820       break;
1821     }
1822     case OP_MODE_ALL:
1823     {
1824       if (!cmd_line.size())
1825       {
1826         print_error("No directory specified!\n");
1827         return EXIT_FAILURE;
1828       }
1829       else if (cmd_line.size() != 1)
1830       {
1831         print_error("Too many filenames!\n");
1832         return EXIT_FAILURE;
1833       }
1834       if (test_recursive(cmd_line[0].c_str(), options))
1835         exit_status = EXIT_SUCCESS;
1836       break;
1837     }
1838     case OP_MODE_ARCHIVES:
1839     {
1840       if (!cmd_line.size())
1841       {
1842         print_error("No directory specified!\n");
1843         return EXIT_FAILURE;
1844       }
1845       else if (cmd_line.size() != 1)
1846       {
1847         print_error("Too many filenames!\n");
1848         return EXIT_FAILURE;
1849       }
1850       if (test_archives(cmd_line[0].c_str(), options))
1851         exit_status = EXIT_SUCCESS;
1852       break;
1853     }
1854     default:
1855     {
1856       print_error("No mode specified!\n");
1857       print_usage();
1858       return EXIT_FAILURE;
1859     }
1860   }
1861 
1862   return exit_status;
1863 }
1864 
main(int argc,char * argv[])1865 int main(int argc, char *argv[])
1866 {
1867 #if defined(_WIN64) || defined(__LP64__) || defined(_LP64)
1868   printf("miniz.c x64 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__);
1869 #else
1870   printf("miniz.c x86 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__);
1871 #endif
1872   timer::get_ticks();
1873 
1874   string_array cmd_line;
1875   for (int i = 1; i < argc; i++)
1876     cmd_line.push_back(std::string(argv[i]));
1877 
1878   int exit_status = main_internal(cmd_line);
1879 
1880   return exit_status;
1881 }
1882