1 // LZ4 API example : Dictionary Random Access 2 3 #if defined(_MSC_VER) && (_MSC_VER <= 1800) /* Visual Studio <= 2013 */ 4 # define _CRT_SECURE_NO_WARNINGS 5 # define snprintf sprintf_s 6 #endif 7 #include "lz4.h" 8 9 #include <stdio.h> 10 #include <stdint.h> 11 #include <stdlib.h> 12 #include <string.h> 13 14 #define MIN(x, y) (x) < (y) ? (x) : (y) 15 16 enum { 17 BLOCK_BYTES = 1024, /* 1 KiB of uncompressed data in a block */ 18 DICTIONARY_BYTES = 1024, /* Load a 1 KiB dictionary */ 19 MAX_BLOCKS = 1024 /* For simplicity of implementation */ 20 }; 21 22 /** 23 * Magic bytes for this test case. 24 * This is not a great magic number because it is a common word in ASCII. 25 * However, it is important to have some versioning system in your format. 26 */ 27 const char kTestMagic[] = { 'T', 'E', 'S', 'T' }; 28 29 30 void write_int(FILE* fp, int i) { 31 size_t written = fwrite(&i, sizeof(i), 1, fp); 32 if (written != 1) { exit(10); } 33 } 34 35 void write_bin(FILE* fp, const void* array, size_t arrayBytes) { 36 size_t written = fwrite(array, 1, arrayBytes, fp); 37 if (written != arrayBytes) { exit(11); } 38 } 39 40 void read_int(FILE* fp, int* i) { 41 size_t read = fread(i, sizeof(*i), 1, fp); 42 if (read != 1) { exit(12); } 43 } 44 45 size_t read_bin(FILE* fp, void* array, size_t arrayBytes) { 46 size_t read = fread(array, 1, arrayBytes, fp); 47 if (ferror(fp)) { exit(12); } 48 return read; 49 } 50 51 void seek_bin(FILE* fp, long offset, int origin) { 52 if (fseek(fp, offset, origin)) { exit(14); } 53 } 54 55 56 void test_compress(FILE* outFp, FILE* inpFp, void *dict, int dictSize) 57 { 58 LZ4_stream_t lz4Stream_body; 59 LZ4_stream_t* lz4Stream = &lz4Stream_body; 60 61 char inpBuf[BLOCK_BYTES]; 62 int offsets[MAX_BLOCKS]; 63 int *offsetsEnd = offsets; 64 65 66 LZ4_resetStream(lz4Stream); 67 68 /* Write header magic */ 69 write_bin(outFp, kTestMagic, sizeof(kTestMagic)); 70 71 *offsetsEnd++ = sizeof(kTestMagic); 72 /* Write compressed data blocks. Each block contains BLOCK_BYTES of plain 73 data except possibly the last. */ 74 for(;;) { 75 const int inpBytes = (int) read_bin(inpFp, inpBuf, BLOCK_BYTES); 76 if(0 == inpBytes) { 77 break; 78 } 79 80 /* Forget previously compressed data and load the dictionary */ 81 LZ4_loadDict(lz4Stream, dict, dictSize); 82 { 83 char cmpBuf[LZ4_COMPRESSBOUND(BLOCK_BYTES)]; 84 const int cmpBytes = LZ4_compress_fast_continue( 85 lz4Stream, inpBuf, cmpBuf, inpBytes, sizeof(cmpBuf), 1); 86 if(cmpBytes <= 0) { exit(1); } 87 write_bin(outFp, cmpBuf, (size_t)cmpBytes); 88 /* Keep track of the offsets */ 89 *offsetsEnd = *(offsetsEnd - 1) + cmpBytes; 90 ++offsetsEnd; 91 } 92 if (offsetsEnd - offsets > MAX_BLOCKS) { exit(2); } 93 } 94 /* Write the tailing jump table */ 95 { 96 int *ptr = offsets; 97 while (ptr != offsetsEnd) { 98 write_int(outFp, *ptr++); 99 } 100 write_int(outFp, offsetsEnd - offsets); 101 } 102 } 103 104 105 void test_decompress(FILE* outFp, FILE* inpFp, void *dict, int dictSize, int offset, int length) 106 { 107 LZ4_streamDecode_t lz4StreamDecode_body; 108 LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body; 109 110 /* The blocks [currentBlock, endBlock) contain the data we want */ 111 int currentBlock = offset / BLOCK_BYTES; 112 int endBlock = ((offset + length - 1) / BLOCK_BYTES) + 1; 113 114 char decBuf[BLOCK_BYTES]; 115 int offsets[MAX_BLOCKS]; 116 117 /* Special cases */ 118 if (length == 0) { return; } 119 120 /* Read the magic bytes */ 121 { 122 char magic[sizeof(kTestMagic)]; 123 size_t read = read_bin(inpFp, magic, sizeof(magic)); 124 if (read != sizeof(magic)) { exit(1); } 125 if (memcmp(kTestMagic, magic, sizeof(magic))) { exit(2); } 126 } 127 128 /* Read the offsets tail */ 129 { 130 int numOffsets; 131 int block; 132 int *offsetsPtr = offsets; 133 seek_bin(inpFp, -4, SEEK_END); 134 read_int(inpFp, &numOffsets); 135 if (numOffsets <= endBlock) { exit(3); } 136 seek_bin(inpFp, -4 * (numOffsets + 1), SEEK_END); 137 for (block = 0; block <= endBlock; ++block) { 138 read_int(inpFp, offsetsPtr++); 139 } 140 } 141 /* Seek to the first block to read */ 142 seek_bin(inpFp, offsets[currentBlock], SEEK_SET); 143 offset = offset % BLOCK_BYTES; 144 145 /* Start decoding */ 146 for(; currentBlock < endBlock; ++currentBlock) { 147 char cmpBuf[LZ4_COMPRESSBOUND(BLOCK_BYTES)]; 148 /* The difference in offsets is the size of the block */ 149 int cmpBytes = offsets[currentBlock + 1] - offsets[currentBlock]; 150 { 151 const size_t read = read_bin(inpFp, cmpBuf, (size_t)cmpBytes); 152 if(read != (size_t)cmpBytes) { exit(4); } 153 } 154 155 /* Load the dictionary */ 156 LZ4_setStreamDecode(lz4StreamDecode, dict, dictSize); 157 { 158 const int decBytes = LZ4_decompress_safe_continue( 159 lz4StreamDecode, cmpBuf, decBuf, cmpBytes, BLOCK_BYTES); 160 if(decBytes <= 0) { exit(5); } 161 { 162 /* Write out the part of the data we care about */ 163 int blockLength = MIN(length, (decBytes - offset)); 164 write_bin(outFp, decBuf + offset, (size_t)blockLength); 165 offset = 0; 166 length -= blockLength; 167 } 168 } 169 } 170 } 171 172 173 int compare(FILE* fp0, FILE* fp1, int length) 174 { 175 int result = 0; 176 177 while(0 == result) { 178 char b0[4096]; 179 char b1[4096]; 180 const size_t r0 = read_bin(fp0, b0, MIN(length, (int)sizeof(b0))); 181 const size_t r1 = read_bin(fp1, b1, MIN(length, (int)sizeof(b1))); 182 183 result = (int) r0 - (int) r1; 184 185 if(0 == r0 || 0 == r1) { 186 break; 187 } 188 if(0 == result) { 189 result = memcmp(b0, b1, r0); 190 } 191 length -= r0; 192 } 193 194 return result; 195 } 196 197 198 int main(int argc, char* argv[]) 199 { 200 char inpFilename[256] = { 0 }; 201 char lz4Filename[256] = { 0 }; 202 char decFilename[256] = { 0 }; 203 char dictFilename[256] = { 0 }; 204 int offset; 205 int length; 206 char dict[DICTIONARY_BYTES]; 207 int dictSize; 208 209 if(argc < 5) { 210 printf("Usage: %s input dictionary offset length", argv[0]); 211 return 0; 212 } 213 214 snprintf(inpFilename, 256, "%s", argv[1]); 215 snprintf(lz4Filename, 256, "%s.lz4s-%d", argv[1], BLOCK_BYTES); 216 snprintf(decFilename, 256, "%s.lz4s-%d.dec", argv[1], BLOCK_BYTES); 217 snprintf(dictFilename, 256, "%s", argv[2]); 218 offset = atoi(argv[3]); 219 length = atoi(argv[4]); 220 221 printf("inp = [%s]\n", inpFilename); 222 printf("lz4 = [%s]\n", lz4Filename); 223 printf("dec = [%s]\n", decFilename); 224 printf("dict = [%s]\n", dictFilename); 225 printf("offset = [%d]\n", offset); 226 printf("length = [%d]\n", length); 227 228 /* Load dictionary */ 229 { 230 FILE* dictFp = fopen(dictFilename, "rb"); 231 dictSize = (int)read_bin(dictFp, dict, DICTIONARY_BYTES); 232 fclose(dictFp); 233 } 234 235 /* compress */ 236 { 237 FILE* inpFp = fopen(inpFilename, "rb"); 238 FILE* outFp = fopen(lz4Filename, "wb"); 239 240 printf("compress : %s -> %s\n", inpFilename, lz4Filename); 241 test_compress(outFp, inpFp, dict, dictSize); 242 printf("compress : done\n"); 243 244 fclose(outFp); 245 fclose(inpFp); 246 } 247 248 /* decompress */ 249 { 250 FILE* inpFp = fopen(lz4Filename, "rb"); 251 FILE* outFp = fopen(decFilename, "wb"); 252 253 printf("decompress : %s -> %s\n", lz4Filename, decFilename); 254 test_decompress(outFp, inpFp, dict, DICTIONARY_BYTES, offset, length); 255 printf("decompress : done\n"); 256 257 fclose(outFp); 258 fclose(inpFp); 259 } 260 261 /* verify */ 262 { 263 FILE* inpFp = fopen(inpFilename, "rb"); 264 FILE* decFp = fopen(decFilename, "rb"); 265 seek_bin(inpFp, offset, SEEK_SET); 266 267 printf("verify : %s <-> %s\n", inpFilename, decFilename); 268 const int cmp = compare(inpFp, decFp, length); 269 if(0 == cmp) { 270 printf("verify : OK\n"); 271 } else { 272 printf("verify : NG\n"); 273 } 274 275 fclose(decFp); 276 fclose(inpFp); 277 } 278 279 return 0; 280 } 281