/*******************************************************************************
* Copyright 2017-2018 Intel Corporation.
*
* This software and the related documents are Intel copyrighted  materials,  and
* your use of  them is  governed by the  express license  under which  they were
* provided to you (License).  Unless the License provides otherwise, you may not
* use, modify, copy, publish, distribute,  disclose or transmit this software or
* the related documents without Intel's prior written permission.
*
* This software and the related documents  are provided as  is,  with no express
* or implied  warranties,  other  than those  that are  expressly stated  in the
* License.
*******************************************************************************/

/*
 The example below shows how to use the functions:
     ippsEncodeLZ4HashTableGetSize_8u
     ippsEncodeLZ4HashTableInit_8u
     ippsEncodeLZ4Dict_8u
     ippsDecodeLZ4_8u
*/

#include <stdio.h>
#include <string.h>

#include <ipp.h>

/* Next two defines are created to simplify code reading and understanding */
#define EXIT_MAIN       exitLine:                              /* Label for Exit */
#define check_sts(st)   if((st) != ippStsNoErr) goto exitLine; /* Go to Exit if Intel(R) Integrated Primitives (Intel(R) IPP) function returned status different from ippStsNoErr */

#define TEST_SIZE   (1024 * 4)
#define CHUNK_SIZE  (512)
#define RATIO       (TEST_SIZE / CHUNK_SIZE)

static int Verify(const Ipp8u* comprBuf, int comprLen, Ipp8u* decomprBuf,
                  int maxDecomprLen, const Ipp8u* srcBuf, int srcLen);

int main(void)
{
    Ipp8u       *srcBuf = NULL, *comprBuf = NULL, *decomprBuf = NULL, *hashTable = NULL;
    Ipp8u       *comprPtr, *dict;
    IppStatus   st = ippStsNoErr;
    int         hashSize = 0,
                maxChunkCompr = CHUNK_SIZE + CHUNK_SIZE / 255 + 16,
                comprLen = (maxChunkCompr) * RATIO, /* Extra bytes for uncompressible
                                                                                data */
                decomprLen = TEST_SIZE + 33;
    int         destLen;
    int         i;

    srcBuf      = ippsMalloc_8u(TEST_SIZE);
    decomprBuf  = ippsMalloc_8u(decomprLen); /* Spare bytes for "wild" (non-safe) 
                                                                        decompression */
    comprBuf    = ippsMalloc_8u(comprLen);
    /* Initialize source buffer */
    check_sts(  st = ippsVectorJaehne_8u(srcBuf, TEST_SIZE, IPP_MAX_8U) )
    for(i = 0; i < TEST_SIZE; i++)
        srcBuf[i] >>= 6;                    /* Decrease source data entropy */
    /* Allocate and initialize hash table */
    check_sts(  st = ippsEncodeLZ4HashTableGetSize_8u(&hashSize)    )
    hashTable   = ippsMalloc_8u(hashSize);
    check_sts(  st = ippsEncodeLZ4HashTableInit_8u(hashTable, TEST_SIZE)    )
    /* Compress source data using chunks without dictionary*/
    comprPtr = comprBuf;
    for(i = 0; i < RATIO; i++)
    {
        destLen = maxChunkCompr;
        check_sts(  st = ippsEncodeLZ4_8u(srcBuf + CHUNK_SIZE * i, CHUNK_SIZE, 
                                          comprPtr + 2, &destLen, hashTable)    )
        *comprPtr++   = destLen & 0xFF;
        *comprPtr++   = (destLen >> 8) & 0xFF;
        comprPtr += destLen;
    }
    /* Print compression result */
    comprLen = (int)(comprPtr - comprBuf);
    printf("Compression w/o dictionary: %d bytes compressed into %d bytes.", TEST_SIZE,
           comprLen);
    if(!Verify(comprBuf, comprLen, decomprBuf, decomprLen, srcBuf, TEST_SIZE))
    {
        printf("Verification failed!\n");
        st = ippStsErr;
        goto exitLine;
    }
    else
        printf("Verified.\n");

    /* Compress source data with dictionary*/
    comprPtr = comprBuf;
    dict = srcBuf;
    for(i = 0; i < RATIO; i++)
    {
        int     srcIdx, dictLen;
        destLen = maxChunkCompr;
        srcIdx = CHUNK_SIZE * i;
        dictLen = CHUNK_SIZE * i;           /* All previous chunks as dictionary */

        check_sts(  st = ippsEncodeLZ4Dict_8u(srcBuf + CHUNK_SIZE * i, srcIdx, CHUNK_SIZE,
                                              comprPtr + 2, &destLen, hashTable, dict, 
                                              dictLen)  )
        *comprPtr++   = destLen & 0xFF;
        *comprPtr++   = (destLen >> 8) & 0xFF;
        comprPtr += destLen;
    }
    /* Print compression result */
    comprLen = (int)(comprPtr - comprBuf);
    printf("Compression with dictionary: %d bytes compressed into %d bytes.", TEST_SIZE,
           comprLen);
    if(!Verify(comprBuf, comprLen, decomprBuf, decomprLen, srcBuf, TEST_SIZE))
    {
        printf("Verification failed!\n");
        st = ippStsErr;
        goto exitLine;
    }
    else
        printf("Verified.\n");

EXIT_MAIN
    if(st != ippStsNoErr)
        printf("Function status: %s\n", ippGetStatusString(st));
    ippsFree(srcBuf);
    ippsFree(comprBuf);
    ippsFree(decomprBuf);
    ippsFree(hashTable);
    return (int)st;
}

static int Verify(const Ipp8u* comprBuf, int comprLen, Ipp8u* decomprBuf,
                  int maxDecomprLen, const Ipp8u* srcBuf, int srcLen)
{
    const Ipp8u* locPtr = comprBuf;
    Ipp8u* decomprPtr = decomprBuf;
    IppStatus st;
    int decomprLen = 0;

    while((int)(locPtr - comprBuf) < comprLen)
    {
        int nextInLen;
        int nextOutLen;

        nextInLen = (*locPtr++);
        nextInLen +=  (*locPtr++) << 8;
        nextOutLen = maxDecomprLen - decomprLen;
        st = ippsDecodeLZ4_8u(locPtr, nextInLen, decomprPtr, &nextOutLen);
        if(st != ippStsNoErr) return 0;
        locPtr += nextInLen;
        decomprLen += nextOutLen;
        decomprPtr += nextOutLen;
    }
    if(decomprLen != srcLen || memcmp(decomprBuf, srcBuf, srcLen) != 0)
        return 0;
    return 1;
}