1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "xz_utils.h" 18 19 #include <vector> 20 #include <mutex> 21 22 #include "base/array_ref.h" 23 #include "base/bit_utils.h" 24 #include "base/leb128.h" 25 #include "dwarf/writer.h" 26 27 // liblzma. 28 #include "7zCrc.h" 29 #include "Xz.h" 30 #include "XzCrc64.h" 31 #include "XzEnc.h" 32 33 namespace art { 34 35 constexpr size_t kChunkSize = 16 * KB; 36 37 static void XzInitCrc() { 38 static std::once_flag crc_initialized; 39 std::call_once(crc_initialized, []() { 40 CrcGenerateTable(); 41 Crc64GenerateTable(); 42 }); 43 } 44 45 void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst, int level) { 46 // Configure the compression library. 47 XzInitCrc(); 48 CLzma2EncProps lzma2Props; 49 Lzma2EncProps_Init(&lzma2Props); 50 lzma2Props.lzmaProps.level = level; 51 lzma2Props.lzmaProps.reduceSize = src.size(); // Size of data that will be compressed. 52 lzma2Props.blockSize = kChunkSize; 53 Lzma2EncProps_Normalize(&lzma2Props); 54 CXzProps props; 55 XzProps_Init(&props); 56 props.lzma2Props = lzma2Props; 57 // Implement the required interface for communication (written in C so no virtual methods). 58 struct XzCallbacks : public ISeqInStream, public ISeqOutStream, public ICompressProgress { 59 static SRes ReadImpl(const ISeqInStream* p, void* buf, size_t* size) { 60 auto* ctx = static_cast<XzCallbacks*>(const_cast<ISeqInStream*>(p)); 61 *size = std::min(*size, ctx->src_.size() - ctx->src_pos_); 62 memcpy(buf, ctx->src_.data() + ctx->src_pos_, *size); 63 ctx->src_pos_ += *size; 64 return SZ_OK; 65 } 66 static size_t WriteImpl(const ISeqOutStream* p, const void* buf, size_t size) { 67 auto* ctx = static_cast<const XzCallbacks*>(p); 68 const uint8_t* buffer = reinterpret_cast<const uint8_t*>(buf); 69 ctx->dst_->insert(ctx->dst_->end(), buffer, buffer + size); 70 return size; 71 } 72 static SRes ProgressImpl(const ICompressProgress* , UInt64, UInt64) { 73 return SZ_OK; 74 } 75 size_t src_pos_; 76 ArrayRef<const uint8_t> src_; 77 std::vector<uint8_t>* dst_; 78 }; 79 XzCallbacks callbacks; 80 callbacks.Read = XzCallbacks::ReadImpl; 81 callbacks.Write = XzCallbacks::WriteImpl; 82 callbacks.Progress = XzCallbacks::ProgressImpl; 83 callbacks.src_pos_ = 0; 84 callbacks.src_ = src; 85 callbacks.dst_ = dst; 86 // Compress. 87 SRes res = Xz_Encode(&callbacks, &callbacks, &props, &callbacks); 88 CHECK_EQ(res, SZ_OK); 89 90 // Decompress the data back and check that we get the original. 91 if (kIsDebugBuild) { 92 std::vector<uint8_t> decompressed; 93 XzDecompress(ArrayRef<const uint8_t>(*dst), &decompressed); 94 DCHECK_EQ(decompressed.size(), src.size()); 95 DCHECK_EQ(memcmp(decompressed.data(), src.data(), src.size()), 0); 96 } 97 } 98 99 void XzDecompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst) { 100 XzInitCrc(); 101 std::unique_ptr<CXzUnpacker> state(new CXzUnpacker()); 102 ISzAlloc alloc; 103 alloc.Alloc = [](ISzAllocPtr, size_t size) { return malloc(size); }; 104 alloc.Free = [](ISzAllocPtr, void* ptr) { return free(ptr); }; 105 XzUnpacker_Construct(state.get(), &alloc); 106 107 size_t src_offset = 0; 108 size_t dst_offset = 0; 109 ECoderStatus status; 110 do { 111 dst->resize(RoundUp(dst_offset + kPageSize / 4, kPageSize)); 112 size_t src_remaining = src.size() - src_offset; 113 size_t dst_remaining = dst->size() - dst_offset; 114 int return_val = XzUnpacker_Code(state.get(), 115 dst->data() + dst_offset, 116 &dst_remaining, 117 src.data() + src_offset, 118 &src_remaining, 119 true, 120 CODER_FINISH_ANY, 121 &status); 122 CHECK_EQ(return_val, SZ_OK); 123 src_offset += src_remaining; 124 dst_offset += dst_remaining; 125 } while (status == CODER_STATUS_NOT_FINISHED); 126 CHECK_EQ(src_offset, src.size()); 127 CHECK(XzUnpacker_IsStreamWasFinished(state.get())); 128 XzUnpacker_Free(state.get()); 129 dst->resize(dst_offset); 130 } 131 132 } // namespace art 133