/* * Copyright (C) 2018 The Android Open Source Project * * 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 "xz_utils.h" #include <vector> #include <mutex> #include "base/array_ref.h" #include "base/bit_utils.h" #include "base/leb128.h" #include "dwarf/writer.h" // liblzma. #include "7zCrc.h" #include "Xz.h" #include "XzCrc64.h" #include "XzEnc.h" namespace art { constexpr size_t kChunkSize = 16 * KB; static void XzInitCrc() { static std::once_flag crc_initialized; std::call_once(crc_initialized, []() { CrcGenerateTable(); Crc64GenerateTable(); }); } void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst, int level) { // Configure the compression library. XzInitCrc(); CLzma2EncProps lzma2Props; Lzma2EncProps_Init(&lzma2Props); lzma2Props.lzmaProps.level = level; lzma2Props.lzmaProps.reduceSize = src.size(); // Size of data that will be compressed. lzma2Props.blockSize = kChunkSize; Lzma2EncProps_Normalize(&lzma2Props); CXzProps props; XzProps_Init(&props); props.lzma2Props = lzma2Props; // Implement the required interface for communication (written in C so no virtual methods). struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) { auto* ctx = static_cast<XzCallbacks*>(const_cast<ISeqInStream*>(p)); *size = std::min(*size, ctx->src_.size() - ctx->src_pos_); memcpy(buf, ctx->src_.data() + ctx->src_pos_, *size); ctx->src_pos_ += *size; return SZ_OK; } static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) { auto* ctx = static_cast<const XzCallbacks*>(p); const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); return size; } static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) { return SZ_OK; } size_t src_pos_; ArrayRef<const uint8_t> src_; std::vector<uint8_t>* dst_; }; XzCallbacks callbacks; callbacks.Read = XzCallbacks::ReadImpl; callbacks.Write = XzCallbacks::WriteImpl; callbacks.Progress = XzCallbacks::ProgressImpl; callbacks.src_pos_ = 0; callbacks.src_ = src; callbacks.dst_ = dst; // Compress. SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks); CHECK_EQ(res, SZ_OK); // Decompress the data back and check that we get the original. if (kIsDebugBuild) { std::vector<uint8_t> decompressed; XzDecompress(ArrayRef<const uint8_t>(*dst), &decompressed); DCHECK_EQ(decompressed.size(), src.size()); DCHECK_EQ(memcmp(decompressed.data(), src.data(), src.size()), 0); } } void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) { XzInitCrc(); std::unique_ptr<CXzUnpacker> state(new CXzUnpacker()); ISzAlloc alloc; alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); }; alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); }; XzUnpacker_Construct(state.get(), &alloc); size_t src_offset = 0; size_t dst_offset = 0; ECoderStatus status; do { dst->resize(RoundUp(dst_offset + kPageSize / 4, kPageSize)); size_t src_remaining = src.size() - src_offset; size_t dst_remaining = dst->size() - dst_offset; int return_val = XzUnpacker_Code(state.get(), dst->data() + dst_offset, &dst_remaining, src.data() + src_offset, &src_remaining, true, CODER_FINISH_ANY, &status); CHECK_EQ(return_val, SZ_OK); src_offset += src_remaining; dst_offset += dst_remaining; } while (status == CODER_STATUS_NOT_FINISHED); CHECK_EQ(src_offset, src.size()); CHECK(XzUnpacker_IsStreamWasFinished(state.get())); XzUnpacker_Free(state.get()); dst->resize(dst_offset); } } // namespace art