/* 
// 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:
     ippsEncodeLZ4HCHashTableGetSize_8u
     ippsEncodeLZ4HCHashTableInit_8u
     ippsEncodeLZ4HC_8u
     ippsDecodeLZ4_8u
*/

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

#include <ippdc.h>
#include <ipps.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)

static int checkDecompr(Ipp8u* decomprAddr, int decomprLen, Ipp8u* srcAddr, int srcLen);

int main(void)
{
    Ipp8u       *srcBuf = NULL, *comprBuf = NULL, *decomprBuf = NULL, *hashTable = NULL,
                *prevTable = NULL;
    Ipp8u       *tables[2];
    IppStatus   st = ippStsNoErr;
    int         hashSize = 0, prevSize = 0,
                srcLen = TEST_SIZE,
                comprLen = TEST_SIZE + TEST_SIZE / 255 + 16,    /* Extra bytes for 
                                                                    uncompressible data */
                decomprLen = TEST_SIZE + 33;                    /* Spare bytes for "wild"
                                                                (non-safe) decompression */
    int         i;

    srcBuf      = ippsMalloc_8u(TEST_SIZE);
    decomprBuf  = ippsMalloc_8u(decomprLen);
    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 */
    /* Init and allocate hash table */
    check_sts(      st = ippsEncodeLZ4HCHashTableGetSize_8u(&hashSize, &prevSize)       )
    hashTable   = ippsMalloc_8u(hashSize);
    prevTable   = ippsMalloc_8u(prevSize);
    tables[0]   = hashTable;
    tables[1]   = prevTable;
    check_sts(      st = ippsEncodeLZ4HCHashTableInit_8u(tables)      )
    /* Compress source data at level 1*/
    check_sts(      st = ippsEncodeLZ4HC_8u((const Ipp8u*)srcBuf, 0, &srcLen, comprBuf,
                                            &comprLen, tables, NULL, 0, 1)   )
    /* Print compression result */
    printf("Compression lev 1: %d bytes compressed into %d bytes\n", TEST_SIZE, comprLen);
    /* Decompression */
    decomprLen  = TEST_SIZE + 33;
    check_sts(  st = ippsDecodeLZ4_8u((const Ipp8u*)comprBuf, comprLen, decomprBuf, 
                                          &decomprLen)  )
    check_sts(  st = checkDecompr(decomprBuf, decomprLen, srcBuf, srcLen)   )

    /* Compress source data at level 6*/
    comprLen = TEST_SIZE + TEST_SIZE / 255 + 16;
    check_sts(  st = ippsEncodeLZ4HC_8u((const Ipp8u*)srcBuf, 0, &srcLen, comprBuf,
                                            &comprLen, tables, NULL, 0, 6)   )
    /* Print compression result */
    printf("Compression lev 6: %d bytes compressed into %d bytes\n", TEST_SIZE, comprLen);
    /* Decompression */
    decomprLen  = TEST_SIZE + 33;
    check_sts(  st = ippsDecodeLZ4_8u((const Ipp8u*)comprBuf, comprLen, decomprBuf,
                                          &decomprLen)      )
    check_sts(  st = checkDecompr(decomprBuf, decomprLen, srcBuf, srcLen)                             )

    /* Compress source data at level 9*/
    comprLen = TEST_SIZE + TEST_SIZE / 255 + 16;
    check_sts(  st = ippsEncodeLZ4HC_8u((const Ipp8u*)srcBuf, 0, &srcLen, comprBuf,
                                            &comprLen, tables, NULL, 0, 9)   )
    /* Print compression result */
    printf("Compression lev 9: %d bytes compressed into %d bytes\n", TEST_SIZE, comprLen);
    /* Decompression */
    decomprLen  = TEST_SIZE + 33;
    check_sts(  st = ippsDecodeLZ4_8u((const Ipp8u*)comprBuf, comprLen, decomprBuf,
                                          &decomprLen)  )
    st = checkDecompr(decomprBuf, decomprLen, srcBuf, srcLen);

EXIT_MAIN
    ippsFree(srcBuf);
    ippsFree(comprBuf);
    ippsFree(decomprBuf);
    ippsFree(hashTable);
    ippsFree(prevTable);
    return (int)st;
}
static IppStatus checkDecompr(Ipp8u* decomprAddr, int decomprLen, Ipp8u* srcAddr,
                              int srcLen)
{
    /* Check */
    if(decomprLen != srcLen || memcmp(decomprAddr, srcAddr, srcLen) != 0)
    {
            printf("Wrong decompression!\n");
            return ippStsErr;
    }
    printf("Decompressed OK into %d bytes\n", decomprLen);
    return ippStsNoErr;
}