127b27ec6Sopenharmony_ci// LZ4 API example : Dictionary Random Access
227b27ec6Sopenharmony_ci
327b27ec6Sopenharmony_ci#if defined(_MSC_VER) && (_MSC_VER <= 1800)  /* Visual Studio <= 2013 */
427b27ec6Sopenharmony_ci#  define _CRT_SECURE_NO_WARNINGS
527b27ec6Sopenharmony_ci#  define snprintf sprintf_s
627b27ec6Sopenharmony_ci#endif
727b27ec6Sopenharmony_ci#include "lz4.h"
827b27ec6Sopenharmony_ci
927b27ec6Sopenharmony_ci#include <stdio.h>
1027b27ec6Sopenharmony_ci#include <stdint.h>
1127b27ec6Sopenharmony_ci#include <stdlib.h>
1227b27ec6Sopenharmony_ci#include <string.h>
1327b27ec6Sopenharmony_ci
1427b27ec6Sopenharmony_ci#define MIN(x, y)  ((x) < (y) ? (x) : (y))
1527b27ec6Sopenharmony_ci
1627b27ec6Sopenharmony_cienum {
1727b27ec6Sopenharmony_ci    BLOCK_BYTES = 1024,  /* 1 KiB of uncompressed data in a block */
1827b27ec6Sopenharmony_ci    DICTIONARY_BYTES = 1024, /* Load a 1 KiB dictionary */
1927b27ec6Sopenharmony_ci    MAX_BLOCKS = 1024 /* For simplicity of implementation */
2027b27ec6Sopenharmony_ci};
2127b27ec6Sopenharmony_ci
2227b27ec6Sopenharmony_ci/**
2327b27ec6Sopenharmony_ci * Magic bytes for this test case.
2427b27ec6Sopenharmony_ci * This is not a great magic number because it is a common word in ASCII.
2527b27ec6Sopenharmony_ci * However, it is important to have some versioning system in your format.
2627b27ec6Sopenharmony_ci */
2727b27ec6Sopenharmony_ciconst char kTestMagic[] = { 'T', 'E', 'S', 'T' };
2827b27ec6Sopenharmony_ci
2927b27ec6Sopenharmony_ci
3027b27ec6Sopenharmony_civoid write_int(FILE* fp, int i) {
3127b27ec6Sopenharmony_ci    size_t written = fwrite(&i, sizeof(i), 1, fp);
3227b27ec6Sopenharmony_ci    if (written != 1) { exit(10); }
3327b27ec6Sopenharmony_ci}
3427b27ec6Sopenharmony_ci
3527b27ec6Sopenharmony_civoid write_bin(FILE* fp, const void* array, size_t arrayBytes) {
3627b27ec6Sopenharmony_ci    size_t written = fwrite(array, 1, arrayBytes, fp);
3727b27ec6Sopenharmony_ci    if (written != arrayBytes) { exit(11); }
3827b27ec6Sopenharmony_ci}
3927b27ec6Sopenharmony_ci
4027b27ec6Sopenharmony_civoid read_int(FILE* fp, int* i) {
4127b27ec6Sopenharmony_ci    size_t read = fread(i, sizeof(*i), 1, fp);
4227b27ec6Sopenharmony_ci    if (read != 1) { exit(12); }
4327b27ec6Sopenharmony_ci}
4427b27ec6Sopenharmony_ci
4527b27ec6Sopenharmony_cisize_t read_bin(FILE* fp, void* array, size_t arrayBytes) {
4627b27ec6Sopenharmony_ci    size_t read = fread(array, 1, arrayBytes, fp);
4727b27ec6Sopenharmony_ci    if (ferror(fp)) { exit(12); }
4827b27ec6Sopenharmony_ci    return read;
4927b27ec6Sopenharmony_ci}
5027b27ec6Sopenharmony_ci
5127b27ec6Sopenharmony_civoid seek_bin(FILE* fp, long offset, int origin) {
5227b27ec6Sopenharmony_ci    if (fseek(fp, offset, origin)) { exit(14); }
5327b27ec6Sopenharmony_ci}
5427b27ec6Sopenharmony_ci
5527b27ec6Sopenharmony_ci
5627b27ec6Sopenharmony_civoid test_compress(FILE* outFp, FILE* inpFp, void *dict, int dictSize)
5727b27ec6Sopenharmony_ci{
5827b27ec6Sopenharmony_ci    LZ4_stream_t lz4Stream_body;
5927b27ec6Sopenharmony_ci    LZ4_stream_t* lz4Stream = &lz4Stream_body;
6027b27ec6Sopenharmony_ci
6127b27ec6Sopenharmony_ci    char inpBuf[BLOCK_BYTES];
6227b27ec6Sopenharmony_ci    int offsets[MAX_BLOCKS];
6327b27ec6Sopenharmony_ci    int *offsetsEnd = offsets;
6427b27ec6Sopenharmony_ci
6527b27ec6Sopenharmony_ci
6627b27ec6Sopenharmony_ci    LZ4_initStream(lz4Stream, sizeof(*lz4Stream));
6727b27ec6Sopenharmony_ci
6827b27ec6Sopenharmony_ci    /* Write header magic */
6927b27ec6Sopenharmony_ci    write_bin(outFp, kTestMagic, sizeof(kTestMagic));
7027b27ec6Sopenharmony_ci
7127b27ec6Sopenharmony_ci    *offsetsEnd++ = sizeof(kTestMagic);
7227b27ec6Sopenharmony_ci    /* Write compressed data blocks.  Each block contains BLOCK_BYTES of plain
7327b27ec6Sopenharmony_ci       data except possibly the last. */
7427b27ec6Sopenharmony_ci    for(;;) {
7527b27ec6Sopenharmony_ci        const int inpBytes = (int) read_bin(inpFp, inpBuf, BLOCK_BYTES);
7627b27ec6Sopenharmony_ci        if(0 == inpBytes) {
7727b27ec6Sopenharmony_ci            break;
7827b27ec6Sopenharmony_ci        }
7927b27ec6Sopenharmony_ci
8027b27ec6Sopenharmony_ci        /* Forget previously compressed data and load the dictionary */
8127b27ec6Sopenharmony_ci        LZ4_loadDict(lz4Stream, (const char*) dict, dictSize);
8227b27ec6Sopenharmony_ci        {
8327b27ec6Sopenharmony_ci            char cmpBuf[LZ4_COMPRESSBOUND(BLOCK_BYTES)];
8427b27ec6Sopenharmony_ci            const int cmpBytes = LZ4_compress_fast_continue(
8527b27ec6Sopenharmony_ci                lz4Stream, inpBuf, cmpBuf, inpBytes, sizeof(cmpBuf), 1);
8627b27ec6Sopenharmony_ci            if(cmpBytes <= 0) { exit(1); }
8727b27ec6Sopenharmony_ci            write_bin(outFp, cmpBuf, (size_t)cmpBytes);
8827b27ec6Sopenharmony_ci            /* Keep track of the offsets */
8927b27ec6Sopenharmony_ci            *offsetsEnd = *(offsetsEnd - 1) + cmpBytes;
9027b27ec6Sopenharmony_ci            ++offsetsEnd;
9127b27ec6Sopenharmony_ci        }
9227b27ec6Sopenharmony_ci        if (offsetsEnd - offsets > MAX_BLOCKS) { exit(2); }
9327b27ec6Sopenharmony_ci    }
9427b27ec6Sopenharmony_ci    /* Write the tailing jump table */
9527b27ec6Sopenharmony_ci    {
9627b27ec6Sopenharmony_ci        int *ptr = offsets;
9727b27ec6Sopenharmony_ci        while (ptr != offsetsEnd) {
9827b27ec6Sopenharmony_ci            write_int(outFp, *ptr++);
9927b27ec6Sopenharmony_ci        }
10027b27ec6Sopenharmony_ci        write_int(outFp, (int) (offsetsEnd - offsets));
10127b27ec6Sopenharmony_ci    }
10227b27ec6Sopenharmony_ci}
10327b27ec6Sopenharmony_ci
10427b27ec6Sopenharmony_ci
10527b27ec6Sopenharmony_civoid test_decompress(FILE* outFp, FILE* inpFp, void *dict, int dictSize, int offset, int length)
10627b27ec6Sopenharmony_ci{
10727b27ec6Sopenharmony_ci    LZ4_streamDecode_t lz4StreamDecode_body;
10827b27ec6Sopenharmony_ci    LZ4_streamDecode_t* lz4StreamDecode = &lz4StreamDecode_body;
10927b27ec6Sopenharmony_ci
11027b27ec6Sopenharmony_ci    /* The blocks [currentBlock, endBlock) contain the data we want */
11127b27ec6Sopenharmony_ci    int currentBlock = offset / BLOCK_BYTES;
11227b27ec6Sopenharmony_ci    int endBlock = ((offset + length - 1) / BLOCK_BYTES) + 1;
11327b27ec6Sopenharmony_ci
11427b27ec6Sopenharmony_ci    char decBuf[BLOCK_BYTES];
11527b27ec6Sopenharmony_ci    int offsets[MAX_BLOCKS];
11627b27ec6Sopenharmony_ci
11727b27ec6Sopenharmony_ci    /* Special cases */
11827b27ec6Sopenharmony_ci    if (length == 0) { return; }
11927b27ec6Sopenharmony_ci
12027b27ec6Sopenharmony_ci    /* Read the magic bytes */
12127b27ec6Sopenharmony_ci    {
12227b27ec6Sopenharmony_ci        char magic[sizeof(kTestMagic)];
12327b27ec6Sopenharmony_ci        size_t read = read_bin(inpFp, magic, sizeof(magic));
12427b27ec6Sopenharmony_ci        if (read != sizeof(magic)) { exit(1); }
12527b27ec6Sopenharmony_ci        if (memcmp(kTestMagic, magic, sizeof(magic))) { exit(2); }
12627b27ec6Sopenharmony_ci    }
12727b27ec6Sopenharmony_ci
12827b27ec6Sopenharmony_ci    /* Read the offsets tail */
12927b27ec6Sopenharmony_ci    {
13027b27ec6Sopenharmony_ci        int numOffsets;
13127b27ec6Sopenharmony_ci        int block;
13227b27ec6Sopenharmony_ci        int *offsetsPtr = offsets;
13327b27ec6Sopenharmony_ci        seek_bin(inpFp, -4, SEEK_END);
13427b27ec6Sopenharmony_ci        read_int(inpFp, &numOffsets);
13527b27ec6Sopenharmony_ci        if (numOffsets <= endBlock) { exit(3); }
13627b27ec6Sopenharmony_ci        seek_bin(inpFp, -4 * (numOffsets + 1), SEEK_END);
13727b27ec6Sopenharmony_ci        for (block = 0; block <= endBlock; ++block) {
13827b27ec6Sopenharmony_ci            read_int(inpFp, offsetsPtr++);
13927b27ec6Sopenharmony_ci        }
14027b27ec6Sopenharmony_ci    }
14127b27ec6Sopenharmony_ci    /* Seek to the first block to read */
14227b27ec6Sopenharmony_ci    seek_bin(inpFp, offsets[currentBlock], SEEK_SET);
14327b27ec6Sopenharmony_ci    offset = offset % BLOCK_BYTES;
14427b27ec6Sopenharmony_ci
14527b27ec6Sopenharmony_ci    /* Start decoding */
14627b27ec6Sopenharmony_ci    for(; currentBlock < endBlock; ++currentBlock) {
14727b27ec6Sopenharmony_ci        char cmpBuf[LZ4_COMPRESSBOUND(BLOCK_BYTES)];
14827b27ec6Sopenharmony_ci        /* The difference in offsets is the size of the block */
14927b27ec6Sopenharmony_ci        int  cmpBytes = offsets[currentBlock + 1] - offsets[currentBlock];
15027b27ec6Sopenharmony_ci        {
15127b27ec6Sopenharmony_ci            const size_t read = read_bin(inpFp, cmpBuf, (size_t)cmpBytes);
15227b27ec6Sopenharmony_ci            if(read != (size_t)cmpBytes) { exit(4); }
15327b27ec6Sopenharmony_ci        }
15427b27ec6Sopenharmony_ci
15527b27ec6Sopenharmony_ci        /* Load the dictionary */
15627b27ec6Sopenharmony_ci        LZ4_setStreamDecode(lz4StreamDecode, (const char*) dict, dictSize);
15727b27ec6Sopenharmony_ci        {
15827b27ec6Sopenharmony_ci            const int decBytes = LZ4_decompress_safe_continue(
15927b27ec6Sopenharmony_ci                lz4StreamDecode, cmpBuf, decBuf, cmpBytes, BLOCK_BYTES);
16027b27ec6Sopenharmony_ci            if(decBytes <= 0) { exit(5); }
16127b27ec6Sopenharmony_ci            {
16227b27ec6Sopenharmony_ci                /* Write out the part of the data we care about */
16327b27ec6Sopenharmony_ci                int blockLength = MIN(length, (decBytes - offset));
16427b27ec6Sopenharmony_ci                write_bin(outFp, decBuf + offset, (size_t)blockLength);
16527b27ec6Sopenharmony_ci                offset = 0;
16627b27ec6Sopenharmony_ci                length -= blockLength;
16727b27ec6Sopenharmony_ci            }
16827b27ec6Sopenharmony_ci        }
16927b27ec6Sopenharmony_ci    }
17027b27ec6Sopenharmony_ci}
17127b27ec6Sopenharmony_ci
17227b27ec6Sopenharmony_ci
17327b27ec6Sopenharmony_ciint compare(FILE* fp0, FILE* fp1, int length)
17427b27ec6Sopenharmony_ci{
17527b27ec6Sopenharmony_ci    int result = 0;
17627b27ec6Sopenharmony_ci
17727b27ec6Sopenharmony_ci    while(0 == result) {
17827b27ec6Sopenharmony_ci        char b0[4096];
17927b27ec6Sopenharmony_ci        char b1[4096];
18027b27ec6Sopenharmony_ci        const size_t r0 = read_bin(fp0, b0, MIN(length, (int)sizeof(b0)));
18127b27ec6Sopenharmony_ci        const size_t r1 = read_bin(fp1, b1, MIN(length, (int)sizeof(b1)));
18227b27ec6Sopenharmony_ci
18327b27ec6Sopenharmony_ci        result = (int) r0 - (int) r1;
18427b27ec6Sopenharmony_ci
18527b27ec6Sopenharmony_ci        if(0 == r0 || 0 == r1) {
18627b27ec6Sopenharmony_ci            break;
18727b27ec6Sopenharmony_ci        }
18827b27ec6Sopenharmony_ci        if(0 == result) {
18927b27ec6Sopenharmony_ci            result = memcmp(b0, b1, r0);
19027b27ec6Sopenharmony_ci        }
19127b27ec6Sopenharmony_ci        length -= r0;
19227b27ec6Sopenharmony_ci    }
19327b27ec6Sopenharmony_ci
19427b27ec6Sopenharmony_ci    return result;
19527b27ec6Sopenharmony_ci}
19627b27ec6Sopenharmony_ci
19727b27ec6Sopenharmony_ci
19827b27ec6Sopenharmony_ciint main(int argc, char* argv[])
19927b27ec6Sopenharmony_ci{
20027b27ec6Sopenharmony_ci    char inpFilename[256] = { 0 };
20127b27ec6Sopenharmony_ci    char lz4Filename[256] = { 0 };
20227b27ec6Sopenharmony_ci    char decFilename[256] = { 0 };
20327b27ec6Sopenharmony_ci    char dictFilename[256] = { 0 };
20427b27ec6Sopenharmony_ci    int offset;
20527b27ec6Sopenharmony_ci    int length;
20627b27ec6Sopenharmony_ci    char dict[DICTIONARY_BYTES];
20727b27ec6Sopenharmony_ci    int dictSize;
20827b27ec6Sopenharmony_ci
20927b27ec6Sopenharmony_ci    if(argc < 5) {
21027b27ec6Sopenharmony_ci        printf("Usage: %s input dictionary offset length", argv[0]);
21127b27ec6Sopenharmony_ci        return 0;
21227b27ec6Sopenharmony_ci    }
21327b27ec6Sopenharmony_ci
21427b27ec6Sopenharmony_ci    snprintf(inpFilename, 256, "%s", argv[1]);
21527b27ec6Sopenharmony_ci    snprintf(lz4Filename, 256, "%s.lz4s-%d", argv[1], BLOCK_BYTES);
21627b27ec6Sopenharmony_ci    snprintf(decFilename, 256, "%s.lz4s-%d.dec", argv[1], BLOCK_BYTES);
21727b27ec6Sopenharmony_ci    snprintf(dictFilename, 256, "%s", argv[2]);
21827b27ec6Sopenharmony_ci    offset = atoi(argv[3]);
21927b27ec6Sopenharmony_ci    length = atoi(argv[4]);
22027b27ec6Sopenharmony_ci
22127b27ec6Sopenharmony_ci    printf("inp    = [%s]\n", inpFilename);
22227b27ec6Sopenharmony_ci    printf("lz4    = [%s]\n", lz4Filename);
22327b27ec6Sopenharmony_ci    printf("dec    = [%s]\n", decFilename);
22427b27ec6Sopenharmony_ci    printf("dict   = [%s]\n", dictFilename);
22527b27ec6Sopenharmony_ci    printf("offset = [%d]\n", offset);
22627b27ec6Sopenharmony_ci    printf("length = [%d]\n", length);
22727b27ec6Sopenharmony_ci
22827b27ec6Sopenharmony_ci    /* Load dictionary */
22927b27ec6Sopenharmony_ci    {
23027b27ec6Sopenharmony_ci        FILE* dictFp = fopen(dictFilename, "rb");
23127b27ec6Sopenharmony_ci        dictSize = (int)read_bin(dictFp, dict, DICTIONARY_BYTES);
23227b27ec6Sopenharmony_ci        fclose(dictFp);
23327b27ec6Sopenharmony_ci    }
23427b27ec6Sopenharmony_ci
23527b27ec6Sopenharmony_ci    /* compress */
23627b27ec6Sopenharmony_ci    {
23727b27ec6Sopenharmony_ci        FILE* inpFp = fopen(inpFilename, "rb");
23827b27ec6Sopenharmony_ci        FILE* outFp = fopen(lz4Filename, "wb");
23927b27ec6Sopenharmony_ci
24027b27ec6Sopenharmony_ci        printf("compress : %s -> %s\n", inpFilename, lz4Filename);
24127b27ec6Sopenharmony_ci        test_compress(outFp, inpFp, dict, dictSize);
24227b27ec6Sopenharmony_ci        printf("compress : done\n");
24327b27ec6Sopenharmony_ci
24427b27ec6Sopenharmony_ci        fclose(outFp);
24527b27ec6Sopenharmony_ci        fclose(inpFp);
24627b27ec6Sopenharmony_ci    }
24727b27ec6Sopenharmony_ci
24827b27ec6Sopenharmony_ci    /* decompress */
24927b27ec6Sopenharmony_ci    {
25027b27ec6Sopenharmony_ci        FILE* inpFp = fopen(lz4Filename, "rb");
25127b27ec6Sopenharmony_ci        FILE* outFp = fopen(decFilename, "wb");
25227b27ec6Sopenharmony_ci
25327b27ec6Sopenharmony_ci        printf("decompress : %s -> %s\n", lz4Filename, decFilename);
25427b27ec6Sopenharmony_ci        test_decompress(outFp, inpFp, dict, DICTIONARY_BYTES, offset, length);
25527b27ec6Sopenharmony_ci        printf("decompress : done\n");
25627b27ec6Sopenharmony_ci
25727b27ec6Sopenharmony_ci        fclose(outFp);
25827b27ec6Sopenharmony_ci        fclose(inpFp);
25927b27ec6Sopenharmony_ci    }
26027b27ec6Sopenharmony_ci
26127b27ec6Sopenharmony_ci    /* verify */
26227b27ec6Sopenharmony_ci    {
26327b27ec6Sopenharmony_ci        FILE* inpFp = fopen(inpFilename, "rb");
26427b27ec6Sopenharmony_ci        FILE* decFp = fopen(decFilename, "rb");
26527b27ec6Sopenharmony_ci        seek_bin(inpFp, offset, SEEK_SET);
26627b27ec6Sopenharmony_ci
26727b27ec6Sopenharmony_ci        printf("verify : %s <-> %s\n", inpFilename, decFilename);
26827b27ec6Sopenharmony_ci        const int cmp = compare(inpFp, decFp, length);
26927b27ec6Sopenharmony_ci        if(0 == cmp) {
27027b27ec6Sopenharmony_ci            printf("verify : OK\n");
27127b27ec6Sopenharmony_ci        } else {
27227b27ec6Sopenharmony_ci            printf("verify : NG\n");
27327b27ec6Sopenharmony_ci        }
27427b27ec6Sopenharmony_ci
27527b27ec6Sopenharmony_ci        fclose(decFp);
27627b27ec6Sopenharmony_ci        fclose(inpFp);
27727b27ec6Sopenharmony_ci    }
27827b27ec6Sopenharmony_ci
27927b27ec6Sopenharmony_ci    return 0;
28027b27ec6Sopenharmony_ci}
281