Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 1 | // LZ4 HC streaming API example : ring buffer |
Yann Collet | 6c94c94 | 2017-11-24 17:18:46 -0800 | [diff] [blame] | 2 | // Based on a previous example by Takayuki Matsuoka |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 3 | |
| 4 | |
| 5 | /************************************** |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 6 | * Compiler Options |
| 7 | **************************************/ |
Yann Collet | 6c94c94 | 2017-11-24 17:18:46 -0800 | [diff] [blame] | 8 | #if defined(_MSC_VER) && (_MSC_VER <= 1800) /* Visual Studio <= 2013 */ |
| 9 | # define _CRT_SECURE_NO_WARNINGS |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 10 | # define snprintf sprintf_s |
| 11 | #endif |
| 12 | |
| 13 | #define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) |
| 14 | #ifdef __GNUC__ |
| 15 | # pragma GCC diagnostic ignored "-Wmissing-braces" /* GCC bug 53119 : doesn't accept { 0 } as initializer (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119) */ |
| 16 | #endif |
| 17 | |
| 18 | |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 19 | /************************************** |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 20 | * Includes |
| 21 | **************************************/ |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 22 | #include "lz4hc.h" |
| 23 | #include "lz4.h" |
| 24 | |
| 25 | #include <stdio.h> |
| 26 | #include <stdint.h> |
| 27 | #include <stdlib.h> |
| 28 | #include <string.h> |
Yann Collet | 3b917ef | 2019-06-28 20:55:47 -0700 | [diff] [blame] | 29 | #include <assert.h> |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 30 | |
| 31 | enum { |
| 32 | MESSAGE_MAX_BYTES = 1024, |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 33 | RING_BUFFER_BYTES = 1024 * 8 + MESSAGE_MAX_BYTES, |
Yann Collet | 7050d4b | 2014-11-04 10:04:37 +0100 | [diff] [blame] | 34 | DEC_BUFFER_BYTES = RING_BUFFER_BYTES + MESSAGE_MAX_BYTES // Intentionally larger to test unsynchronized ring buffers |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 35 | }; |
| 36 | |
| 37 | |
| 38 | size_t write_int32(FILE* fp, int32_t i) { |
| 39 | return fwrite(&i, sizeof(i), 1, fp); |
| 40 | } |
| 41 | |
| 42 | size_t write_bin(FILE* fp, const void* array, int arrayBytes) { |
Yann Collet | 3b917ef | 2019-06-28 20:55:47 -0700 | [diff] [blame] | 43 | assert(arrayBytes >= 0); |
| 44 | return fwrite(array, 1, (size_t)arrayBytes, fp); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 45 | } |
| 46 | |
| 47 | size_t read_int32(FILE* fp, int32_t* i) { |
| 48 | return fread(i, sizeof(*i), 1, fp); |
| 49 | } |
| 50 | |
| 51 | size_t read_bin(FILE* fp, void* array, int arrayBytes) { |
Yann Collet | 3b917ef | 2019-06-28 20:55:47 -0700 | [diff] [blame] | 52 | assert(arrayBytes >= 0); |
| 53 | return fread(array, 1, (size_t)arrayBytes, fp); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 54 | } |
| 55 | |
| 56 | |
| 57 | void test_compress(FILE* outFp, FILE* inpFp) |
| 58 | { |
| 59 | LZ4_streamHC_t lz4Stream_body = { 0 }; |
| 60 | LZ4_streamHC_t* lz4Stream = &lz4Stream_body; |
| 61 | |
| 62 | static char inpBuf[RING_BUFFER_BYTES]; |
| 63 | int inpOffset = 0; |
| 64 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 65 | for(;;) { |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 66 | // Read random length ([1,MESSAGE_MAX_BYTES]) data to the ring buffer. |
| 67 | char* const inpPtr = &inpBuf[inpOffset]; |
| 68 | const int randomLength = (rand() % MESSAGE_MAX_BYTES) + 1; |
| 69 | const int inpBytes = (int) read_bin(inpFp, inpPtr, randomLength); |
| 70 | if (0 == inpBytes) break; |
| 71 | |
Yann Collet | 2fe3aa9 | 2016-11-21 15:00:50 -0800 | [diff] [blame] | 72 | #define CMPBUFSIZE (LZ4_COMPRESSBOUND(MESSAGE_MAX_BYTES)) |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 73 | { char cmpBuf[CMPBUFSIZE]; |
Yann Collet | 2fe3aa9 | 2016-11-21 15:00:50 -0800 | [diff] [blame] | 74 | const int cmpBytes = LZ4_compress_HC_continue(lz4Stream, inpPtr, cmpBuf, inpBytes, CMPBUFSIZE); |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 75 | |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 76 | if(cmpBytes <= 0) break; |
| 77 | write_int32(outFp, cmpBytes); |
| 78 | write_bin(outFp, cmpBuf, cmpBytes); |
| 79 | |
| 80 | inpOffset += inpBytes; |
| 81 | |
| 82 | // Wraparound the ringbuffer offset |
| 83 | if(inpOffset >= RING_BUFFER_BYTES - MESSAGE_MAX_BYTES) |
| 84 | inpOffset = 0; |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | write_int32(outFp, 0); |
| 89 | } |
| 90 | |
| 91 | |
| 92 | void test_decompress(FILE* outFp, FILE* inpFp) |
| 93 | { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 94 | static char decBuf[DEC_BUFFER_BYTES]; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 95 | int decOffset = 0; |
| 96 | LZ4_streamDecode_t lz4StreamDecode_body = { 0 }; |
| 97 | LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body; |
| 98 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 99 | for(;;) { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 100 | int cmpBytes = 0; |
Yann Collet | 2fe3aa9 | 2016-11-21 15:00:50 -0800 | [diff] [blame] | 101 | char cmpBuf[CMPBUFSIZE]; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 102 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 103 | { const size_t r0 = read_int32(inpFp, &cmpBytes); |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 104 | size_t r1; |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 105 | if(r0 != 1 || cmpBytes <= 0) |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 106 | break; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 107 | |
| 108 | r1 = read_bin(inpFp, cmpBuf, cmpBytes); |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 109 | if(r1 != (size_t) cmpBytes) |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 110 | break; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 111 | } |
| 112 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 113 | { char* const decPtr = &decBuf[decOffset]; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 114 | const int decBytes = LZ4_decompress_safe_continue( |
| 115 | lz4StreamDecode, cmpBuf, decPtr, cmpBytes, MESSAGE_MAX_BYTES); |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 116 | if(decBytes <= 0) |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 117 | break; |
| 118 | |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 119 | decOffset += decBytes; |
| 120 | write_bin(outFp, decPtr, decBytes); |
| 121 | |
| 122 | // Wraparound the ringbuffer offset |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 123 | if(decOffset >= DEC_BUFFER_BYTES - MESSAGE_MAX_BYTES) |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 124 | decOffset = 0; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 125 | } |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 130 | // Compare 2 files content |
| 131 | // return 0 if identical |
| 132 | // return ByteNb>0 if different |
| 133 | size_t compare(FILE* f0, FILE* f1) |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 134 | { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 135 | size_t result = 1; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 136 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 137 | for (;;) { |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 138 | char b0[65536]; |
| 139 | char b1[65536]; |
| 140 | const size_t r0 = fread(b0, 1, sizeof(b0), f0); |
| 141 | const size_t r1 = fread(b1, 1, sizeof(b1), f1); |
| 142 | |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 143 | if ((r0==0) && (r1==0)) return 0; // success |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 144 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 145 | if (r0 != r1) { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 146 | size_t smallest = r0; |
| 147 | if (r1<r0) smallest = r1; |
| 148 | result += smallest; |
Yann Collet | 8f844fe | 2014-12-13 11:49:55 +0100 | [diff] [blame] | 149 | break; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 150 | } |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 151 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 152 | if (memcmp(b0, b1, r0)) { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 153 | unsigned errorPos = 0; |
| 154 | while ((errorPos < r0) && (b0[errorPos]==b1[errorPos])) errorPos++; |
| 155 | result += errorPos; |
Yann Collet | 8f844fe | 2014-12-13 11:49:55 +0100 | [diff] [blame] | 156 | break; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 157 | } |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 158 | |
| 159 | result += sizeof(b0); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 160 | } |
| 161 | |
Yann Collet | 8f844fe | 2014-12-13 11:49:55 +0100 | [diff] [blame] | 162 | return result; |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 163 | } |
| 164 | |
| 165 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 166 | int main(int argc, const char** argv) |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 167 | { |
| 168 | char inpFilename[256] = { 0 }; |
| 169 | char lz4Filename[256] = { 0 }; |
| 170 | char decFilename[256] = { 0 }; |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 171 | unsigned fileID = 1; |
| 172 | unsigned pause = 0; |
| 173 | |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 174 | |
| 175 | if(argc < 2) { |
| 176 | printf("Please specify input filename\n"); |
| 177 | return 0; |
| 178 | } |
| 179 | |
Yann Collet | 3b917ef | 2019-06-28 20:55:47 -0700 | [diff] [blame] | 180 | if (!strcmp(argv[1], "-p")) { pause = 1; fileID = 2; } |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 181 | |
| 182 | snprintf(inpFilename, 256, "%s", argv[fileID]); |
| 183 | snprintf(lz4Filename, 256, "%s.lz4s-%d", argv[fileID], 9); |
| 184 | snprintf(decFilename, 256, "%s.lz4s-%d.dec", argv[fileID], 9); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 185 | |
Yann Collet | 3de7f00 | 2014-11-03 09:29:45 +0100 | [diff] [blame] | 186 | printf("input = [%s]\n", inpFilename); |
| 187 | printf("lz4 = [%s]\n", lz4Filename); |
| 188 | printf("decoded = [%s]\n", decFilename); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 189 | |
| 190 | // compress |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 191 | { FILE* const inpFp = fopen(inpFilename, "rb"); |
| 192 | FILE* const outFp = fopen(lz4Filename, "wb"); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 193 | |
| 194 | test_compress(outFp, inpFp); |
| 195 | |
| 196 | fclose(outFp); |
| 197 | fclose(inpFp); |
| 198 | } |
| 199 | |
| 200 | // decompress |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 201 | { FILE* const inpFp = fopen(lz4Filename, "rb"); |
| 202 | FILE* const outFp = fopen(decFilename, "wb"); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 203 | |
| 204 | test_decompress(outFp, inpFp); |
| 205 | |
| 206 | fclose(outFp); |
| 207 | fclose(inpFp); |
| 208 | } |
| 209 | |
| 210 | // verify |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 211 | { FILE* const inpFp = fopen(inpFilename, "rb"); |
| 212 | FILE* const decFp = fopen(decFilename, "rb"); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 213 | |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 214 | const size_t cmp = compare(inpFp, decFp); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 215 | if(0 == cmp) { |
| 216 | printf("Verify : OK\n"); |
| 217 | } else { |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 218 | printf("Verify : NG : error at pos %u\n", (unsigned)cmp-1); |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 219 | } |
| 220 | |
| 221 | fclose(decFp); |
| 222 | fclose(inpFp); |
| 223 | } |
| 224 | |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 225 | if (pause) { |
| 226 | int unused; |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 227 | printf("Press enter to continue ...\n"); |
Yann Collet | cc21115 | 2016-11-21 19:26:49 -0800 | [diff] [blame] | 228 | unused = getchar(); (void)unused; /* silence static analyzer */ |
Yann Collet | 2b421e9 | 2014-10-25 20:52:10 +0100 | [diff] [blame] | 229 | } |
| 230 | |
Yann Collet | e2c8411 | 2014-10-22 08:07:56 +0100 | [diff] [blame] | 231 | return 0; |
| 232 | } |