* 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 "lz4_adapter.h"
#include <iostream>
#include <vector>
#include "lz4.h"
#include "lz4frame.h"
#include "lz4hc.h"
#include "pkg_manager.h"
using namespace Hpackage;
namespace UpdatePatch {
Lz4Adapter::Lz4Adapter(UpdatePatchWriterPtr outStream, size_t offset, const PkgManager::FileInfoPtr fileInfo)
: DeflateAdapter(), outStream_(outStream), offset_(offset)
{
const Hpackage::Lz4FileInfo *info = (const Hpackage::Lz4FileInfo *)fileInfo;
compressionLevel_ = info->compressionLevel;
blockIndependence_ = info->blockIndependence;
contentChecksumFlag_ = info->contentChecksumFlag;
blockSizeID_ = info->blockSizeID;
autoFlush_ = info->autoFlush;
if (compressionLevel_ < 1) {
compressionLevel_ = LZ4HC_CLEVEL_MIN - 1;
}
if (compressionLevel_ >= LZ4HC_CLEVEL_MAX) {
compressionLevel_ = LZ4HC_CLEVEL_MAX;
}
}
int32_t Lz4FrameAdapter::Open()
{
if (init_) {
PATCH_LOGE("Has been open");
return 0;
}
LZ4F_preferences_t preferences;
LZ4F_errorCode_t errorCode = LZ4F_createCompressionContext(&compressionContext_, LZ4F_VERSION);
if (LZ4F_isError(errorCode)) {
PATCH_LOGE("Failed to create compress context %s", LZ4F_getErrorName(errorCode));
return -1;
}
if (memset_s(&preferences, sizeof(preferences), 0, sizeof(preferences)) != EOK) {
PATCH_LOGE("Memset failed");
return -1;
}
preferences.autoFlush = static_cast<uint32_t>(autoFlush_);
preferences.compressionLevel = compressionLevel_;
preferences.frameInfo.blockMode = ((blockIndependence_ == 0) ? LZ4F_blockLinked : LZ4F_blockIndependent);
preferences.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID_;
preferences.frameInfo.contentChecksumFlag =
((contentChecksumFlag_ == 0) ? LZ4F_noContentChecksum : LZ4F_contentChecksumEnabled);
size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
blockSize = (blockSize > LZ4B_BLOCK_SIZE) ? LZ4B_BLOCK_SIZE : blockSize;
inData_.resize(blockSize);
size_t outBuffSize = LZ4F_compressBound(blockSize, &preferences);
if (outBuffSize <= 0) {
PATCH_LOGE("BufferSize must > 0");
return -1;
}
buffer_.resize(outBuffSize);
PATCH_LOGI("frameInfo blockSizeID %d compressionLevel_: %d blockIndependence_:%d %d %d blockSize %zu %zu %zu",
static_cast<int32_t>(blockSizeID_), static_cast<int32_t>(compressionLevel_),
static_cast<int32_t>(blockIndependence_), static_cast<int32_t>(contentChecksumFlag_), autoFlush_,
blockSize, LZ4_BLOCK_SIZE(blockSizeID_), outBuffSize);
size_t dataSize = LZ4F_compressBegin(compressionContext_, buffer_.data(), buffer_.size(), &preferences);
if (LZ4F_isError(dataSize)) {
PATCH_LOGE("Failed to generate header %s", LZ4F_getErrorName(dataSize));
return -1;
}
int32_t ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
if (ret != 0) {
PATCH_LOGE("Failed to deflate data");
return -1;
}
offset_ += dataSize;
init_ = true;
return ret;
}
int32_t Lz4FrameAdapter::Close()
{
if (!init_) {
PATCH_LOGE("Has been close");
return 0;
}
LZ4F_errorCode_t errorCode = LZ4F_freeCompressionContext(compressionContext_);
if (LZ4F_isError(errorCode)) {
PATCH_LOGE("Failed to free compress context %s", LZ4F_getErrorName(errorCode));
return -1;
}
init_ = false;
return 0;
}
int32_t Lz4FrameAdapter::WriteData(const BlockBuffer &srcData)
{
size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
int32_t ret = 0;
if (currDataSize_ < inData_.size() && srcData.length < (inData_.size() - currDataSize_)) {
ret = memcpy_s(inData_.data() + currDataSize_, inData_.size() - currDataSize_, srcData.buffer, srcData.length);
if (ret != 0) {
PATCH_LOGE("Failed to copy data ");
return -1;
}
currDataSize_ += srcData.length;
} else if (currDataSize_ > inData_.size()) {
PATCH_LOGE("Failed to size currDataSize_ = %zu, inData_.size() = %zu", currDataSize_, inData_.size());
return -1;
} else {
size_t hasCopyLen = inData_.size() - currDataSize_;
ret = memcpy_s(inData_.data() + currDataSize_, hasCopyLen, srcData.buffer, hasCopyLen);
if (ret != 0) {
PATCH_LOGE("Failed to copy data ");
return -1;
}
BlockBuffer data = {inData_.data(), inData_.size()};
ret = CompressData(data);
if (ret != 0) {
PATCH_LOGE("Failed to compress data ");
return -1;
}
size_t remainLen = srcData.length - hasCopyLen;
currDataSize_ = 0;
while (remainLen >= inData_.size()) {
size_t length = (blockSize <= remainLen) ? blockSize : remainLen;
BlockBuffer data = {srcData.buffer + srcData.length - remainLen, length};
ret = CompressData(data);
if (ret != 0) {
PATCH_LOGE("Failed compress data ");
return -1;
}
remainLen -= length;
}
if (remainLen > 0) {
ret = memcpy_s(inData_.data(), inData_.size(), srcData.buffer + srcData.length - remainLen, remainLen);
if (ret != 0) {
PATCH_LOGE("Failed to copy data ");
return -1;
}
currDataSize_ = remainLen;
}
}
return ret;
}
int32_t Lz4FrameAdapter::CompressData(const BlockBuffer &srcData)
{
int32_t ret = 0;
size_t dataSize = LZ4F_compressUpdate(compressionContext_,
buffer_.data(), buffer_.size(), srcData.buffer, srcData.length, nullptr);
if (LZ4F_isError(dataSize)) {
PATCH_LOGE("Failed to compress update %s", LZ4F_getErrorName(dataSize));
return -1;
}
if (dataSize > 0) {
ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
if (ret != 0) {
PATCH_LOGE("Failed write data ");
return -1;
}
}
offset_ += dataSize;
return ret;
}
int32_t Lz4FrameAdapter::FlushData(size_t &offset)
{
if (currDataSize_ > 0) {
BlockBuffer data = {inData_.data(), currDataSize_};
int32_t ret = CompressData(data);
if (ret != 0) {
PATCH_LOGE("Failed to compress data ");
return -1;
}
}
size_t dataSize = LZ4F_compressEnd(compressionContext_, buffer_.data(), buffer_.size(), nullptr);
if (LZ4F_isError(dataSize)) {
PATCH_LOGE("Failed to compress update end %s", LZ4F_getErrorName(dataSize));
return -1;
}
int32_t ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
if (ret != 0) {
PATCH_LOGE("Failed to deflate data");
return -1;
}
offset_ += dataSize;
offset = offset_;
return ret;
}
int32_t Lz4BlockAdapter::Open()
{
if (init_) {
PATCH_LOGE("Has been open");
return 0;
}
size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
blockSize = (blockSize > LZ4B_BLOCK_SIZE) ? LZ4B_BLOCK_SIZE : blockSize;
inData_.resize(blockSize);
PATCH_LOGI("frameInfo blockSizeID %d compressionLevel_: %d blockIndependence_:%d %d blockSize %zu %zu %zu",
static_cast<int32_t>(blockSizeID_), static_cast<int32_t>(compressionLevel_),
static_cast<int32_t>(blockIndependence_), static_cast<int32_t>(contentChecksumFlag_),
blockSize, LZ4_BLOCK_SIZE(blockSizeID_), LZ4B_BLOCK_SIZE);
int bufferSize = LZ4_compressBound(LZ4_BLOCK_SIZE(blockSizeID_));
if (bufferSize < 0) {
PATCH_LOGE("Buffer size should >= 0");
return -1;
}
buffer_.resize(static_cast<size_t>(bufferSize));
int32_t ret = memcpy_s(buffer_.data(), buffer_.size(), &LZ4B_MAGIC_NUMBER, sizeof(int32_t));
if (ret != 0) {
PATCH_LOGE("Failed to memcpy ");
return -1;
}
ret = outStream_->Write(offset_, {buffer_.data(), sizeof(int32_t)}, sizeof(int32_t));
if (ret != 0) {
PATCH_LOGE("Failed to deflate data ");
return -1;
}
offset_ += sizeof(int32_t);
init_ = true;
return ret;
}
int32_t Lz4BlockAdapter::Close()
{
if (!init_) {
PATCH_LOGE("Has been close");
return 0;
}
init_ = false;
return 0;
}
int32_t Lz4BlockAdapter::CompressData(const BlockBuffer &srcData)
{
int32_t dataSize = 0;
if (compressionLevel_ < LZ4HC_CLEVEL_MIN) {
dataSize = LZ4_compress_default(reinterpret_cast<const char *>(srcData.buffer),
reinterpret_cast<char *>(buffer_.data() + LZ4B_REVERSED_LEN),
(int32_t)srcData.length, (int32_t)(buffer_.size() - LZ4B_REVERSED_LEN));
} else {
dataSize = LZ4_compress_HC(reinterpret_cast<const char *>(srcData.buffer),
reinterpret_cast<char *>(buffer_.data() + LZ4B_REVERSED_LEN),
(int32_t)srcData.length, (int32_t)(buffer_.size() - LZ4B_REVERSED_LEN),
compressionLevel_);
}
if (dataSize <= 0) {
PATCH_LOGE("Failed to compress data dataSize %d ", dataSize);
return -1;
}
int32_t ret = memcpy_s(buffer_.data(), buffer_.size(), &dataSize, sizeof(int32_t));
if (ret != 0) {
PATCH_LOGE("Failed to memcpy ");
return -1;
}
dataSize += LZ4B_REVERSED_LEN;
ret = outStream_->Write(offset_, {buffer_.data(), static_cast<size_t>(dataSize)}, static_cast<size_t>(dataSize));
if (ret != 0) {
PATCH_LOGE("Failed to write data ");
return -1;
}
offset_ += static_cast<size_t>(dataSize);
return ret;
}
int32_t Lz4BlockAdapter::FlushData(size_t &offset)
{
if (currDataSize_ > 0) {
BlockBuffer data = {inData_.data(), currDataSize_};
int32_t ret = CompressData(data);
if (ret != 0) {
PATCH_LOGE("Failed to compress data ");
return -1;
}
}
offset = offset_;
return 0;
}
}