1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "lz4_adapter.h"
17#include <iostream>
18#include <vector>
19#include "lz4.h"
20#include "lz4frame.h"
21#include "lz4hc.h"
22#include "pkg_manager.h"
23
24using namespace Hpackage;
25
26namespace UpdatePatch {
27Lz4Adapter::Lz4Adapter(UpdatePatchWriterPtr outStream, size_t offset, const PkgManager::FileInfoPtr fileInfo)
28    : DeflateAdapter(), outStream_(outStream), offset_(offset)
29{
30    const Hpackage::Lz4FileInfo *info = (const Hpackage::Lz4FileInfo *)fileInfo;
31    compressionLevel_ = info->compressionLevel;
32    blockIndependence_ = info->blockIndependence;
33    contentChecksumFlag_ = info->contentChecksumFlag;
34    blockSizeID_ = info->blockSizeID;
35    autoFlush_ = info->autoFlush;
36    if (compressionLevel_ < 1) {
37        compressionLevel_ = LZ4HC_CLEVEL_MIN - 1;
38    }
39    if (compressionLevel_ >= LZ4HC_CLEVEL_MAX) {
40        compressionLevel_ = LZ4HC_CLEVEL_MAX;
41    }
42}
43
44int32_t Lz4FrameAdapter::Open()
45{
46    if (init_) {
47        PATCH_LOGE("Has been open");
48        return 0;
49    }
50    LZ4F_preferences_t preferences;
51    LZ4F_errorCode_t errorCode = LZ4F_createCompressionContext(&compressionContext_, LZ4F_VERSION);
52    if (LZ4F_isError(errorCode)) {
53        PATCH_LOGE("Failed to create compress context %s", LZ4F_getErrorName(errorCode));
54        return -1;
55    }
56    if (memset_s(&preferences, sizeof(preferences), 0, sizeof(preferences)) != EOK) {
57        PATCH_LOGE("Memset failed");
58        return -1;
59    }
60    preferences.autoFlush = static_cast<uint32_t>(autoFlush_);
61    preferences.compressionLevel = compressionLevel_;
62    preferences.frameInfo.blockMode = ((blockIndependence_ == 0) ? LZ4F_blockLinked : LZ4F_blockIndependent);
63    preferences.frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID_;
64    preferences.frameInfo.contentChecksumFlag =
65        ((contentChecksumFlag_ == 0) ? LZ4F_noContentChecksum : LZ4F_contentChecksumEnabled);
66    size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
67    blockSize = (blockSize > LZ4B_BLOCK_SIZE) ? LZ4B_BLOCK_SIZE : blockSize;
68    inData_.resize(blockSize);
69    size_t outBuffSize = LZ4F_compressBound(blockSize, &preferences);
70    if (outBuffSize <= 0) {
71        PATCH_LOGE("BufferSize must > 0");
72        return -1;
73    }
74    buffer_.resize(outBuffSize);
75    PATCH_LOGI("frameInfo blockSizeID %d compressionLevel_: %d blockIndependence_:%d %d %d blockSize %zu %zu %zu",
76        static_cast<int32_t>(blockSizeID_), static_cast<int32_t>(compressionLevel_),
77        static_cast<int32_t>(blockIndependence_), static_cast<int32_t>(contentChecksumFlag_), autoFlush_,
78        blockSize, LZ4_BLOCK_SIZE(blockSizeID_), outBuffSize);
79
80    /* write package header */
81    size_t dataSize = LZ4F_compressBegin(compressionContext_, buffer_.data(), buffer_.size(), &preferences);
82    if (LZ4F_isError(dataSize)) {
83        PATCH_LOGE("Failed to generate header %s", LZ4F_getErrorName(dataSize));
84        return -1;
85    }
86    int32_t ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
87    if (ret != 0) {
88        PATCH_LOGE("Failed to deflate data");
89        return -1;
90    }
91    offset_ += dataSize;
92    init_ = true;
93    return ret;
94}
95
96int32_t Lz4FrameAdapter::Close()
97{
98    if (!init_) {
99        PATCH_LOGE("Has been close");
100        return 0;
101    }
102    LZ4F_errorCode_t errorCode = LZ4F_freeCompressionContext(compressionContext_);
103    if (LZ4F_isError(errorCode)) {
104        PATCH_LOGE("Failed to free compress context %s", LZ4F_getErrorName(errorCode));
105        return -1;
106    }
107    init_ = false;
108    return 0;
109}
110
111int32_t Lz4FrameAdapter::WriteData(const BlockBuffer &srcData)
112{
113    size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
114    int32_t ret = 0;
115    if ((currDataSize_ + srcData.length) < inData_.size()) {
116        ret = memcpy_s(inData_.data() + currDataSize_, inData_.size(), srcData.buffer, srcData.length);
117        if (ret != 0) {
118            PATCH_LOGE("Failed to copy data ");
119            return -1;
120        }
121        currDataSize_ += srcData.length;
122    } else {
123        size_t hasCopyLen = inData_.size() - currDataSize_;
124        ret = memcpy_s(inData_.data() + currDataSize_, inData_.size(), srcData.buffer, hasCopyLen);
125        if (ret != 0) {
126            PATCH_LOGE("Failed to copy data ");
127            return -1;
128        }
129
130        BlockBuffer data = {inData_.data(), inData_.size()};
131        ret = CompressData(data);
132        if (ret != 0) {
133            PATCH_LOGE("Failed to compress data ");
134            return -1;
135        }
136
137        size_t remainLen = srcData.length - hasCopyLen;
138        currDataSize_ = 0;
139        while (remainLen >= inData_.size()) {
140            size_t length = (blockSize <= remainLen) ? blockSize : remainLen;
141            BlockBuffer data = {srcData.buffer + srcData.length - remainLen, length};
142            ret = CompressData(data);
143            if (ret != 0) {
144                PATCH_LOGE("Failed compress data ");
145                return -1;
146            }
147            remainLen -= length;
148        }
149        if (remainLen > 0) {
150            ret = memcpy_s(inData_.data(), inData_.size(), srcData.buffer + srcData.length - remainLen, remainLen);
151            if (ret != 0) {
152                PATCH_LOGE("Failed to copy data ");
153                return -1;
154            }
155            currDataSize_ = remainLen;
156        }
157    }
158    return ret;
159}
160
161int32_t Lz4FrameAdapter::CompressData(const BlockBuffer &srcData)
162{
163    int32_t ret = 0;
164    size_t dataSize = LZ4F_compressUpdate(compressionContext_,
165        buffer_.data(), buffer_.size(), srcData.buffer, srcData.length, nullptr);
166    if (LZ4F_isError(dataSize)) {
167        PATCH_LOGE("Failed to compress update %s", LZ4F_getErrorName(dataSize));
168        return -1;
169    }
170
171    if (dataSize > 0) {
172        ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
173        if (ret != 0) {
174            PATCH_LOGE("Failed write data ");
175            return -1;
176        }
177    }
178    offset_ += dataSize;
179    return ret;
180}
181
182int32_t Lz4FrameAdapter::FlushData(size_t &offset)
183{
184    if (currDataSize_ > 0) {
185        BlockBuffer data = {inData_.data(), currDataSize_};
186        int32_t ret = CompressData(data);
187        if (ret != 0) {
188            PATCH_LOGE("Failed to compress data ");
189            return -1;
190        }
191    }
192    size_t dataSize = LZ4F_compressEnd(compressionContext_, buffer_.data(), buffer_.size(), nullptr);
193    if (LZ4F_isError(dataSize)) {
194        PATCH_LOGE("Failed to compress update end %s", LZ4F_getErrorName(dataSize));
195        return -1;
196    }
197    int32_t ret = outStream_->Write(offset_, {buffer_.data(), dataSize}, dataSize);
198    if (ret != 0) {
199        PATCH_LOGE("Failed to deflate data");
200        return -1;
201    }
202    offset_ += dataSize;
203    offset = offset_;
204    return ret;
205}
206
207int32_t Lz4BlockAdapter::Open()
208{
209    if (init_) {
210        PATCH_LOGE("Has been open");
211        return 0;
212    }
213    size_t blockSize = LZ4_BLOCK_SIZE(blockSizeID_);
214    blockSize = (blockSize > LZ4B_BLOCK_SIZE) ? LZ4B_BLOCK_SIZE : blockSize;
215    inData_.resize(blockSize);
216    PATCH_LOGI("frameInfo blockSizeID %d compressionLevel_: %d blockIndependence_:%d %d blockSize %zu %zu %zu",
217        static_cast<int32_t>(blockSizeID_), static_cast<int32_t>(compressionLevel_),
218        static_cast<int32_t>(blockIndependence_), static_cast<int32_t>(contentChecksumFlag_),
219        blockSize, LZ4_BLOCK_SIZE(blockSizeID_), LZ4B_BLOCK_SIZE);
220
221    /* write package */
222    int bufferSize = LZ4_compressBound(LZ4_BLOCK_SIZE(blockSizeID_));
223    if (bufferSize < 0) {
224        PATCH_LOGE("Buffer size should >= 0");
225        return -1;
226    }
227    buffer_.resize(static_cast<size_t>(bufferSize));
228    int32_t ret = memcpy_s(buffer_.data(), buffer_.size(), &LZ4B_MAGIC_NUMBER, sizeof(int32_t));
229    if (ret != 0) {
230        PATCH_LOGE("Failed to memcpy ");
231        return -1;
232    }
233    ret = outStream_->Write(offset_, {buffer_.data(), sizeof(int32_t)}, sizeof(int32_t));
234    if (ret != 0) {
235        PATCH_LOGE("Failed to deflate data ");
236        return -1;
237    }
238    offset_ += sizeof(int32_t);
239    init_ = true;
240    return ret;
241}
242
243int32_t Lz4BlockAdapter::Close()
244{
245    if (!init_) {
246        PATCH_LOGE("Has been close");
247        return 0;
248    }
249    init_ = false;
250    return 0;
251}
252
253int32_t Lz4BlockAdapter::CompressData(const BlockBuffer &srcData)
254{
255    int32_t dataSize = 0;
256    // Compress Block, reserve 4 bytes to store block size
257    if (compressionLevel_ < LZ4HC_CLEVEL_MIN) { // hc 最小是3
258        dataSize = LZ4_compress_default(reinterpret_cast<const char *>(srcData.buffer),
259            reinterpret_cast<char *>(buffer_.data() + LZ4B_REVERSED_LEN),
260            (int32_t)srcData.length, (int32_t)(buffer_.size() - LZ4B_REVERSED_LEN));
261    } else {
262        dataSize = LZ4_compress_HC(reinterpret_cast<const char *>(srcData.buffer),
263            reinterpret_cast<char *>(buffer_.data() + LZ4B_REVERSED_LEN),
264            (int32_t)srcData.length, (int32_t)(buffer_.size() - LZ4B_REVERSED_LEN),
265            compressionLevel_);
266    }
267    if (dataSize <= 0) {
268        PATCH_LOGE("Failed to compress data dataSize %d ", dataSize);
269        return -1;
270    }
271
272    // Write block to buffer.
273    // Buffer format: <block size> + <block contents>
274    int32_t ret = memcpy_s(buffer_.data(), buffer_.size(), &dataSize, sizeof(int32_t));
275    if (ret != 0) {
276        PATCH_LOGE("Failed to memcpy ");
277        return -1;
278    }
279    dataSize += LZ4B_REVERSED_LEN;
280
281    ret = outStream_->Write(offset_, {buffer_.data(), static_cast<size_t>(dataSize)}, static_cast<size_t>(dataSize));
282    if (ret != 0) {
283        PATCH_LOGE("Failed to write data ");
284        return -1;
285    }
286    offset_ += static_cast<size_t>(dataSize);
287    return ret;
288}
289
290int32_t Lz4BlockAdapter::FlushData(size_t &offset)
291{
292    if (currDataSize_ > 0) {
293        BlockBuffer data = {inData_.data(), currDataSize_};
294        int32_t ret = CompressData(data);
295        if (ret != 0) {
296            PATCH_LOGE("Failed to compress data ");
297            return -1;
298        }
299    }
300    offset = offset_;
301    return 0;
302}
303} // namespace UpdatePatch
304