/* 
// Copyright 2017 2018 Intel Corporation All Rights Reserved.
// 
// The source code, information and material ("Material") contained herein is
// owned by Intel Corporation or its suppliers or licensors, and title
// to such Material remains with Intel Corporation or its suppliers or
// licensors. The Material contains proprietary information of Intel
// or its suppliers and licensors. The Material is protected by worldwide
// copyright laws and treaty provisions. No part of the Material may be used,
// copied, reproduced, modified, published, uploaded, posted, transmitted,
// distributed or disclosed in any way without Intel's prior express written
// permission. No license under any patent, copyright or other intellectual
// property rights in the Material is granted to or conferred upon you,
// either expressly, by implication, inducement, estoppel or otherwise.
// Any license under such intellectual property rights must be express and
// approved by Intel in writing.
// 
// Unless otherwise agreed by Intel in writing,
// you may not remove or alter this notice or any other notice embedded in
// Materials by Intel or Intel's suppliers or licensors in any way.
*/

/*
 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;
}