/*

 * Copyright (c) 2021 Huawei Device Co., Ltd.

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



#include "image_diff.h"

#include <iostream>

#include <vector>

#include "diffpatch.h"



using namespace Hpackage;



namespace UpdatePatch {

#define GET_REAL_DATA_LEN(info) (info) ->packedSize + (info)->dataOffset - (info)->headerOffset

constexpr int32_t LZ4F_MAX_BLOCKID = 7;

constexpr int32_t ZIP_MAX_LEVEL = 9;

constexpr int32_t MAX_NEW_LENGTH = 1 << 20;



template<class DataType>

static void WriteToFile(std::ofstream &patchFile, DataType data, size_t dataSize)

{

    patchFile.write(reinterpret_cast<const char*>(&data), dataSize);

}



static size_t GetHeaderSize(const ImageBlock &block)

{

    switch (block.type) {

        case BLOCK_NORMAL:

            return sizeof(uint32_t) + PATCH_NORMAL_MIN_HEADER_LEN;

        case BLOCK_DEFLATE:

            return sizeof(uint32_t) + PATCH_DEFLATE_MIN_HEADER_LEN;

        case BLOCK_RAW:

            return sizeof(uint32_t) + sizeof(uint32_t) + block.newInfo.length;

        case BLOCK_LZ4:

            return sizeof(uint32_t) + PATCH_LZ4_MIN_HEADER_LEN;

        default:

            return 0;

    }

}



int32_t ImageDiff::MakePatch(const std::string &patchName)

{

    PATCH_LOGI("ImageDiff::MakePatch %s limit_:%d", patchName.c_str(), limit_);

    if (newParser_ == nullptr || oldParser_ == nullptr) {

        PATCH_LOGE("Invalid parser");

        return -1;

    }

    BlockBuffer newBuffer;

    BlockBuffer olduffer;

    int32_t ret = newParser_->GetPkgBuffer(newBuffer);

    int32_t ret1 = oldParser_->GetPkgBuffer(olduffer);

    if (ret != 0 || ret1 != 0) {

        PATCH_LOGE("Failed to get pkgbuffer");

        return -1;

    }



    PATCH_LOGI("ImageDiff::MakePatch newBuffer %zu %zu ", newBuffer.length, olduffer.length);



    if (limit_ == 0 || newBuffer.length <= limit_) {

        ImageBlock block = {

            BLOCK_NORMAL,

            { newBuffer.buffer, 0, newBuffer.length },

            { olduffer.buffer, 0, olduffer.length },

        };

        updateBlocks_.push_back(std::move(block));

    } else {

        PatchBuffer oldInfo = { olduffer.buffer, 0, olduffer.length };

        PatchBuffer newInfo = { newBuffer.buffer, 0, newBuffer.length };

        ret = SplitImage(oldInfo, newInfo);

        if (ret != 0) {

            PATCH_LOGE("Failed to split imgage");

            return ret;

        }

    }

    return DiffImage(patchName);

}



int32_t ImageDiff::SplitImage(const PatchBuffer &oldInfo, const PatchBuffer &newInfo)

{

    size_t blockCount = (newInfo.length % limit_ == 0) ? (newInfo.length / limit_) : (newInfo.length / limit_ + 1);

    size_t oldBlockSize = oldInfo.length / blockCount;

    size_t newBlockSize = newInfo.length / blockCount;

    int32_t type = (oldInfo.length == 0) ? BLOCK_RAW : BLOCK_NORMAL;

    size_t i = 0;

    while (i < blockCount - 1) {

        ImageBlock block = {

            type,

            { newInfo.buffer, newInfo.start + newBlockSize * i, newBlockSize },

            { oldInfo.buffer, oldInfo.start + oldBlockSize * i, oldBlockSize },

        };

        updateBlocks_.push_back(std::move(block));

        i++;

    }

    ImageBlock block = {

        type,

        { newInfo.buffer, newInfo.start + newBlockSize * i, newInfo.length - (newInfo.start + newBlockSize * i) },

        { oldInfo.buffer, oldInfo.start + oldBlockSize * i, oldInfo.length - (oldInfo.start + oldBlockSize * i) },

    };

    updateBlocks_.push_back(std::move(block));

    return 0;

}



int32_t ImageDiff::WriteHeader(std::ofstream &patchFile,

    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const

{

    int32_t ret = 0;

    switch (block.type) {

        case BLOCK_NORMAL: {

            size_t patchSize = 0;

            BlockBuffer newInfo = { block.newInfo.buffer + block.newInfo.start, block.newInfo.length };

            BlockBuffer oldInfo = { block.oldInfo.buffer + block.oldInfo.start, block.oldInfo.length };

            ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);

            if (ret != 0) {

                PATCH_LOGE("Failed to make block patch");

                return -1;

            }

            PATCH_LOGI("WriteHeader BLOCK_NORMAL patchOffset %zu oldInfo %ld %ld newInfo:%zu %zu patch %zu %zu",

                static_cast<size_t>(patchFile.tellp()),

                block.oldInfo.start, block.oldInfo.length, block.newInfo.start, block.newInfo.length,

                dataOffset, patchSize);

            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));

            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));

            WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));

            dataOffset += patchSize;

            break;

        }

        case BLOCK_RAW: {

            PATCH_LOGI("WriteHeader BLOCK_ROW patchOffset %zu dataOffset:%zu newInfo:%zu",

                static_cast<size_t>(patchFile.tellp()), dataOffset, block.newInfo.length);

            WriteToFile<int32_t>(patchFile, static_cast<int32_t>(block.newInfo.length), sizeof(int32_t));

            patchFile.write(reinterpret_cast<const char*>(block.newInfo.buffer + block.newInfo.start),

                block.newInfo.length);

            BlockBuffer rawData = { block.newInfo.buffer + block.newInfo.start, block.newInfo.length };

            PATCH_LOGI("WriteHeader BLOCK_ROW hash %zu %s",

                block.newInfo.length, GeneraterBufferHash(rawData).c_str());

            break;

        }

        default:

            break;

    }

    return ret;

}



int32_t ImageDiff::MakeBlockPatch(ImageBlock &block, std::fstream &blockPatchFile,

    const BlockBuffer &newInfo, const BlockBuffer &oldInfo, size_t &patchSize) const

{

    if (!usePatchFile_) {

        std::vector<uint8_t> patchData;

        int32_t ret = BlocksDiff::MakePatch(newInfo, oldInfo, patchData, 0, patchSize);

        if (ret != 0) {

            PATCH_LOGE("Failed to make block patch");

            return -1;

        }

        BlockBuffer patchBuffer = {patchData.data(), patchSize};

        PATCH_DEBUG("MakeBlockPatch hash %zu %s", patchSize, GeneraterBufferHash(patchBuffer).c_str());

        block.patchData = std::move(patchData);

    } else {

        int32_t ret = BlocksDiff::MakePatch(newInfo, oldInfo, blockPatchFile, patchSize);

        if (ret != 0) {

            PATCH_LOGE("Failed to make block patch");

            return -1;

        }

        PATCH_LOGI("MakeBlockPatch patch %zu patch %zu",

            patchSize, static_cast<size_t>(blockPatchFile.tellp()));

    }

    return 0;

}



int32_t ImageDiff::WritePatch(std::ofstream &patchFile, std::fstream &blockPatchFile)

{

    if (usePatchFile_) { // copy to patch

        size_t bsPatchSize = static_cast<size_t>(blockPatchFile.tellp());

        PATCH_LOGI("WritePatch patch block patch %zu img patch offset %zu",

            bsPatchSize, static_cast<size_t>(patchFile.tellp()));

        blockPatchFile.seekg(0, std::ios::beg);

        std::vector<char> buffer(IGMDIFF_LIMIT_UNIT);

        while (bsPatchSize > 0) {

            size_t readLen = (bsPatchSize > IGMDIFF_LIMIT_UNIT) ? IGMDIFF_LIMIT_UNIT : bsPatchSize;

            blockPatchFile.read(buffer.data(), readLen);

            patchFile.write(buffer.data(), readLen);

            bsPatchSize -= readLen;

        }

        PATCH_LOGI("WritePatch patch %zu", static_cast<size_t>(patchFile.tellp()));

    } else {

        for (size_t index = 0; index < updateBlocks_.size(); index++) {

            if (updateBlocks_[index].type == BLOCK_RAW) {

                continue;

            }

            PATCH_LOGI("WritePatch [%zu] write patch patchOffset %zu length %zu",

                index, static_cast<size_t>(patchFile.tellp()), updateBlocks_[index].patchData.size());

            patchFile.write(reinterpret_cast<const char*>(updateBlocks_[index].patchData.data()),

                updateBlocks_[index].patchData.size());

        }

    }

    return 0;

}



int32_t ImageDiff::DiffImage(const std::string &patchName)

{

    std::fstream blockPatchFile;

    std::ofstream patchFile(patchName, std::ios::out | std::ios::trunc | std::ios::binary);

    if (patchFile.fail()) {

        PATCH_LOGE("Failed to open %s", patchName.c_str());

        return -1;

    }

    patchFile.write(PKGDIFF_MAGIC, std::char_traits<char>::length(PKGDIFF_MAGIC));

    size_t dataOffset = std::char_traits<char>::length(PKGDIFF_MAGIC);

    uint32_t size = static_cast<uint32_t>(updateBlocks_.size());

    patchFile.write(reinterpret_cast<const char*>(&size), sizeof(uint32_t));

    dataOffset += sizeof(uint32_t);



    for (size_t index = 0; index < updateBlocks_.size(); index++) {

        dataOffset += GetHeaderSize(updateBlocks_[index]);

        if (updateBlocks_[index].destOriginalLength >= MAX_NEW_LENGTH ||

            updateBlocks_[index].newInfo.length >= MAX_NEW_LENGTH) {

            usePatchFile_ = true;

        }

    }



    if (usePatchFile_) {

        blockPatchFile.open(patchName + ".bspatch", std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary);

        if (blockPatchFile.fail()) {

            PATCH_LOGE("Failed to open bspatch %s", patchName.c_str());

            return -1;

        }

    }



    for (size_t index = 0; index < updateBlocks_.size(); index++) {

        PATCH_LOGI("DiffImage [%zu] write header patchOffset %zu dataOffset %zu",

            index, static_cast<size_t>(patchFile.tellp()), dataOffset);

        patchFile.write(reinterpret_cast<const char*>(&updateBlocks_[index].type), sizeof(uint32_t));

        int32_t ret = WriteHeader(patchFile, blockPatchFile, dataOffset, updateBlocks_[index]);

        if (ret != 0) {

            PATCH_LOGE("Failed to write header");

            return -1;

        }

    }



    int32_t ret = WritePatch(patchFile, blockPatchFile);

    if (ret != 0) {

        PATCH_LOGE("Failed to write patch");

        return -1;

    }

    PATCH_LOGI("DiffImage success patchOffset %zu %s", static_cast<size_t>(patchFile.tellp()), patchName.c_str());

    patchFile.close();

    return 0;

}



int32_t CompressedImageDiff::MakePatch(const std::string &patchName)

{

    PATCH_DEBUG("CompressedImageDiff::MakePatch %s limit_:%d", patchName.c_str(), limit_);

    if (newParser_ == nullptr || oldParser_ == nullptr) {

        PATCH_LOGE("Invalid parser");

        return -1;

    }

    BlockBuffer newBuffer;

    BlockBuffer oldBuffer;

    if (newParser_->GetPkgBuffer(newBuffer) != 0 || oldParser_->GetPkgBuffer(oldBuffer) != 0) {

        PATCH_LOGE("Failed to get pkgbuffer");

        return -1;

    }



    if (limit_ != 0 && newBuffer.length >= limit_) {

        PatchBuffer oldInfo = { oldBuffer.buffer, 0, oldBuffer.length };

        PatchBuffer newInfo = { newBuffer.buffer, 0, newBuffer.length };

        if (SplitImage(oldInfo, newInfo) != 0) {

            PATCH_LOGE("Failed to split imgage");

            return -1;

        }

        return DiffImage(patchName);

    }



    size_t oldOffset = 0;

    size_t newOffset = 0;

    for (size_t i = 0; i < newParser_->GetFileIds().size(); i++) {

        PATCH_LOGI("CompressedImageDiff::DiffFile %s oldOffset:%zu newOffset:%zu",

            newParser_->GetFileIds()[i].c_str(), oldOffset, newOffset);

        if (DiffFile(newParser_->GetFileIds()[i], oldOffset, newOffset) != 0) {

            PATCH_LOGE("Failed to generate patch");

            updateBlocks_.clear();

            return ImageDiff::MakePatch(patchName);

        }

    }

    PATCH_LOGI("MakePatch oldOffset:%zu newOffset:%zu newBuffer %zu oldBuffer: %zu",

        oldOffset, newOffset, newBuffer.length, oldBuffer.length);

    if (newOffset < newBuffer.length) {

        ImageBlock block = {

            BLOCK_RAW,

            { newBuffer.buffer, newOffset, newBuffer.length - newOffset },

            { oldBuffer.buffer, oldOffset, oldBuffer.length - oldOffset },

        };

        updateBlocks_.push_back(std::move(block));

    }

    return DiffImage(patchName);

}



int32_t CompressedImageDiff::DiffFile(const std::string &fileName, size_t &oldOffset, size_t &newOffset)

{

    BlockBuffer orgNewBuffer;

    BlockBuffer orgOldBuffer;

    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);

    int32_t ret1 = oldParser_->GetPkgBuffer(orgOldBuffer);

    if (ret != 0 || ret1 != 0) {

        PATCH_LOGE("Failed to get pkgbuffer");

        return -1;

    }

    std::vector<uint8_t> newBuffer;

    std::vector<uint8_t> oldBuffer;

    ret = newParser_->Extract(fileName, newBuffer);

    const FileInfo *newFileInfo = newParser_->GetFileInfo(fileName);

    if (ret != 0 || newFileInfo == nullptr) {

        PATCH_LOGE("Failed to get new data");

        return -1;

    }

    newOffset += GET_REAL_DATA_LEN(newFileInfo);

    if (limit_ != 0 && newFileInfo->unpackedSize >= limit_) {

        PATCH_LOGE("Exceed limit, so make patch by original file");

        return PATCH_EXCEED_LIMIT;

    }



    ret = oldParser_->Extract(fileName, oldBuffer);

    if (ret != 0) {

        ImageBlock block = {

            BLOCK_RAW,

            { orgNewBuffer.buffer, newFileInfo->headerOffset, GET_REAL_DATA_LEN(newFileInfo) },

            { orgOldBuffer.buffer, 0, orgOldBuffer.length },

        };

        updateBlocks_.push_back(std::move(block));

        return 0;

    }

    const FileInfo *oldFileInfo = oldParser_->GetFileInfo(fileName);

    if (oldFileInfo == nullptr) {

        PATCH_LOGE("Failed to get file info");

        return -1;

    }

    oldOffset += GET_REAL_DATA_LEN(oldFileInfo);



    BlockBuffer newData = {newBuffer.data(), newFileInfo->unpackedSize};

    ret = TestAndSetConfig(newData, fileName);

    if (ret != 0) {

        PATCH_LOGE("Failed to test zip config");

        return -1;

    }

    std::pair<std::vector<uint8_t>, std::vector<uint8_t>> buffer(std::move(oldBuffer), std::move(newBuffer));

    UpdateBlocks(orgNewBuffer, newFileInfo, orgOldBuffer, oldFileInfo, buffer);

    return 0;

}



void CompressedImageDiff::UpdateBlocks(const BlockBuffer &orgNewBuffer, const Hpackage::FileInfo *newFileInfo,

    const BlockBuffer &orgOldBuffer, const Hpackage::FileInfo *oldFileInfo,

    std::pair<std::vector<uint8_t>, std::vector<uint8_t>> &buffer)

{

    if (type_ != BLOCK_LZ4 && newFileInfo->dataOffset > newFileInfo->headerOffset) {

        ImageBlock block = {

            BLOCK_RAW,

            { orgNewBuffer.buffer, newFileInfo->headerOffset, newFileInfo->dataOffset - newFileInfo->headerOffset },

            { orgOldBuffer.buffer, oldFileInfo->headerOffset, oldFileInfo->dataOffset - oldFileInfo->headerOffset },

        };

        updateBlocks_.push_back(std::move(block));

    }



    ImageBlock block = {

        type_,

        { orgNewBuffer.buffer, newFileInfo->dataOffset, newFileInfo->packedSize },

        { orgOldBuffer.buffer, oldFileInfo->dataOffset, oldFileInfo->packedSize },

    };

    block.srcOriginalData = std::move(buffer.first);

    block.destOriginalData = std::move(buffer.second);

    block.srcOriginalLength = oldFileInfo->unpackedSize;

    block.destOriginalLength = newFileInfo->unpackedSize;

    updateBlocks_.push_back(std::move(block));

}



int32_t ZipImageDiff::WriteHeader(std::ofstream &patchFile,

    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const

{

    int32_t ret = 0;

    if (block.type == BLOCK_DEFLATE) {

        size_t patchSize = 0;

        BlockBuffer oldInfo = { block.srcOriginalData.data(), block.srcOriginalLength };

        BlockBuffer newInfo = { block.destOriginalData.data(), block.destOriginalLength };

        ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);

        if (ret != 0) {

            PATCH_LOGE("Failed to make block patch");

            return -1;

        }



        PATCH_LOGI("WriteHeader BLOCK_DEFLATE patchoffset %zu dataOffset:%zu patchData:%zu",

            static_cast<size_t>(patchFile.tellp()), dataOffset, patchSize);

        PATCH_LOGI("WriteHeader oldInfo start:%zu length:%zu", block.oldInfo.start, block.oldInfo.length);

        PATCH_LOGI("WriteHeader uncompressedLength:%zu %zu", block.srcOriginalLength, block.destOriginalLength);

        PATCH_LOGI("WriteHeader level_:%d method_:%d windowBits_:%d memLevel_:%d strategy_:%d",

            level_, method_, windowBits_, memLevel_, strategy_);



        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.srcOriginalLength), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.destOriginalLength), sizeof(int64_t));



        WriteToFile<int32_t>(patchFile, level_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, method_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, windowBits_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, memLevel_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, strategy_, sizeof(int32_t));

        dataOffset += patchSize;

    } else {

        ret = ImageDiff::WriteHeader(patchFile, blockPatchFile, dataOffset, block);

    }

    return ret;

}



int32_t ZipImageDiff::TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName)

{

    const FileInfo *fileInfo = newParser_->GetFileInfo(fileName);

    if (fileInfo == nullptr) {

        PATCH_LOGE("Failed to get file info");

        return -1;

    }

    ZipFileInfo *info = (ZipFileInfo *)fileInfo;

    method_ = info->method;

    level_ = info->level;

    windowBits_ = info->windowBits;

    memLevel_ = info->memLevel;

    strategy_ = info->strategy;



    BlockBuffer orgNewBuffer;

    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);

    if (ret != 0) {

        PATCH_LOGE("Failed to get pkgbuffer");

        return -1;

    }

    ZipFileInfo zipInfo {};

    zipInfo.fileInfo.packMethod = info->fileInfo.packMethod;

    zipInfo.method = info->method;

    zipInfo.level = info->level;

    zipInfo.windowBits = info->windowBits;

    zipInfo.memLevel = info->memLevel;

    zipInfo.strategy = info->strategy;



    PATCH_LOGI("TestAndSetConfig new info %zu %zu %zu %zu",

        fileInfo->unpackedSize, fileInfo->packedSize, fileInfo->dataOffset, fileInfo->headerOffset);

    PATCH_LOGI("TestAndSetConfig level_:%d method_:%d windowBits_:%d memLevel_:%d strategy_:%d",

        level_, method_, windowBits_, memLevel_, strategy_);

    BlockBuffer orgData = {orgNewBuffer.buffer + fileInfo->dataOffset, fileInfo->packedSize};

    PATCH_DEBUG("DiffFile new orignial hash %zu %s", fileInfo->packedSize, GeneraterBufferHash(orgData).c_str());

    std::vector<uint8_t> data;

    for (int32_t i = ZIP_MAX_LEVEL; i >= 0; i--) {

        zipInfo.level = i;

        size_t bufferSize = 0;

        ret = CompressData(&zipInfo.fileInfo, buffer, data, bufferSize);

        if (ret != 0) {

            PATCH_LOGE("Can not Compress buff");

            return -1;

        }

        if ((bufferSize == fileInfo->packedSize) &&

            memcmp(data.data(), orgNewBuffer.buffer + fileInfo->dataOffset, bufferSize) == 0) {

            level_ = i;

            return 0;

        }

    }

    return -1;

}



int32_t Lz4ImageDiff::WriteHeader(std::ofstream &patchFile,

    std::fstream &blockPatchFile, size_t &dataOffset, ImageBlock &block) const

{

    int32_t ret = 0;

    if (block.type == BLOCK_LZ4) {

        size_t patchSize = 0;

        BlockBuffer oldInfo = { block.srcOriginalData.data(), block.srcOriginalLength };

        BlockBuffer newInfo = { block.destOriginalData.data(), block.destOriginalLength };

        ret = MakeBlockPatch(block, blockPatchFile, newInfo, oldInfo, patchSize);

        if (ret != 0) {

            PATCH_LOGE("Failed to make block patch");

            return -1;

        }



        PATCH_LOGI("WriteHeader BLOCK_LZ4 patchoffset %zu dataOffset:%zu %zu",

            static_cast<size_t>(patchFile.tellp()), dataOffset, patchSize);

        PATCH_LOGI("WriteHeader oldInfo start:%zu length:%zu", block.oldInfo.start, block.oldInfo.length);

        PATCH_LOGI("WriteHeader uncompressedLength:%zu %zu", block.srcOriginalLength, block.destOriginalLength);

        PATCH_LOGI("WriteHeader level_:%d method_:%d blockIndependence_:%d contentChecksumFlag_:%d blockSizeID_:%d %d",

            compressionLevel_, method_, blockIndependence_, contentChecksumFlag_, blockSizeID_, autoFlush_);

        PATCH_LOGI("WriteHeader BLOCK_LZ4 decompressed hash %zu %s",

            newInfo.length, GeneraterBufferHash(newInfo).c_str());

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.start), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.oldInfo.length), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(dataOffset), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.srcOriginalLength), sizeof(int64_t));

        WriteToFile<int64_t>(patchFile, static_cast<int64_t>(block.destOriginalLength), sizeof(int64_t));



        int32_t magic = (method_ == PKG_COMPRESS_METHOD_LZ4_BLOCK) ? LZ4B_MAGIC : LZ4S_MAGIC;

        WriteToFile<int32_t>(patchFile, compressionLevel_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, magic, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, blockIndependence_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, contentChecksumFlag_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, blockSizeID_, sizeof(int32_t));

        WriteToFile<int32_t>(patchFile, autoFlush_, sizeof(int32_t));

        dataOffset += patchSize;

    } else {

        ret = ImageDiff::WriteHeader(patchFile, blockPatchFile, dataOffset, block);

    }

    return ret;

}



int32_t Lz4ImageDiff::TestAndSetConfig(const BlockBuffer &buffer, const std::string &fileName)

{

    const FileInfo *fileInfo = newParser_->GetFileInfo(fileName);

    if (fileInfo == nullptr) {

        PATCH_LOGE("Failed to get file info");

        return -1;

    }

    Lz4FileInfo *info = (Lz4FileInfo *)fileInfo;

    method_ = static_cast<int32_t>(info->fileInfo.packMethod);

    compressionLevel_ = info->compressionLevel;

    blockIndependence_ = info->blockIndependence;

    contentChecksumFlag_ = info->contentChecksumFlag;

    blockSizeID_ = info->blockSizeID;

    autoFlush_ = info->autoFlush;



    BlockBuffer orgNewBuffer;

    int32_t ret = newParser_->GetPkgBuffer(orgNewBuffer);

    if (ret != 0) {

        PATCH_LOGE("Failed to get pkgbuffer");

        return -1;

    }

    Lz4FileInfo lz4Info {{}, info->compressionLevel, info->blockIndependence, info->contentChecksumFlag,

        info->blockSizeID, info->autoFlush};

    lz4Info.fileInfo.packMethod = info->fileInfo.packMethod;



    PATCH_DEBUG("TestAndSetConfig level_:%d method_:%d blockIndependence_:%d contentChecksumFlag_:%d blockSizeID_:%d",

        compressionLevel_, method_, blockIndependence_, contentChecksumFlag_, blockSizeID_);

    PATCH_DEBUG("DiffFile new info %zu %zu %zu %zu",

        fileInfo->unpackedSize, fileInfo->packedSize, fileInfo->dataOffset, fileInfo->headerOffset);

    BlockBuffer orgData = { orgNewBuffer.buffer, fileInfo->packedSize + sizeof(uint32_t) };

    PATCH_DEBUG("DiffFile new orignial hash %zu %s",

        fileInfo->packedSize + sizeof(uint32_t), GeneraterBufferHash(orgData).c_str());



    std::vector<uint8_t> data;

    for (int32_t i = 0; i <= LZ4F_MAX_BLOCKID; i++) {

        lz4Info.blockSizeID = i;

        size_t bufferSize = 0;

        ret = CompressData(&lz4Info.fileInfo, buffer, data, bufferSize);

        if (ret != 0) {

            PATCH_LOGE("Can not Compress buff");

            return -1;

        }

        if ((bufferSize == fileInfo->packedSize + sizeof(uint32_t)) &&

            memcmp(data.data(), orgNewBuffer.buffer + fileInfo->headerOffset, bufferSize) == 0) {

            blockSizeID_ = i;

            return 0;

        }

    }

    return -1;

}



int32_t CompressedImageDiff::CompressData(Hpackage::PkgManager::FileInfoPtr info,

    const BlockBuffer &buffer, std::vector<uint8_t> &outData, size_t &outSize) const

{

    Hpackage::PkgManager *pkgManager = Hpackage::PkgManager::CreatePackageInstance();

    if (pkgManager == nullptr) {

        PATCH_LOGE("Can not get manager ");

        return -1;

    }

    Hpackage::PkgManager::StreamPtr stream1 = nullptr;

    pkgManager->CreatePkgStream(stream1, "gzip",

        [&outData, &outSize](const PkgBuffer &data,

            size_t size, size_t start, bool isFinish, const void *context) ->int {

            if (isFinish) {

                return 0;

            }

            outSize += size;

            if ((start + outSize) > outData.size()) {

                outData.resize(IGMDIFF_LIMIT_UNIT * ((start + outSize) / IGMDIFF_LIMIT_UNIT + 1));

            }

            if (memcpy_s(outData.data() + start, outData.size() - start, data.buffer, size) != EOK) {

                PATCH_LOGE("Failed to memcpy_s data outData.size() = %zu size = %zu start = %zu",

                    outData.size(), size, start);

                return -1;

            }

            return 0;

        }, nullptr);

    int32_t ret = pkgManager->CompressBuffer(info, {buffer.buffer, buffer.length}, stream1);

    if (ret != 0) {

        PATCH_LOGE("Can not Compress buff ");

        Hpackage::PkgManager::ReleasePackageInstance(pkgManager);

        return -1;

    }

    PATCH_DEBUG("UpdateDiff::MakePatch totalSize: %zu", outSize);

    Hpackage::PkgManager::ReleasePackageInstance(pkgManager);

    return 0;

}

} // namespace UpdatePatch