diff --git a/example1.c b/example1.c index fc5001c..95d1cdb 100644 --- a/example1.c +++ b/example1.c @@ -24,7 +24,7 @@ int main(int argc, char *argv[]) uLong uncomp_len = src_len; uint8 *pCmp, *pUncomp; uint total_succeeded = 0; - argc, argv; + (void)argc, (void)argv; printf("miniz.c version: %s\n", MZ_VERSION); @@ -49,7 +49,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Compressed from %u to %u bytes\n", src_len, cmp_len); + printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, (mz_uint32)cmp_len); if (step) { @@ -61,11 +61,11 @@ int main(int argc, char *argv[]) pCmp[i] ^= (rand() & 0xFF); } } - + // Decompress. cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); total_succeeded += (cmp_status == Z_OK); - + if (step) { printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, total_succeeded); @@ -80,7 +80,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Decompressed from %u to %u bytes\n", cmp_len, uncomp_len); + printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, (mz_uint32)uncomp_len); // Ensure uncompress() returned the expected data. if ((uncomp_len != src_len) || (memcmp(pUncomp, s_pStr, (size_t)src_len))) @@ -96,9 +96,9 @@ int main(int argc, char *argv[]) free(pUncomp); step++; - + // Keep on fuzzy testing if there's a non-empty command line. - } while (argc >= 2); + } while (argc >= 2); printf("Success.\n"); return EXIT_SUCCESS; diff --git a/example1.cbp b/example1.cbp new file mode 100644 index 0000000..c10be25 --- /dev/null +++ b/example1.cbp @@ -0,0 +1,88 @@ + + + + + + diff --git a/example2.c b/example2.c index 125a47c..b8d7ac1 100644 --- a/example2.c +++ b/example2.c @@ -7,28 +7,28 @@ typedef unsigned short uint16; typedef unsigned int uint; // The string to compress. -static const char *s_pStr = - "MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ - "if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ - "actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ - "guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ - "the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ - "No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ - "event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; +static const char *s_pStr = +"MISSION CONTROL I wouldn't worry too much about the computer. First of all, there is still a chance that he is right, despite your tests, and" \ +"if it should happen again, we suggest eliminating this possibility by allowing the unit to remain in place and seeing whether or not it" \ +"actually fails. If the computer should turn out to be wrong, the situation is still not alarming. The type of obsessional error he may be" \ +"guilty of is not unknown among the latest generation of HAL 9000 computers. It has almost always revolved around a single detail, such as" \ +"the one you have described, and it has never interfered with the integrity or reliability of the computer's performance in other areas." \ +"No one is certain of the cause of this kind of malfunctioning. It may be over-programming, but it could also be any number of reasons. In any" \ +"event, it is somewhat analogous to human neurotic behavior. Does this answer your query? Zero-five-three-Zero, MC, transmission concluded."; static const char *s_pComment = "This is a comment"; int main(int argc, char *argv[]) { - uint i; - mz_bool status; - size_t uncomp_size; - mz_zip_archive zip_archive; - void *p; + uint i; + mz_bool status; + size_t uncomp_size; + mz_zip_archive zip_archive; + void *p; - printf("miniz.c version: %s\n", MZ_VERSION); + printf("miniz.c version: %s\n", MZ_VERSION); - argc, argv; + (void)argc, (void)argv; // Append a bunch of text files to test.zip for (i = 0; i < 50; i++) @@ -43,29 +43,29 @@ int main(int argc, char *argv[]) } } - // Now try to open the archive. - memset(&zip_archive, 0, sizeof(zip_archive)); - status = mz_zip_reader_init_file(&zip_archive, "test.zip", 0); - if (!status) - { - printf("mz_zip_reader_init_file() failed!\n"); - return EXIT_FAILURE; - } - - // Get and print information about each file in the archive. - for (i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) - { - mz_zip_archive_file_stat file_stat; - if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) - { - printf("mz_zip_reader_file_stat() failed!\n"); - mz_zip_reader_end(&zip_archive); + // Now try to open the archive. + memset(&zip_archive, 0, sizeof(zip_archive)); + status = mz_zip_reader_init_file(&zip_archive, "test.zip", 0); + if (!status) + { + printf("mz_zip_reader_init_file() failed!\n"); return EXIT_FAILURE; - } + } + + // Get and print information about each file in the archive. + for (i = 0; i < mz_zip_reader_get_num_files(&zip_archive); i++) + { + mz_zip_archive_file_stat file_stat; + if (!mz_zip_reader_file_stat(&zip_archive, i, &file_stat)) + { + printf("mz_zip_reader_file_stat() failed!\n"); + mz_zip_reader_end(&zip_archive); + return EXIT_FAILURE; + } + + printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size); + } - printf("Filename: \"%s\", Comment: \"%s\", Uncompressed size: %u, Compressed size: %u\n", file_stat.m_filename, file_stat.m_comment, (uint)file_stat.m_uncomp_size, (uint)file_stat.m_comp_size); - } - // Try to extract 0.txt to the heap. p = mz_zip_reader_extract_file_to_heap(&zip_archive, "0.txt", &uncomp_size, 0); if (!p) @@ -86,10 +86,10 @@ int main(int argc, char *argv[]) printf("Successfully extracted file \"0.txt\", size %u\n", (uint)uncomp_size); - // We're done. - free(p); - mz_zip_reader_end(&zip_archive); + // We're done. + free(p); + mz_zip_reader_end(&zip_archive); - printf("Success.\n"); - return EXIT_SUCCESS; + printf("Success.\n"); + return EXIT_SUCCESS; } diff --git a/example2.cbp b/example2.cbp new file mode 100644 index 0000000..4724426 --- /dev/null +++ b/example2.cbp @@ -0,0 +1,88 @@ + + + + + + diff --git a/example3.c b/example3.c index afd3e04..88b7b5f 100644 --- a/example3.c +++ b/example3.c @@ -2,6 +2,7 @@ // Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. // For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. #include "miniz.c" +#include typedef unsigned char uint8; typedef unsigned short uint16; @@ -21,10 +22,11 @@ int main(int argc, char *argv[]) uint infile_size; int level = Z_BEST_COMPRESSION; z_stream stream; - int n = 1; + int p = 1; const char *pSrc_filename; const char *pDst_filename; - + long file_loc; + printf("miniz.c version: %s\n", MZ_VERSION); if (argc < 4) @@ -38,9 +40,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - while ((n < argc) && (argv[n][0] == '-')) + while ((p < argc) && (argv[p][0] == '-')) { - switch (argv[n][1]) + switch (argv[p][1]) { case 'l': { @@ -54,36 +56,36 @@ int main(int argc, char *argv[]) } default: { - printf("Invalid option: %s\n", argv[n]); + printf("Invalid option: %s\n", argv[p]); return EXIT_FAILURE; } } - n++; + p++; } - if ((argc - n) < 3) + if ((argc - p) < 3) { printf("Must specify mode, input filename, and output filename after options!\n"); return EXIT_FAILURE; } - else if ((argc - n) > 3) + else if ((argc - p) > 3) { printf("Too many filenames!\n"); return EXIT_FAILURE; } - - pMode = argv[n++]; + + pMode = argv[p++]; if (!strchr("cCdD", pMode[0])) { printf("Invalid mode!\n"); return EXIT_FAILURE; } - pSrc_filename = argv[n++]; - pDst_filename = argv[n++]; + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); - + // Open input file. pInfile = fopen(pSrc_filename, "rb"); if (!pInfile) @@ -91,12 +93,21 @@ int main(int argc, char *argv[]) printf("Failed opening input file!\n"); return EXIT_FAILURE; } - + // Determine input file's size. fseek(pInfile, 0, SEEK_END); - infile_size = ftell(pInfile); + file_loc = ftell(pInfile); fseek(pInfile, 0, SEEK_SET); + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + // Open output file. pOutfile = fopen(pDst_filename, "wb"); if (!pOutfile) @@ -106,7 +117,7 @@ int main(int argc, char *argv[]) } printf("Input file size: %u\n", infile_size); - + // Init the z_stream memset(&stream, 0, sizeof(stream)); stream.next_in = s_inbuf; @@ -132,16 +143,16 @@ int main(int argc, char *argv[]) { // Input buffer is empty, so read more bytes from input file. uint n = my_min(BUF_SIZE, infile_remaining); - + if (fread(s_inbuf, 1, n, pInfile) != n) { printf("Failed reading from input file!\n"); return EXIT_FAILURE; } - + stream.next_in = s_inbuf; stream.avail_in = n; - + infile_remaining -= n; //printf("Input bytes remaining: %u\n", infile_remaining); } @@ -235,7 +246,7 @@ int main(int argc, char *argv[]) { printf("inflateEnd() failed!\n"); return EXIT_FAILURE; - } + } } else { @@ -250,8 +261,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Total input bytes: %u\n", stream.total_in); - printf("Total output bytes: %u\n", stream.total_out); + printf("Total input bytes: %u\n", (mz_uint32)stream.total_in); + printf("Total output bytes: %u\n", (mz_uint32)stream.total_out); printf("Success.\n"); return EXIT_SUCCESS; } diff --git a/example3.cbp b/example3.cbp new file mode 100644 index 0000000..625d60a --- /dev/null +++ b/example3.cbp @@ -0,0 +1,88 @@ + + + + + + diff --git a/example4.c b/example4.c index d035e03..6df19ff 100644 --- a/example4.c +++ b/example4.c @@ -2,6 +2,7 @@ // Public domain, May 15 2011, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. #include "tinfl.c" #include +#include typedef unsigned char uint8; typedef unsigned short uint16; @@ -22,7 +23,8 @@ int main(int argc, char *argv[]) uint infile_size, outfile_size; size_t in_buf_size; uint8 *pCmp_data; - + long file_loc; + if (argc != 3) { printf("Usage: example4 infile outfile\n"); @@ -38,12 +40,21 @@ int main(int argc, char *argv[]) printf("Failed opening input file!\n"); return EXIT_FAILURE; } - + // Determine input file's size. fseek(pInfile, 0, SEEK_END); - infile_size = ftell(pInfile); + file_loc = ftell(pInfile); fseek(pInfile, 0, SEEK_SET); + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + pCmp_data = (uint8 *)malloc(infile_size); if (!pCmp_data) { @@ -65,7 +76,7 @@ int main(int argc, char *argv[]) } printf("Input file size: %u\n", infile_size); - + in_buf_size = infile_size; status = tinfl_decompress_mem_to_callback(pCmp_data, &in_buf_size, tinfl_put_buf_func, pOutfile, TINFL_FLAG_PARSE_ZLIB_HEADER); if (!status) diff --git a/example4.cbp b/example4.cbp new file mode 100644 index 0000000..8e54036 --- /dev/null +++ b/example4.cbp @@ -0,0 +1,88 @@ + + + + + + diff --git a/example5.c b/example5.c index 4641980..288e1d6 100644 --- a/example5.c +++ b/example5.c @@ -2,8 +2,19 @@ // The low-level API's are the fastest, make no use of dynamic memory allocation, and are the most flexible functions exposed by miniz.c. // Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com. See "unlicense" statement at the end of tinfl.c. // For simplicity, this example is limited to files smaller than 4GB, but this is not a limitation of miniz.c. + +// Purposely disable a whole bunch of stuff this low-level example doesn't use. +#define MINIZ_NO_STDIO +#define MINIZ_NO_ARCHIVE_APIS +#define MINIZ_NO_TIME +#define MINIZ_NO_ZLIB_APIS +#define MINIZ_NO_MALLOC #include "miniz.c" +// Now include stdio.h because this test uses fopen(), etc. (but we still don't want miniz.c's stdio stuff, for testing). +#include +#include + typedef unsigned char uint8; typedef unsigned short uint16; typedef unsigned int uint; @@ -26,13 +37,17 @@ static uint8 s_inbuf[IN_BUF_SIZE]; #define OUT_BUF_SIZE (1024*512) static uint8 s_outbuf[OUT_BUF_SIZE]; +// tdefl_compressor contains all the state needed by the low-level compressor so it's a pretty big struct (~300k). +// This example makes it a global vs. putting it on the stack, of course in real-world usage you'll probably malloc() or new it. +tdefl_compressor g_deflator; + int main(int argc, char *argv[]) { const char *pMode; FILE *pInfile, *pOutfile; uint infile_size; - int level = Z_BEST_COMPRESSION; - int n = 1; + int level = 9; + int p = 1; const char *pSrc_filename; const char *pDst_filename; const void *next_in = s_inbuf; @@ -40,10 +55,11 @@ int main(int argc, char *argv[]) void *next_out = s_outbuf; size_t avail_out = OUT_BUF_SIZE; size_t total_in = 0, total_out = 0; + long file_loc; assert(COMP_OUT_BUF_SIZE <= OUT_BUF_SIZE); - printf("miniz.c version: %s\n", MZ_VERSION); + printf("miniz.c example5 (demonstrates tinfl/tdefl)\n"); if (argc < 4) { @@ -57,9 +73,9 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - while ((n < argc) && (argv[n][0] == '-')) + while ((p < argc) && (argv[p][0] == '-')) { - switch (argv[n][1]) + switch (argv[p][1]) { case 'l': { @@ -73,36 +89,36 @@ int main(int argc, char *argv[]) } default: { - printf("Invalid option: %s\n", argv[n]); + printf("Invalid option: %s\n", argv[p]); return EXIT_FAILURE; } } - n++; + p++; } - if ((argc - n) < 3) + if ((argc - p) < 3) { printf("Must specify mode, input filename, and output filename after options!\n"); return EXIT_FAILURE; } - else if ((argc - n) > 3) + else if ((argc - p) > 3) { printf("Too many filenames!\n"); return EXIT_FAILURE; } - pMode = argv[n++]; + pMode = argv[p++]; if (!strchr("cCdD", pMode[0])) { printf("Invalid mode!\n"); return EXIT_FAILURE; } - pSrc_filename = argv[n++]; - pDst_filename = argv[n++]; + pSrc_filename = argv[p++]; + pDst_filename = argv[p++]; printf("Mode: %c, Level: %u\nInput File: \"%s\"\nOutput File: \"%s\"\n", pMode[0], level, pSrc_filename, pDst_filename); - + // Open input file. pInfile = fopen(pSrc_filename, "rb"); if (!pInfile) @@ -113,9 +129,18 @@ int main(int argc, char *argv[]) // Determine input file's size. fseek(pInfile, 0, SEEK_END); - infile_size = ftell(pInfile); + file_loc = ftell(pInfile); fseek(pInfile, 0, SEEK_SET); + if ((file_loc < 0) || (file_loc > INT_MAX)) + { + // This is not a limitation of miniz or tinfl, but this example. + printf("File is too large to be processed by this example.\n"); + return EXIT_FAILURE; + } + + infile_size = (uint)file_loc; + // Open output file. pOutfile = fopen(pDst_filename, "wb"); if (!pOutfile) @@ -125,15 +150,22 @@ int main(int argc, char *argv[]) } printf("Input file size: %u\n", infile_size); - + if ((pMode[0] == 'c') || (pMode[0] == 'C')) { + // The number of dictionary probes to use at each compression level (0-10). 0=implies fastest/minimal possible probing. + static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; + + tdefl_status status; uint infile_remaining = infile_size; - uint comp_flags = tdefl_create_comp_flags_from_zip_params(level, MZ_DEFAULT_WINDOW_BITS, MZ_DEFAULT_STRATEGY) | TDEFL_WRITE_ZLIB_HEADER; + + // create tdefl() compatible flags (we have to compose the low-level flags ourselves, or use tdefl_create_comp_flags_from_zip_params() but that means MINIZ_NO_ZLIB_APIS can't be defined). + mz_uint comp_flags = TDEFL_WRITE_ZLIB_HEADER | s_tdefl_num_probes[MZ_MIN(10, level)] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; // Initialize the low-level compressor. - tdefl_compressor deflator; - tdefl_status status = tdefl_init(&deflator, NULL, NULL, comp_flags); + status = tdefl_init(&g_deflator, NULL, NULL, comp_flags); if (status != TDEFL_STATUS_OKAY) { printf("tdefl_init() failed!\n"); @@ -141,7 +173,7 @@ int main(int argc, char *argv[]) } avail_out = COMP_OUT_BUF_SIZE; - + // Compression. for ( ; ; ) { @@ -160,7 +192,7 @@ int main(int argc, char *argv[]) next_in = s_inbuf; avail_in = n; - + infile_remaining -= n; //printf("Input bytes remaining: %u\n", infile_remaining); } @@ -168,8 +200,8 @@ int main(int argc, char *argv[]) in_bytes = avail_in; out_bytes = avail_out; // Compress as much of the input as possible (or all of it) to the output buffer. - status = tdefl_compress(&deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - + status = tdefl_compress(&g_deflator, next_in, &in_bytes, next_out, &out_bytes, infile_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); + next_in = (const char *)next_in + in_bytes; avail_in -= in_bytes; total_in += in_bytes; @@ -211,7 +243,7 @@ int main(int argc, char *argv[]) tinfl_decompressor inflator; tinfl_init(&inflator); - + for ( ; ; ) { size_t in_bytes, out_bytes; @@ -236,7 +268,7 @@ int main(int argc, char *argv[]) in_bytes = avail_in; out_bytes = avail_out; status = tinfl_decompress(&inflator, (const mz_uint8 *)next_in, &in_bytes, s_outbuf, (mz_uint8 *)next_out, &out_bytes, (infile_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0) | TINFL_FLAG_PARSE_ZLIB_HEADER); - + avail_in -= in_bytes; next_in = (const mz_uint8 *)next_in + in_bytes; total_in += in_bytes; @@ -244,7 +276,7 @@ int main(int argc, char *argv[]) avail_out -= out_bytes; next_out = (mz_uint8 *)next_out + out_bytes; total_out += out_bytes; - + if ((status <= TINFL_STATUS_DONE) || (!avail_out)) { // Output buffer is full, or decompression is done, so write buffer to output file. @@ -288,8 +320,8 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } - printf("Total input bytes: %u\n", total_in); - printf("Total output bytes: %u\n", total_out); + printf("Total input bytes: %u\n", (mz_uint32)total_in); + printf("Total output bytes: %u\n", (mz_uint32)total_out); printf("Success.\n"); return EXIT_SUCCESS; } diff --git a/example5.cbp b/example5.cbp new file mode 100644 index 0000000..d4f13a2 --- /dev/null +++ b/example5.cbp @@ -0,0 +1,88 @@ + + + + + + diff --git a/examples.sln b/examples.sln index 07d464e..20b48e9 100644 --- a/examples.sln +++ b/examples.sln @@ -11,6 +11,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example4", "example4.vcproj EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example5", "example5.vcproj", "{AE293522-92D8-4B60-95C7-B4AEE10A303F}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniz_tester", "miniz_tester.vcproj", "{AE293522-92D9-1B60-95C7-B4AEE10A303F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -59,6 +61,14 @@ Global {AE293522-92D8-4B60-95C7-B4AEE10A303F}.Release|Win32.Build.0 = Release|Win32 {AE293522-92D8-4B60-95C7-B4AEE10A303F}.Release|x64.ActiveCfg = Release|x64 {AE293522-92D8-4B60-95C7-B4AEE10A303F}.Release|x64.Build.0 = Release|x64 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Debug|Win32.ActiveCfg = Debug|Win32 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Debug|Win32.Build.0 = Debug|Win32 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Debug|x64.ActiveCfg = Debug|x64 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Debug|x64.Build.0 = Debug|x64 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Release|Win32.ActiveCfg = Release|Win32 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Release|Win32.Build.0 = Release|Win32 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Release|x64.ActiveCfg = Release|x64 + {AE293522-92D9-1B60-95C7-B4AEE10A303F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/examples.workspace b/examples.workspace new file mode 100644 index 0000000..f331b5f --- /dev/null +++ b/examples.workspace @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/miniz.c b/miniz.c index e6673d0..aaa10f2 100644 --- a/miniz.c +++ b/miniz.c @@ -1,18 +1,28 @@ -/* miniz.c v1.12 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +/* miniz.c v1.13 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. - Rich Geldreich , last updated April 12, 2012 + Rich Geldreich , last updated May 19, 2012 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). * Change History + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. + Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly + "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). + Created 32-bit and 64-bit Codeblocks projects/workspace. + Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. + Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. + Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) + Fix fell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. 5/28/11 v1.11 - Added statement from unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). + Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a + Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. Refactored the compression code for better readability and maintainability. Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large @@ -179,7 +189,7 @@ extern "C" { // ------------------- zlib-style API Definitions. -// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. +// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! typedef unsigned long mz_ulong; // Heap allocation callbacks. @@ -204,11 +214,11 @@ enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3 #ifndef MINIZ_NO_ZLIB_APIS -#define MZ_VERSION "9.1.12" -#define MZ_VERNUM 0x91C0 +#define MZ_VERSION "9.1.13" +#define MZ_VERNUM 0x91D0 #define MZ_VER_MAJOR 9 #define MZ_VER_MINOR 1 -#define MZ_VER_REVISION 12 +#define MZ_VER_REVISION 13 #define MZ_VER_SUBREVISION 0 // Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). @@ -430,7 +440,11 @@ typedef int mz_bool; #define MZ_TRUE (1) // Works around MSVC's spammy "warning C4127: conditional expression is constant" message. -#define MZ_MACRO_END while (0, 0) +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif // ------------------- ZIP archive reading/writing @@ -835,7 +849,7 @@ typedef struct mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; } tdefl_compressor; -// Initializes the compressor. +// Initializes the compressor. // There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. // pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. // If pBut_buf_func is NULL the user should always call the tdefl_compress() API. @@ -852,11 +866,14 @@ tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, siz tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); mz_uint32 tdefl_get_adler32(tdefl_compressor *d); +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS // Create tdefl_compress() flags given zlib-style compression parameters. // level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) // window_bits may be -15 (raw deflate) or 15 (zlib) // strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS #ifdef __cplusplus } @@ -879,7 +896,7 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; #ifdef MINIZ_NO_MALLOC #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) x, ((void)0) + #define MZ_FREE(x) (void)x, ((void)0) #define MZ_REALLOC(p, x) NULL #else #define MZ_MALLOC(x) malloc(x) @@ -913,10 +930,6 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; // ------------------- zlib-style API's -static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque; return MZ_MALLOC(items * size); } -static void def_free_func(void *opaque, void *address) { (void)opaque, MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque; return MZ_REALLOC(address, items * size); } - mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; @@ -937,12 +950,18 @@ mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; + mz_uint32 crcu32 = (mz_uint32)crc; if (!ptr) return MZ_CRC32_INIT; - crc = ~crc; while (buf_len--) { mz_uint8 b = *ptr++; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b & 0xF)]; crc = (crc >> 4) ^ s_crc32[(crc & 0xF) ^ (b >> 4)]; } return ~crc; + crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } + return ~crcu32; } #ifndef MINIZ_NO_ZLIB_APIS +static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } +static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } +static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } + const char *mz_version(void) { return MZ_VERSION; @@ -1055,7 +1074,7 @@ int mz_deflateEnd(mz_streamp pStream) mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { - pStream; + (void)pStream; // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); } @@ -2172,7 +2191,7 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); } - else if (pOutput_buf_start == d->m_output_buf) + else if (pOutput_buf_start == d->m_output_buf) { int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); @@ -2421,10 +2440,10 @@ static __forceinline void tdefl_record_match(tdefl_compressor *d, mz_uint match_ *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; - d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; + if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) @@ -2515,7 +2534,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d) } } else if (!cur_match_dist) - tdefl_record_literal(d, d->m_dict[cur_pos]); + tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) { tdefl_record_match(d, cur_match_len, cur_match_dist); @@ -2523,7 +2542,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d) } else { - d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; } // Move the lookahead forward by len_to_move bytes. d->m_lookahead_pos += len_to_move; @@ -2705,6 +2724,7 @@ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void return out_buf.m_size; } +#ifndef MINIZ_NO_ZLIB_APIS static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; // level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). @@ -2721,6 +2741,7 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int return comp_flags; } +#endif //MINIZ_NO_ZLIB_APIS #ifdef _MSC_VER #pragma warning (push) @@ -3016,7 +3037,7 @@ static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *p static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) { - mz_uint i, n, cdir_size, num_this_disk, cdir_disk_index; + mz_uint cdir_size, num_this_disk, cdir_disk_index; mz_uint64 cdir_ofs; mz_int64 cur_file_ofs; const mz_uint8 *p; @@ -3066,6 +3087,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 fl if (pZip->m_total_files) { + mz_uint i, n; // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) || @@ -3438,7 +3460,11 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif return MZ_FALSE; if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; @@ -3514,7 +3540,11 @@ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, si uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) return NULL; @@ -3594,7 +3624,11 @@ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_ind // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { +#ifdef _MSC_VER if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif return MZ_FALSE; if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; @@ -3755,6 +3789,7 @@ mz_bool mz_zip_reader_end(mz_zip_archive *pZip) return MZ_TRUE; } +#ifndef MINIZ_NO_STDIO mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) { int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); @@ -3762,6 +3797,7 @@ mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pAr return MZ_FALSE; return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); } +#endif // ------------------- .ZIP archive writing @@ -3807,7 +3843,11 @@ static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const vo mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; mz_zip_internal_state *pState = pZip->m_pState; mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); +#ifdef _MSC_VER if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#else + if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) +#endif return 0; if (new_size > pState->m_mem_capacity) { @@ -4216,7 +4256,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; MZ_FILE *pSrc_file = NULL; - + if ((int)level_and_flags < 0) level_and_flags = MZ_DEFAULT_LEVEL; level = level_and_flags & 0xF; @@ -4641,7 +4681,8 @@ mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const if ((!status) && (created_new_archive)) { // It's a new archive and something went wrong, so just delete it. - MZ_DELETE_FILE(pZip_filename); + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; } return status; } diff --git a/miniz_tester.cpp b/miniz_tester.cpp new file mode 100644 index 0000000..4608b1a --- /dev/null +++ b/miniz_tester.cpp @@ -0,0 +1,1804 @@ +// miniz_tester.cpp +// Note: This module is not intended to make a good example, or be used for anything other than testing. +// 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. +#ifdef _MSC_VER +#pragma warning (disable:4127) // warning C4127: conditional expression is constant +#endif + +#if defined(__GNUC__) +#define _FILE_OFFSET_BITS 64 +#endif + +#define MINIZ_HEADER_FILE_ONLY +#include "miniz.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "timer.h" + +#define my_max(a,b) (((a) > (b)) ? (a) : (b)) +#define my_min(a,b) (((a) < (b)) ? (a) : (b)) + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint; + +#define TDEFL_PRINT_OUTPUT_PROGRESS + +#if defined(WIN32) + #define WIN32_LEAN_AND_MEAN + #include + #define FILE_STAT_STRUCT _stat + #define FILE_STAT _stat +#else + #include + #define Sleep(ms) usleep(ms*1000) + #define _aligned_malloc(size, alignment) memalign(alignment, size) + #define _aligned_free free + #define fopen fopen64 + #define _fseeki64 fseeko64 + #define _ftelli64 ftello64 + #define _stricmp strcasecmp + #define FILE_STAT_STRUCT stat + #define FILE_STAT stat +#endif + +#ifdef WIN32 +#define QUAD_INT_FMT "%I64u" +#else +#define QUAD_INT_FMT "%llu" +#endif + +#ifdef _DEBUG +const bool g_is_debug = true; +#else +const bool g_is_debug = false; +#endif + +typedef unsigned char uint8; +typedef unsigned int uint; +typedef unsigned int uint32; +typedef unsigned long long uint64; +typedef long long int64; + +#define TDEFLTEST_COMP_INPUT_BUFFER_SIZE 1024*1024*2 +#define TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE 1024*1024*2 +#define TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE 1024*1024*2 + +static float s_max_small_comp_ratio, s_max_large_comp_ratio; + +struct comp_options +{ + comp_options() : + m_level(7), + m_unbuffered_decompression(false), + m_verify_compressed_data(false), + m_randomize_params(false), + m_randomize_buffer_sizes(false), + m_z_strat(Z_DEFAULT_STRATEGY), + m_random_z_flushing(false), + m_write_zlib_header(true), + m_archive_test(false), + m_write_archives(false) + { + } + + void print() + { + printf("Level: %u\n", m_level); + printf("Write zlib header: %u\n", (uint)m_write_zlib_header); + printf("Unbuffered decompression: %u\n", (uint)m_unbuffered_decompression); + printf("Verify compressed data: %u\n", (uint)m_verify_compressed_data); + printf("Randomize parameters: %u\n", m_randomize_params); + printf("Randomize buffer sizes: %u\n", m_randomize_buffer_sizes); + printf("Deflate strategy: %u\n", m_z_strat); + printf("Random Z stream flushing: %u\n", m_random_z_flushing); + printf("Archive test: %u\n", m_archive_test); + printf("Write archives: %u\n", m_write_archives); + } + + uint m_level; + bool m_unbuffered_decompression; + bool m_verify_compressed_data; + bool m_randomize_params; + bool m_randomize_buffer_sizes; + uint m_z_strat; + bool m_random_z_flushing; + bool m_write_zlib_header; + bool m_archive_test; + bool m_write_archives; +}; + +#define RND_SHR3(x) (x ^= (x << 17), x ^= (x >> 13), x ^= (x << 5)) + +#if 0 +static void random_fill(uint8 *pDst, size_t len, uint32 x) +{ + x ^= (x << 16); + if (!x) x++; + + while (len) + { + RND_SHR3(x); uint64 l0 = x & 0xFFF; + RND_SHR3(x); uint64 l1 = x & 0xFFF; + RND_SHR3(x); uint64 l2 = x & 0xFFF; + RND_SHR3(x); uint c = x; + + uint l = (uint)(((l0*l1*l2)/(16769025ULL) * 32) / 4095); + l = (uint)my_max(1,my_min(l, len)); + len -= l; + + while (l--) + { + *pDst++ = (uint8)c; + } + + if (((int)x < 0) && len) + { + *pDst++ = 0; + len--; + } + } +} +#endif + +static void print_usage() +{ + printf("Usage: [options] [mode] inpath/infile [outfile]\n"); + printf("\n"); + printf("Modes:\n"); + printf("c - Compress \"infile\" to \"outfile\"\n"); + printf("d - Decompress \"infile\" to \"outfile\"\n"); + printf("a - Recursively compress all files under \"inpath\"\n"); + printf("r - Archive decompression test\n"); + printf("\n"); + printf("Options:\n"); + printf("-m[0-10] - Compression level: 0=fastest (Huffman only), 9=best (10=uber)\n"); + printf("-u - Use unbuffered decompression on files that can fit into memory.\n"); + printf(" Unbuffered decompression is faster, but may have more I/O overhead.\n"); + printf("-v - Immediately decompress compressed file after compression for verification.\n"); + printf("-z - Do not write zlib header\n"); + printf("-r - Randomize parameters during recursive testing\n"); + printf("-b - Randomize input/output buffer sizes\n"); + printf("-h - Use random z_flushing\n"); + printf("-x# - Set rand() seed to value\n"); + printf("-t# - Set z_strategy to value [0-4]\n"); + printf("-a - Create single-file archives instead of files during testing\n"); + printf("-w - Test archive cloning\n"); +} + +static void print_error(const char *pMsg, ...) +{ + char buf[1024]; + + va_list args; + va_start(args, pMsg); + vsnprintf(buf, sizeof(buf), pMsg, args); + va_end(args); + + buf[sizeof(buf) - 1] = '\0'; + + fprintf(stderr, "Error: %s", buf); +} + +static FILE* open_file_with_retries(const char *pFilename, const char* pMode) +{ + const uint cNumRetries = 8; + for (uint i = 0; i < cNumRetries; i++) + { + FILE* pFile = fopen(pFilename, pMode); + if (pFile) + return pFile; + Sleep(250); + } + return NULL; +} + +static bool ensure_file_exists_and_is_readable(const char *pFilename) +{ + FILE *p = fopen(pFilename, "rb"); + if (!p) + return false; + + _fseeki64(p, 0, SEEK_END); + uint64 src_file_size = _ftelli64(p); + _fseeki64(p, 0, SEEK_SET); + + if (src_file_size) + { + char buf[1]; + if (fread(buf, 1, 1, p) != 1) + { + fclose(p); + return false; + } + } + fclose(p); + return true; +} + +static bool ensure_file_is_writable(const char *pFilename) +{ + const int cNumRetries = 8; + for (int i = 0; i < cNumRetries; i++) + { + FILE *pFile = fopen(pFilename, "wb"); + if (pFile) + { + fclose(pFile); + return true; + } + Sleep(250); + } + return false; +} + +static int simple_test1(const comp_options &options) +{ + options; + + uint8 cmp_buf[1024]; + size_t cmp_len = sizeof(cmp_buf); + + const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456"; + size_t uncomp_len = strlen(p); + + void *pComp_data = tdefl_compress_mem_to_heap(p, uncomp_len, &cmp_len, TDEFL_WRITE_ZLIB_HEADER); + if (!pComp_data) + { + free(pComp_data); + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len); + + size_t decomp_len = 0; + void *pDecomp_data = tinfl_decompress_mem_to_heap(pComp_data, cmp_len, &decomp_len, TINFL_FLAG_PARSE_ZLIB_HEADER); + + if ((!pDecomp_data) || (decomp_len != uncomp_len) || (memcmp(pDecomp_data, p, uncomp_len))) + { + free(pComp_data); + free(pDecomp_data); + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Low-level API compression test succeeded.\n"); + + free(pComp_data); + free(pDecomp_data); + + return EXIT_SUCCESS; +} + +static int simple_test2(const comp_options &options) +{ + options; + + uint8 cmp_buf[1024], decomp_buf[1024]; + uLong cmp_len = sizeof(cmp_buf); + + const char *p = "This is a test.This is a test.This is a test.1234567This is a test.This is a test.123456"; + uLong uncomp_len = (uLong)strlen(p); + + int status = compress(cmp_buf, &cmp_len, (const uint8*)p, uncomp_len); + if (status != Z_OK) + { + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("Uncompressed size: %u\nCompressed size: %u\n", (uint)uncomp_len, (uint)cmp_len); + + if (cmp_len > compressBound(uncomp_len)) + { + print_error("compressBound() returned bogus result\n"); + return EXIT_FAILURE; + } + + uLong decomp_len = sizeof(decomp_buf); + status = uncompress(decomp_buf, &decomp_len, cmp_buf, cmp_len);; + + if ((status != Z_OK) || (decomp_len != uncomp_len) || (memcmp(decomp_buf, p, uncomp_len))) + { + print_error("Compression test failed!\n"); + return EXIT_FAILURE; + } + + printf("zlib API compression test succeeded.\n"); + + return EXIT_SUCCESS; +} + +static bool compress_file_zlib(const char* pSrc_filename, const char *pDst_filename, const comp_options &options) +{ + printf("Testing: Streaming zlib compression\n"); + + FILE *pInFile = fopen(pSrc_filename, "rb"); + if (!pInFile) + { + print_error("Unable to read file: %s\n", pSrc_filename); + return false; + } + + FILE *pOutFile = fopen(pDst_filename, "wb"); + if (!pOutFile) + { + print_error("Unable to create file: %s\n", pDst_filename); + return false; + } + + _fseeki64(pInFile, 0, SEEK_END); + uint64 src_file_size = _ftelli64(pInFile); + _fseeki64(pInFile, 0, SEEK_SET); + + fputc('D', pOutFile); fputc('E', pOutFile); fputc('F', pOutFile); fputc('0', pOutFile); + fputc(options.m_write_zlib_header, pOutFile); + + for (uint i = 0; i < 8; i++) + fputc(static_cast((src_file_size >> (i * 8)) & 0xFF), pOutFile); + + uint cInBufSize = TDEFLTEST_COMP_INPUT_BUFFER_SIZE; + uint cOutBufSize = TDEFLTEST_COMP_OUTPUT_BUFFER_SIZE; + if (options.m_randomize_buffer_sizes) + { + cInBufSize = 1 + (rand() % 4096); + cOutBufSize = 1 + (rand() % 4096); + } + printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, cOutBufSize); + + uint8 *in_file_buf = static_cast(_aligned_malloc(cInBufSize, 16)); + uint8 *out_file_buf = static_cast(_aligned_malloc(cOutBufSize, 16)); + if ((!in_file_buf) || (!out_file_buf)) + { + print_error("Out of memory!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + uint64 src_bytes_left = src_file_size; + + uint in_file_buf_size = 0; + uint in_file_buf_ofs = 0; + + uint64 total_output_bytes = 0; + + timer_ticks start_time = timer::get_ticks(); + + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + timer_ticks init_start_time = timer::get_ticks(); + 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); + timer_ticks total_init_time = timer::get_ticks() - init_start_time; + + if (status != Z_OK) + { + print_error("Failed initializing compressor!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + printf("deflateInit2() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f); + + uint32 x = my_max(1, (uint32)(src_file_size ^ (src_file_size >> 32))); + + for ( ; ; ) + { + if (src_file_size) + { + double total_elapsed_time = timer::ticks_to_secs(timer::get_ticks() - start_time); + double total_bytes_processed = static_cast(src_file_size - src_bytes_left); + double comp_rate = (total_elapsed_time > 0.0f) ? total_bytes_processed / total_elapsed_time : 0.0f; + +#ifdef TDEFL_PRINT_OUTPUT_PROGRESS + for (int i = 0; i < 15; i++) + printf("\b\b\b\b"); + printf("Progress: %3.1f%%, Bytes Remaining: %3.1fMB, %3.3fMB/sec", (1.0f - (static_cast(src_bytes_left) / src_file_size)) * 100.0f, src_bytes_left / 1048576.0f, comp_rate / (1024.0f * 1024.0f)); + printf(" \b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); +#endif + } + + if (in_file_buf_ofs == in_file_buf_size) + { + in_file_buf_size = static_cast(my_min(cInBufSize, src_bytes_left)); + + if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size) + { + printf("\n"); + print_error("Failure reading from source file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + deflateEnd(&zstream); + return false; + } + + src_bytes_left -= in_file_buf_size; + + in_file_buf_ofs = 0; + } + + zstream.next_in = &in_file_buf[in_file_buf_ofs]; + zstream.avail_in = in_file_buf_size - in_file_buf_ofs; + zstream.next_out = out_file_buf; + zstream.avail_out = cOutBufSize; + + int flush = !src_bytes_left ? Z_FINISH : Z_NO_FLUSH; + if ((flush == Z_NO_FLUSH) && (options.m_random_z_flushing)) + { + RND_SHR3(x); + if ((x & 15) == 0) + { + RND_SHR3(x); + flush = (x & 31) ? Z_SYNC_FLUSH : Z_FULL_FLUSH; + } + } + status = deflate(&zstream, flush); + + uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs) - zstream.avail_in; + uint num_out_bytes = cOutBufSize - zstream.avail_out; + if (num_in_bytes) + { + in_file_buf_ofs += (uint)num_in_bytes; + assert(in_file_buf_ofs <= in_file_buf_size); + } + + if (num_out_bytes) + { + if (fwrite(out_file_buf, 1, static_cast(num_out_bytes), pOutFile) != num_out_bytes) + { + printf("\n"); + print_error("Failure writing to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + deflateEnd(&zstream); + return false; + } + + total_output_bytes += num_out_bytes; + } + + if (status != Z_OK) + break; + } + +#ifdef TDEFL_PRINT_OUTPUT_PROGRESS + for (int i = 0; i < 15; i++) + { + printf("\b\b\b\b \b\b\b\b"); + } +#endif + + src_bytes_left += (in_file_buf_size - in_file_buf_ofs); + + uint32 adler32 = zstream.adler; + deflateEnd(&zstream); + + timer_ticks end_time = timer::get_ticks(); + double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time)); + + uint64 cmp_file_size = _ftelli64(pOutFile); + + _aligned_free(in_file_buf); + in_file_buf = NULL; + _aligned_free(out_file_buf); + out_file_buf = NULL; + + fclose(pInFile); + pInFile = NULL; + fclose(pOutFile); + pOutFile = NULL; + + if (status != Z_STREAM_END) + { + print_error("Compression failed with status %i\n", status); + return false; + } + + if (src_bytes_left) + { + print_error("Compressor failed to consume entire input file!\n"); + return false; + } + + printf("Success\n"); + 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(cmp_file_size) / src_file_size)) * 100.0f) : 0.0f); + 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); + printf("Input file adler32: 0x%08X\n", adler32); + if (src_file_size) + { + if (src_file_size >= 256) + s_max_large_comp_ratio = my_max(s_max_large_comp_ratio, cmp_file_size / (float)src_file_size); + else + s_max_small_comp_ratio = my_max(s_max_small_comp_ratio, cmp_file_size / (float)src_file_size); + } + //printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio); + + return true; +} + +static bool decompress_file_zlib(const char* pSrc_filename, const char *pDst_filename, comp_options options) +{ + FILE *pInFile = fopen(pSrc_filename, "rb"); + if (!pInFile) + { + print_error("Unable to read file: %s\n", pSrc_filename); + return false; + } + + _fseeki64(pInFile, 0, SEEK_END); + uint64 src_file_size = _ftelli64(pInFile); + _fseeki64(pInFile, 0, SEEK_SET); + if (src_file_size < (5+9)) + { + print_error("Compressed file is too small!\n"); + fclose(pInFile); + return false; + } + + int h0 = fgetc(pInFile); + int h1 = fgetc(pInFile); + int h2 = fgetc(pInFile); + int h3 = fgetc(pInFile); + int zlib_header = fgetc(pInFile); + if ((h0 != 'D') | (h1 != 'E') || (h2 != 'F') || (h3 != '0')) + { + print_error("Unrecognized/invalid header in file: %s\n", pSrc_filename); + fclose(pInFile); + return false; + } + + FILE *pOutFile = fopen(pDst_filename, "wb"); + if (!pOutFile) + { + print_error("Unable to create file: %s\n", pDst_filename); + fclose(pInFile); + return false; + } + + uint64 orig_file_size = 0; + for (uint i = 0; i < 8; i++) + orig_file_size |= (static_cast(fgetc(pInFile)) << (i * 8)); + + int total_header_bytes = ftell(pInFile); + + // Avoid running out of memory on large files when using unbuffered decompression. + if ((options.m_unbuffered_decompression) && (orig_file_size > 768*1024*1024)) + { + printf("Output file is too large for unbuffered decompression - switching to streaming decompression.\n"); + options.m_unbuffered_decompression = false; + } + + if (options.m_unbuffered_decompression) + printf("Testing: Unbuffered decompression\n"); + else + printf("Testing: Streaming decompression\n"); + + uint cInBufSize = options.m_unbuffered_decompression ? static_cast(src_file_size) : TDEFLTEST_DECOMP_INPUT_BUFFER_SIZE; + uint out_buf_size = options.m_unbuffered_decompression ? static_cast(orig_file_size) : TINFL_LZ_DICT_SIZE; + + if ((options.m_randomize_buffer_sizes) && (!options.m_unbuffered_decompression)) + { + cInBufSize = 1 + (rand() % 4096); + } + + printf("Input buffer size: %u, Output buffer size: %u\n", cInBufSize, out_buf_size); + + uint8 *in_file_buf = static_cast(_aligned_malloc(cInBufSize, 16)); + uint8 *out_file_buf = static_cast(_aligned_malloc(out_buf_size, 16)); + if ((!in_file_buf) || (!out_file_buf)) + { + print_error("Failed allocating output buffer!\n"); + _aligned_free(in_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + uint64 src_bytes_left = src_file_size - total_header_bytes; + uint64 dst_bytes_left = orig_file_size; + + uint in_file_buf_size = 0; + uint in_file_buf_ofs = 0; + uint out_file_buf_ofs = 0; + + timer_ticks start_time = timer::get_ticks(); + double decomp_only_time = 0; + + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + + timer_ticks init_start_time = timer::get_ticks(); + int status = zlib_header ? inflateInit(&zstream) : inflateInit2(&zstream, -Z_DEFAULT_WINDOW_BITS); + timer_ticks total_init_time = timer::get_ticks() - init_start_time; + if (status != Z_OK) + { + print_error("Failed initializing decompressor!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + printf("inflateInit() took %3.3fms\n", timer::ticks_to_secs(total_init_time)*1000.0f); + + for ( ; ; ) + { + if (in_file_buf_ofs == in_file_buf_size) + { + in_file_buf_size = static_cast(my_min(cInBufSize, src_bytes_left)); + + if (fread(in_file_buf, 1, in_file_buf_size, pInFile) != in_file_buf_size) + { + print_error("Failure reading from source file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + deflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + + src_bytes_left -= in_file_buf_size; + + in_file_buf_ofs = 0; + } + + uint num_in_bytes = (in_file_buf_size - in_file_buf_ofs); + uint num_out_bytes = (out_buf_size - out_file_buf_ofs); + zstream.next_in = in_file_buf + in_file_buf_ofs; + zstream.avail_in = num_in_bytes; + zstream.next_out = out_file_buf + out_file_buf_ofs; + zstream.avail_out = num_out_bytes; + + { + timer decomp_only_timer; + decomp_only_timer.start(); + status = inflate(&zstream, options.m_unbuffered_decompression ? Z_FINISH : Z_SYNC_FLUSH); + decomp_only_time += decomp_only_timer.get_elapsed_secs(); + } + num_in_bytes -= zstream.avail_in; + num_out_bytes -= zstream.avail_out; + + if (num_in_bytes) + { + in_file_buf_ofs += (uint)num_in_bytes; + assert(in_file_buf_ofs <= in_file_buf_size); + } + + out_file_buf_ofs += (uint)num_out_bytes; + + if ((out_file_buf_ofs == out_buf_size) || (status == Z_STREAM_END)) + { + if (fwrite(out_file_buf, 1, static_cast(out_file_buf_ofs), pOutFile) != out_file_buf_ofs) + { + print_error("Failure writing to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + inflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + out_file_buf_ofs = 0; + } + + if (num_out_bytes > dst_bytes_left) + { + print_error("Decompressor wrote too many bytes to destination file!\n"); + _aligned_free(in_file_buf); + _aligned_free(out_file_buf); + inflateEnd(&zstream); + fclose(pInFile); + fclose(pOutFile); + return false; + } + dst_bytes_left -= num_out_bytes; + + if (status != Z_OK) + break; + } + _aligned_free(in_file_buf); + in_file_buf = NULL; + + _aligned_free(out_file_buf); + out_file_buf = NULL; + + src_bytes_left += (in_file_buf_size - in_file_buf_ofs); + + uint32 adler32 = zstream.adler; + inflateEnd(&zstream); + + timer_ticks end_time = timer::get_ticks(); + double total_time = timer::ticks_to_secs(my_max(1, end_time - start_time)); + + fclose(pInFile); + pInFile = NULL; + + fclose(pOutFile); + pOutFile = NULL; + + if (status != Z_STREAM_END) + { + print_error("Decompression FAILED with status %i\n", status); + return false; + } + + if ((src_file_size < UINT_MAX) && (orig_file_size < UINT_MAX)) + { + if ((((size_t)zstream.total_in + total_header_bytes) != src_file_size) || (zstream.total_out != orig_file_size)) + { + print_error("Decompression FAILED to consume all input or write all expected output!\n"); + return false; + } + } + + if (dst_bytes_left) + { + print_error("Decompressor FAILED to output the entire output file!\n"); + return false; + } + + if (src_bytes_left) + { + print_error("Decompressor FAILED to read " QUAD_INT_FMT " bytes from input buffer\n", src_bytes_left); + } + + printf("Success\n"); + printf("Source file size: " QUAD_INT_FMT ", Decompressed file size: " QUAD_INT_FMT "\n", src_file_size, orig_file_size); + if (zlib_header) printf("Decompressed adler32: 0x%08X\n", adler32); + 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); + 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); + + return true; +} + +static bool compare_files(const char *pFilename1, const char* pFilename2) +{ + FILE* pFile1 = open_file_with_retries(pFilename1, "rb"); + if (!pFile1) + { + print_error("Failed opening file: %s\n", pFilename1); + return false; + } + + FILE* pFile2 = open_file_with_retries(pFilename2, "rb"); + if (!pFile2) + { + print_error("Failed opening file: %s\n", pFilename2); + fclose(pFile1); + return false; + } + + _fseeki64(pFile1, 0, SEEK_END); + int64 fileSize1 = _ftelli64(pFile1); + _fseeki64(pFile1, 0, SEEK_SET); + + _fseeki64(pFile2, 0, SEEK_END); + int64 fileSize2 = _ftelli64(pFile2); + _fseeki64(pFile2, 0, SEEK_SET); + + if (fileSize1 != fileSize2) + { + print_error("Files to compare are not the same size: %I64i vs. %I64i.\n", fileSize1, fileSize2); + fclose(pFile1); + fclose(pFile2); + return false; + } + + const uint cBufSize = 1024 * 1024; + std::vector buf1(cBufSize); + std::vector buf2(cBufSize); + + int64 bytes_remaining = fileSize1; + while (bytes_remaining) + { + const uint bytes_to_read = static_cast(my_min(cBufSize, bytes_remaining)); + + if (fread(&buf1.front(), bytes_to_read, 1, pFile1) != 1) + { + print_error("Failed reading from file: %s\n", pFilename1); + fclose(pFile1); + fclose(pFile2); + return false; + } + + if (fread(&buf2.front(), bytes_to_read, 1, pFile2) != 1) + { + print_error("Failed reading from file: %s\n", pFilename2); + fclose(pFile1); + fclose(pFile2); + return false; + } + + if (memcmp(&buf1.front(), &buf2.front(), bytes_to_read) != 0) + { + print_error("File data comparison failed!\n"); + fclose(pFile1); + fclose(pFile2); + return false; + } + + bytes_remaining -= bytes_to_read; + } + + fclose(pFile1); + fclose(pFile2); + return true; +} + +static bool zip_create(const char *pZip_filename, const char *pSrc_filename) +{ + mz_zip_archive zip; + memset(&zip, 0, sizeof(zip)); + if ((rand() % 100) >= 10) + zip.m_file_offset_alignment = 1 << (rand() & 15); + if (!mz_zip_writer_init_file(&zip, pZip_filename, 65537)) + { + print_error("Failed creating zip archive \"%s\"!\n", pZip_filename); + return false; + } + + mz_bool success = MZ_TRUE; + + const char *pStr = "This is a test!This is a test!This is a test!\n"; + size_t comp_size; + void *pComp_data = tdefl_compress_mem_to_heap(pStr, strlen(pStr), &comp_size, 256); + 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))); + + success &= mz_zip_writer_add_mem(&zip, "cool/", NULL, 0, 0); + + success &= mz_zip_writer_add_mem(&zip, "1.txt", pStr, strlen(pStr), 9); + int n = rand() & 4095; + for (int i = 0; i < n; i++) + { + char name[256], buf[256], comment[256]; + sprintf(name, "t%u.txt", i); + sprintf(buf, "%u\n", i*5377); + sprintf(comment, "comment: %u\n", i); + success &= mz_zip_writer_add_mem_ex(&zip, name, buf, strlen(buf), comment, (uint16)strlen(comment), i % 10, 0, 0); + } + + const char *pTestComment = "test comment"; + success &= mz_zip_writer_add_file(&zip, "test.bin", pSrc_filename, pTestComment, (uint16)strlen(pTestComment), 9); + + if (ensure_file_exists_and_is_readable("changelog.txt")) + success &= mz_zip_writer_add_file(&zip, "changelog.txt", "changelog.txt", "This is a comment", (uint16)strlen("This is a comment"), 9); + + if (!success) + { + mz_zip_writer_end(&zip); + remove(pZip_filename); + print_error("Failed creating zip archive \"%s\"!\n", pZip_filename); + return false; + } + + if (!mz_zip_writer_finalize_archive(&zip)) + { + mz_zip_writer_end(&zip); + remove(pZip_filename); + print_error("Failed creating zip archive \"%s\"!\n", pZip_filename); + return false; + } + + mz_zip_writer_end(&zip); + + struct FILE_STAT_STRUCT stat_buf; + FILE_STAT(pZip_filename, &stat_buf); + uint64 actual_file_size = stat_buf.st_size; + if (zip.m_archive_size != actual_file_size) + { + print_error("Archive's actual size and zip archive object's size differ for file \"%s\"!\n", pZip_filename); + return false; + } + + printf("Created zip file \"%s\", file size: " QUAD_INT_FMT "\n", pZip_filename, zip.m_archive_size); + return true; +} + +static size_t zip_write_callback(void *pOpaque, uint64 ofs, const void *pBuf, size_t n) +{ + pOpaque, ofs, pBuf, n; + return n; +} + +static bool zip_extract(const char *pZip_filename, const char *pDst_filename) +{ + mz_zip_archive zip; + memset(&zip, 0, sizeof(zip)); + if (!mz_zip_reader_init_file(&zip, pZip_filename, 0)) + { + print_error("Failed opening zip archive \"%s\"!\n", pZip_filename); + return false; + } + + int file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment", 0); + int alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", "test Comment e", 0); + if ((file_index < 0) || (alt_file_index >= 0)) + { + print_error("Archive \"%s\" is missing test.bin file!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + alt_file_index = mz_zip_reader_locate_file(&zip, "test.bin", NULL, 0); + if (alt_file_index != file_index) + { + print_error("mz_zip_reader_locate_file() failed!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + if (!mz_zip_reader_extract_to_file(&zip, file_index, pDst_filename, 0)) + { + print_error("Failed extracting test.bin from archive \"%s\"!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + for (uint i = 0; i < mz_zip_reader_get_num_files(&zip); i++) + { + mz_zip_archive_file_stat stat; + if (!mz_zip_reader_file_stat(&zip, i, &stat)) + { + print_error("Failed testing archive \"%s\"!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + //printf("\"%s\" %I64u %I64u\n", stat.m_filename, stat.m_comp_size, stat.m_uncomp_size); + size_t size = 0; + + mz_bool status = mz_zip_reader_extract_to_callback(&zip, i, zip_write_callback, NULL, 0); + if (!status) + { + print_error("Failed testing archive \"%s\"!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + + void *p = mz_zip_reader_extract_to_heap(&zip, i, &size, 0); + if (!p) + { + print_error("Failed testing archive \"%s\"!\n", pZip_filename); + mz_zip_reader_end(&zip); + return false; + } + free(p); + } + printf("Verified %u files\n", mz_zip_reader_get_num_files(&zip)); + + mz_zip_reader_end(&zip); + + printf("Extracted file \"%s\"\n", pDst_filename); + return true; +} + +typedef std::vector< std::string > string_array; + +#if defined(WIN32) +static bool find_files(std::string pathname, const std::string &filename, string_array &files, bool recursive, int depth = 0) +{ + if (!pathname.empty()) + { + char c = pathname[pathname.size() - 1]; + if ((c != ':') && (c != '\\') && (c != '/')) + pathname += "\\"; + } + + WIN32_FIND_DATAA find_data; + + HANDLE findHandle = FindFirstFileA((pathname + filename).c_str(), &find_data); + if (findHandle == INVALID_HANDLE_VALUE) + { + HRESULT hres = GetLastError(); + if ((!depth) && (hres != NO_ERROR) && (hres != ERROR_FILE_NOT_FOUND)) + return false; + } + else + { + do + { + const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + const bool is_system = (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0; + const bool is_hidden = false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0; + + std::string filename(find_data.cFileName); + + if ((!is_directory) && (!is_system) && (!is_hidden)) + files.push_back(pathname + filename); + + } while (FindNextFileA(findHandle, &find_data)); + + FindClose(findHandle); + } + + if (recursive) + { + string_array paths; + + HANDLE findHandle = FindFirstFileA((pathname + "*").c_str(), &find_data); + if (findHandle != INVALID_HANDLE_VALUE) + { + do + { + const bool is_directory = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + const bool is_system = (find_data.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) != 0; + const bool is_hidden = false;//(find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0; + + std::string filename(find_data.cFileName); + + if ((is_directory) && (!is_hidden) && (!is_system)) + paths.push_back(filename); + + } while (FindNextFileA(findHandle, &find_data)); + + FindClose(findHandle); + + for (uint i = 0; i < paths.size(); i++) + { + const std::string &path = paths[i]; + if (path[0] == '.') + continue; + + if (!find_files(pathname + path, filename, files, true, depth + 1)) + return false; + } + } + } + + return true; +} +#else +#include +#include +static bool find_files(std::string pathname, const std::string &pattern, string_array &files, bool recursive, int depth = 0) +{ + if (!pathname.empty()) + { + char c = pathname[pathname.size() - 1]; + if ((c != ':') && (c != '\\') && (c != '/')) + pathname += "/"; + } + + DIR *dp = opendir(pathname.c_str()); + + if (!dp) + return depth ? true : false; + + string_array paths; + + for ( ; ; ) + { + struct dirent *ep = readdir(dp); + if (!ep) + break; + + const bool is_directory = (ep->d_type & DT_DIR) != 0; + const bool is_file = (ep->d_type & DT_REG) != 0; + + if (ep->d_name[0] == '.') + continue; + + std::string filename(ep->d_name); + + if (is_directory) + { + if (recursive) + paths.push_back(filename); + } + else if (is_file) + { + if (0 == fnmatch(pattern.c_str(), filename.c_str(), 0)) + files.push_back(pathname + filename); + } + } + + closedir(dp); + dp = NULL; + + if (recursive) + { + for (uint i = 0; i < paths.size(); i++) + { + const std::string &path = paths[i]; + if (!find_files(pathname + path, pattern, files, true, depth + 1)) + return false; + } + } + + return true; +} +#endif + +static bool test_recursive(const char *pPath, comp_options options) +{ + string_array files; + if (!find_files(pPath, "*", files, true)) + { + print_error("Failed finding files under path \"%s\"!\n", pPath); + return false; + } + + uint total_files_compressed = 0; + uint64 total_source_size = 0; + uint64 total_comp_size = 0; + +#ifdef WIN32 + MEMORYSTATUS initial_mem_status; + GlobalMemoryStatus(&initial_mem_status); +#endif + + timer_ticks start_tick_count = timer::get_ticks(); + + const int first_file_index = 0; + + uint unique_id = static_cast(timer::get_init_ticks()); + char cmp_file[256], decomp_file[256]; + + sprintf(cmp_file, "__comp_temp_%u__.tmp", unique_id); + sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id); + + for (uint file_index = first_file_index; file_index < files.size(); file_index++) + { + const std::string &src_file = files[file_index]; + + printf("***** [%u of %u] Compressing file \"%s\" to \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str(), cmp_file); + + if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL)) + { + printf("Skipping temporary file \"%s\"\n", src_file.c_str()); + continue; + } + + FILE *pFile = fopen(src_file.c_str(), "rb"); + if (!pFile) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + continue; + } + _fseeki64(pFile, 0, SEEK_END); + int64 src_file_size = _ftelli64(pFile); + + if (src_file_size) + { + _fseeki64(pFile, 0, SEEK_SET); + if (fgetc(pFile) == EOF) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + fclose(pFile); + continue; + } + } + + fclose(pFile); + + if (!ensure_file_is_writable(cmp_file)) + { + print_error("Unable to create file \"%s\"!\n", cmp_file); + return false; + } + + comp_options file_options(options); + if (options.m_randomize_params) + { + file_options.m_level = rand() % 11; + file_options.m_unbuffered_decompression = (rand() & 1) != 0; + file_options.m_z_strat = rand() % (Z_FIXED + 1); + file_options.m_write_zlib_header = (rand() & 1) != 0; + file_options.m_random_z_flushing = (rand() & 1) != 0; + file_options.print(); + } + + bool status; + if (file_options.m_archive_test) + { + if (src_file_size > 0xFFF00000) + { + printf("Skipping too big file \"%s\"\n", src_file.c_str()); + continue; + } + printf("Creating test archive with file \"%s\", size " QUAD_INT_FMT "\n", src_file.c_str(), src_file_size); + status = zip_create(cmp_file, src_file.c_str()); + } + else + status = compress_file_zlib(src_file.c_str(), cmp_file, file_options); + if (!status) + { + print_error("Failed compressing file \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file); + return false; + } + + if (file_options.m_verify_compressed_data) + { + printf("Decompressing file \"%s\" to \"%s\"\n", cmp_file, decomp_file); + + if (!ensure_file_is_writable(decomp_file)) + { + print_error("Unable to create file \"%s\"!\n", decomp_file); + return false; + } + + if (file_options.m_archive_test) + status = zip_extract(cmp_file, decomp_file); + else + status = decompress_file_zlib(cmp_file, decomp_file, file_options); + + if (!status) + { + print_error("Failed decompressing file \"%s\" to \"%s\"\n", src_file.c_str(), decomp_file); + return false; + } + + printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str()); + + if (!compare_files(decomp_file, src_file.c_str())) + { + print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file); + return false; + } + else + { + printf("Decompressed file compared OK to original file.\n"); + } + } + + int64 cmp_file_size = 0; + pFile = fopen(cmp_file, "rb"); + if (pFile) + { + _fseeki64(pFile, 0, SEEK_END); + cmp_file_size = _ftelli64(pFile); + fclose(pFile); + } + + total_files_compressed++; + total_source_size += src_file_size; + total_comp_size += cmp_file_size; + +#ifdef WIN32 + MEMORYSTATUS mem_status; + GlobalMemoryStatus(&mem_status); + + const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual; + + printf("Memory allocated relative to first file: %I64i\n", bytes_allocated); +#endif + + printf("\n"); + } + + timer_ticks end_tick_count = timer::get_ticks(); + + double total_elapsed_time = timer::ticks_to_secs(end_tick_count - start_tick_count); + + printf("Test successful: %f secs\n", total_elapsed_time); + printf("Total files processed: %u\n", total_files_compressed); + printf("Total source size: " QUAD_INT_FMT "\n", total_source_size); + printf("Total compressed size: " QUAD_INT_FMT "\n", total_comp_size); + printf("Max small comp ratio: %f, Max large comp ratio: %f\n", s_max_small_comp_ratio, s_max_large_comp_ratio); + + remove(cmp_file); + remove(decomp_file); + + return true; +} + +static size_t dummy_zip_file_write_callback(void *pOpaque, uint64 ofs, const void *pBuf, size_t n) +{ + ofs; pBuf; + uint32 *pCRC = (uint32*)pOpaque; + *pCRC = mz_crc32(*pCRC, (const uint8*)pBuf, n); + return n; +} + +static bool test_archives(const char *pPath, comp_options options) +{ + options; + + string_array files; + if (!find_files(pPath, "*.zip", files, true)) + { + print_error("Failed finding files under path \"%s\"!\n", pPath); + return false; + } + + uint total_archives = 0; + uint64 total_bytes_processed = 0; + uint64 total_files_processed = 0; + uint total_errors = 0; + +#ifdef WIN32 + MEMORYSTATUS initial_mem_status; + GlobalMemoryStatus(&initial_mem_status); +#endif + + const int first_file_index = 0; + uint unique_id = static_cast(timer::get_init_ticks()); + char cmp_file[256], decomp_file[256]; + + sprintf(decomp_file, "__decomp_temp_%u__.tmp", unique_id); + + string_array failed_archives; + + for (uint file_index = first_file_index; file_index < files.size(); file_index++) + { + const std::string &src_file = files[file_index]; + + printf("***** [%u of %u] Testing archive file \"%s\"\n", 1 + file_index, (uint)files.size(), src_file.c_str()); + + if ((strstr(src_file.c_str(), "__comp_temp") != NULL) || (strstr(src_file.c_str(), "__decomp_temp") != NULL)) + { + printf("Skipping temporary file \"%s\"\n", src_file.c_str()); + continue; + } + + FILE *pFile = fopen(src_file.c_str(), "rb"); + if (!pFile) + { + printf("Skipping unreadable file \"%s\"\n", src_file.c_str()); + continue; + } + _fseeki64(pFile, 0, SEEK_END); + int64 src_file_size = _ftelli64(pFile); + fclose(pFile); + + src_file_size; + + sprintf(cmp_file, "__comp_temp_%u__.zip", file_index); + + mz_zip_archive src_archive; + memset(&src_archive, 0, sizeof(src_archive)); + + if (!mz_zip_reader_init_file(&src_archive, src_file.c_str(), 0)) + { + failed_archives.push_back(src_file); + + print_error("Failed opening archive \"%s\"!\n", src_file.c_str()); + total_errors++; + continue; + } + + mz_zip_archive dst_archive; + memset(&dst_archive, 0, sizeof(dst_archive)); + if (options.m_write_archives) + { + if (!ensure_file_is_writable(cmp_file)) + { + print_error("Unable to create file \"%s\"!\n", cmp_file); + return false; + } + + if (!mz_zip_writer_init_file(&dst_archive, cmp_file, 0)) + { + print_error("Failed creating archive \"%s\"!\n", cmp_file); + total_errors++; + continue; + } + } + + int i; + //for (i = 0; i < mz_zip_reader_get_num_files(&src_archive); i++) + for (i = mz_zip_reader_get_num_files(&src_archive) - 1; i >= 0; --i) + { + if (mz_zip_reader_is_file_encrypted(&src_archive, i)) + continue; + + mz_zip_archive_file_stat file_stat; + bool status = mz_zip_reader_file_stat(&src_archive, i, &file_stat) != 0; + + int locate_file_index = mz_zip_reader_locate_file(&src_archive, file_stat.m_filename, NULL, 0); + if (locate_file_index != i) + { + mz_zip_archive_file_stat locate_file_stat; + mz_zip_reader_file_stat(&src_archive, locate_file_index, &locate_file_stat); + if (_stricmp(locate_file_stat.m_filename, file_stat.m_filename) != 0) + { + print_error("mz_zip_reader_locate_file() failed!\n"); + return false; + } + else + { + printf("Warning: Duplicate filenames in archive!\n"); + } + } + + if ((file_stat.m_method) && (file_stat.m_method != MZ_DEFLATED)) + continue; + + if (status) + { + char name[260]; + mz_zip_reader_get_filename(&src_archive, i, name, sizeof(name)); + + size_t extracted_size = 0; + void *p = mz_zip_reader_extract_file_to_heap(&src_archive, name, &extracted_size, 0); + if (!p) + status = false; + + uint32 extracted_crc32 = MZ_CRC32_INIT; + if (!mz_zip_reader_extract_file_to_callback(&src_archive, name, dummy_zip_file_write_callback, &extracted_crc32, 0)) + status = false; + + if (mz_crc32(MZ_CRC32_INIT, (const uint8*)p, extracted_size) != extracted_crc32) + status = false; + + free(p); + + if (options.m_write_archives) + { + if ((status) && (!mz_zip_writer_add_from_zip_reader(&dst_archive, &src_archive, i))) + { + print_error("Failed adding new file to archive \"%s\"!\n", cmp_file); + status = false; + } + } + + total_bytes_processed += file_stat.m_uncomp_size; + total_files_processed++; + } + + if (!status) + break; + } + + mz_zip_reader_end(&src_archive); + + //if (i < mz_zip_reader_get_num_files(&src_archive)) + if (i >= 0) + { + failed_archives.push_back(src_file); + + print_error("Failed processing archive \"%s\"!\n", src_file.c_str()); + total_errors++; + } + + if (options.m_write_archives) + { + if (!mz_zip_writer_finalize_archive(&dst_archive) || !mz_zip_writer_end(&dst_archive)) + { + failed_archives.push_back(src_file); + + print_error("Failed finalizing archive \"%s\"!\n", cmp_file); + total_errors++; + } + } + + total_archives++; + +#ifdef WIN32 + MEMORYSTATUS mem_status; + GlobalMemoryStatus(&mem_status); + + const int64 bytes_allocated = initial_mem_status.dwAvailVirtual- mem_status.dwAvailVirtual; + + printf("Memory allocated relative to first file: %I64i\n", bytes_allocated); +#endif + + printf("\n"); + } + + printf("Total archives processed: %u\n", total_archives); + printf("Total errors: %u\n", total_errors); + printf("Total bytes processed: " QUAD_INT_FMT "\n", total_bytes_processed); + printf("Total archive files processed: " QUAD_INT_FMT "\n", total_files_processed); + + printf("List of failed archives:\n"); + for (uint i = 0; i < failed_archives.size(); ++i) + printf("%s\n", failed_archives[i].c_str()); + + remove(cmp_file); + remove(decomp_file); + + return true; +} + +int main_internal(string_array cmd_line) +{ + comp_options options; + + if (!cmd_line.size()) + { + print_usage(); + if (simple_test1(options) || simple_test2(options)) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } + + enum op_mode_t + { + OP_MODE_INVALID = -1, + OP_MODE_COMPRESS = 0, + OP_MODE_DECOMPRESS = 1, + OP_MODE_ALL = 2, + OP_MODE_ARCHIVES = 3 + }; + + op_mode_t op_mode = OP_MODE_INVALID; + + for (int i = 0; i < (int)cmd_line.size(); i++) + { + const std::string &str = cmd_line[i]; + if (str[0] == '-') + { + if (str.size() < 2) + { + print_error("Invalid option: %s\n", str.c_str()); + return EXIT_FAILURE; + } + switch (tolower(str[1])) + { + case 'u': + { + options.m_unbuffered_decompression = true; + break; + } + case 'm': + { + int comp_level = atoi(str.c_str() + 2); + if ((comp_level < 0) || (comp_level > (int)10)) + { + print_error("Invalid compression level: %s\n", str.c_str()); + return EXIT_FAILURE; + } + + options.m_level = comp_level; + break; + } + case 'v': + { + options.m_verify_compressed_data = true; + break; + } + case 'r': + { + options.m_randomize_params = true; + break; + } + case 'b': + { + options.m_randomize_buffer_sizes = true; + break; + } + case 'h': + { + options.m_random_z_flushing = true; + break; + } + case 'x': + { + int seed = atoi(str.c_str() + 2); + srand(seed); + printf("Using random seed: %i\n", seed); + break; + } + case 't': + { + options.m_z_strat = my_min(Z_FIXED, my_max(0, atoi(str.c_str() + 2))); + break; + } + case 'z': + { + options.m_write_zlib_header = false; + break; + } + case 'a': + { + options.m_archive_test = true; + break; + } + case 'w': + { + options.m_write_archives = true; + break; + } + default: + { + print_error("Invalid option: %s\n", str.c_str()); + return EXIT_FAILURE; + } + } + + cmd_line.erase(cmd_line.begin() + i); + i--; + + continue; + } + + if (str.size() != 1) + { + print_error("Invalid mode: %s\n", str.c_str()); + return EXIT_FAILURE; + } + switch (tolower(str[0])) + { + case 'c': + { + op_mode = OP_MODE_COMPRESS; + break; + } + case 'd': + { + op_mode = OP_MODE_DECOMPRESS; + break; + } + case 'a': + { + op_mode = OP_MODE_ALL; + break; + } + case 'r': + { + op_mode = OP_MODE_ARCHIVES; + break; + } + default: + { + print_error("Invalid mode: %s\n", str.c_str()); + return EXIT_FAILURE; + } + } + cmd_line.erase(cmd_line.begin() + i); + break; + } + + if (op_mode == OP_MODE_INVALID) + { + print_error("No mode specified!\n"); + print_usage(); + return EXIT_FAILURE; + } + + printf("Using options:\n"); + options.print(); + printf("\n"); + + int exit_status = EXIT_FAILURE; + + switch (op_mode) + { + case OP_MODE_COMPRESS: + { + if (cmd_line.size() < 2) + { + print_error("Must specify input and output filenames!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() > 2) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + + const std::string &src_file = cmd_line[0]; + const std::string &cmp_file = cmd_line[1]; + + bool comp_result = compress_file_zlib(src_file.c_str(), cmp_file.c_str(), options); + if (comp_result) + exit_status = EXIT_SUCCESS; + + if ((comp_result) && (options.m_verify_compressed_data)) + { + char decomp_file[256]; + + sprintf(decomp_file, "__decomp_temp_%u__.tmp", (uint)timer::get_ms()); + + if (!decompress_file_zlib(cmp_file.c_str(), decomp_file, options)) + { + print_error("Failed decompressing file \"%s\" to \"%s\"\n", cmp_file.c_str(), decomp_file); + return EXIT_FAILURE; + } + + printf("Comparing file \"%s\" to \"%s\"\n", decomp_file, src_file.c_str()); + + if (!compare_files(decomp_file, src_file.c_str())) + { + print_error("Failed comparing decompressed file data while compressing \"%s\" to \"%s\"\n", src_file.c_str(), cmp_file.c_str()); + return EXIT_FAILURE; + } + else + { + printf("Decompressed file compared OK to original file.\n"); + } + + remove(decomp_file); + } + + break; + } + case OP_MODE_DECOMPRESS: + { + if (cmd_line.size() < 2) + { + print_error("Must specify input and output filenames!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() > 2) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (decompress_file_zlib(cmd_line[0].c_str(), cmd_line[1].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + case OP_MODE_ALL: + { + if (!cmd_line.size()) + { + print_error("No directory specified!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() != 1) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (test_recursive(cmd_line[0].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + case OP_MODE_ARCHIVES: + { + if (!cmd_line.size()) + { + print_error("No directory specified!\n"); + return EXIT_FAILURE; + } + else if (cmd_line.size() != 1) + { + print_error("Too many filenames!\n"); + return EXIT_FAILURE; + } + if (test_archives(cmd_line[0].c_str(), options)) + exit_status = EXIT_SUCCESS; + break; + } + default: + { + print_error("No mode specified!\n"); + print_usage(); + return EXIT_FAILURE; + } + } + + return exit_status; +} + +int main(int argc, char *argv[]) +{ +#ifdef _WIN64 + printf("miniz.c x64 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__); +#else + printf("miniz.c x86 Command Line Test App - Compiled %s %s\n", __DATE__, __TIME__); +#endif + timer::get_ticks(); + + string_array cmd_line; + for (int i = 1; i < argc; i++) + cmd_line.push_back(std::string(argv[i])); + + int exit_status = main_internal(cmd_line); + + return exit_status; +} diff --git a/miniz_tester.vcproj b/miniz_tester.vcproj new file mode 100644 index 0000000..9a4a2e0 --- /dev/null +++ b/miniz_tester.vcproj @@ -0,0 +1,358 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/timer.cpp b/timer.cpp new file mode 100644 index 0000000..67fba46 --- /dev/null +++ b/timer.cpp @@ -0,0 +1,152 @@ +// File: timer.cpp - Simple high-precision timer class. Supports Win32, X360, and POSIX/Linux +#include +#include +#include +#include + +#include "timer.h" + +#if defined(WIN32) +#include +#elif defined(_XBOX) +#include +#endif + +unsigned long long timer::g_init_ticks; +unsigned long long timer::g_freq; +double timer::g_inv_freq; + +#if defined(WIN32) || defined(_XBOX) +inline void query_counter(timer_ticks *pTicks) +{ + QueryPerformanceCounter(reinterpret_cast(pTicks)); +} +inline void query_counter_frequency(timer_ticks *pTicks) +{ + QueryPerformanceFrequency(reinterpret_cast(pTicks)); +} +#elif defined(__GNUC__) +#include +inline void query_counter(timer_ticks *pTicks) +{ + struct timeval cur_time; + gettimeofday(&cur_time, NULL); + *pTicks = static_cast(cur_time.tv_sec)*1000000ULL + static_cast(cur_time.tv_usec); +} +inline void query_counter_frequency(timer_ticks *pTicks) +{ + *pTicks = 1000000; +} +#endif + +timer::timer() : + m_start_time(0), + m_stop_time(0), + m_started(false), + m_stopped(false) +{ + if (!g_inv_freq) + init(); +} + +timer::timer(timer_ticks start_ticks) +{ + if (!g_inv_freq) + init(); + + m_start_time = start_ticks; + + m_started = true; + m_stopped = false; +} + +void timer::start(timer_ticks start_ticks) +{ + m_start_time = start_ticks; + + m_started = true; + m_stopped = false; +} + +void timer::start() +{ + query_counter(&m_start_time); + + m_started = true; + m_stopped = false; +} + +void timer::stop() +{ + assert(m_started); + + query_counter(&m_stop_time); + + m_stopped = true; +} + +double timer::get_elapsed_secs() const +{ + assert(m_started); + if (!m_started) + return 0; + + timer_ticks stop_time = m_stop_time; + if (!m_stopped) + query_counter(&stop_time); + + timer_ticks delta = stop_time - m_start_time; + return delta * g_inv_freq; +} + +timer_ticks timer::get_elapsed_us() const +{ + assert(m_started); + if (!m_started) + return 0; + + timer_ticks stop_time = m_stop_time; + if (!m_stopped) + query_counter(&stop_time); + + timer_ticks delta = stop_time - m_start_time; + return (delta * 1000000ULL + (g_freq >> 1U)) / g_freq; +} + +void timer::init() +{ + if (!g_inv_freq) + { + query_counter_frequency(&g_freq); + g_inv_freq = 1.0f / g_freq; + + query_counter(&g_init_ticks); + } +} + +timer_ticks timer::get_init_ticks() +{ + if (!g_inv_freq) + init(); + + return g_init_ticks; +} + +timer_ticks timer::get_ticks() +{ + if (!g_inv_freq) + init(); + + timer_ticks ticks; + query_counter(&ticks); + return ticks - g_init_ticks; +} + +double timer::ticks_to_secs(timer_ticks ticks) +{ + if (!g_inv_freq) + init(); + + return ticks * g_inv_freq; +} + diff --git a/timer.h b/timer.h new file mode 100644 index 0000000..ae0f58b --- /dev/null +++ b/timer.h @@ -0,0 +1,40 @@ +// File: timer.h +#pragma once + +typedef unsigned long long timer_ticks; + +class timer +{ +public: + timer(); + timer(timer_ticks start_ticks); + + void start(); + void start(timer_ticks start_ticks); + + void stop(); + + double get_elapsed_secs() const; + inline double get_elapsed_ms() const { return get_elapsed_secs() * 1000.0f; } + timer_ticks get_elapsed_us() const; + + static void init(); + static inline timer_ticks get_ticks_per_sec() { return g_freq; } + static timer_ticks get_init_ticks(); + static timer_ticks get_ticks(); + static double ticks_to_secs(timer_ticks ticks); + static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; } + static inline double get_secs() { return ticks_to_secs(get_ticks()); } + static inline double get_ms() { return ticks_to_ms(get_ticks()); } + +private: + static timer_ticks g_init_ticks; + static timer_ticks g_freq; + static double g_inv_freq; + + timer_ticks m_start_time; + timer_ticks m_stop_time; + + bool m_started : 1; + bool m_stopped : 1; +}; diff --git a/tinfl.c b/tinfl.c index d82a50f..d00addf 100644 --- a/tinfl.c +++ b/tinfl.c @@ -10,11 +10,11 @@ #include -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; typedef unsigned long long mz_uint64; #if defined(_M_IX86) || defined(_M_X64) @@ -30,7 +30,11 @@ typedef unsigned long long mz_uint64; #endif // Works around MSVC's spammy "warning C4127: conditional expression is constant" message. -#define MZ_MACRO_END while (0, 0) +#ifdef _MSC_VER + #define MZ_MACRO_END while (0, 0) +#else + #define MZ_MACRO_END while (0) +#endif // Decompression flags used by tinfl_decompress(). // TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. @@ -560,7 +564,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, #endif // #ifndef TINFL_HEADER_FILE_ONLY -/* +/* This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or