David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 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> |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 20 | #include <mutex> |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 21 | |
| 22 | #include "base/array_ref.h" |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 23 | #include "base/bit_utils.h" |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 24 | #include "base/leb128.h" |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 25 | #include "dwarf/writer.h" |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 26 | |
| 27 | // liblzma. |
| 28 | #include "7zCrc.h" |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 29 | #include "Xz.h" |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 30 | #include "XzCrc64.h" |
| 31 | #include "XzEnc.h" |
| 32 | |
| 33 | namespace art { |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 34 | |
David Srbecky | 42d0234 | 2019-04-02 13:52:50 +0100 | [diff] [blame] | 35 | constexpr size_t kChunkSize = 16 * KB; |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 36 | |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 37 | static void XzInitCrc() { |
| 38 | static std::once_flag crc_initialized; |
| 39 | std::call_once(crc_initialized, []() { |
| 40 | CrcGenerateTable(); |
| 41 | Crc64GenerateTable(); |
| 42 | }); |
| 43 | } |
| 44 | |
David Srbecky | 42d0234 | 2019-04-02 13:52:50 +0100 | [diff] [blame] | 45 | void XzCompress(ArrayRef<const uint8_t> src, std::vector<uint8_t>* dst, int level) { |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 46 | // Configure the compression library. |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 47 | XzInitCrc(); |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 48 | CLzma2EncProps lzma2Props; |
| 49 | Lzma2EncProps_Init(&lzma2Props); |
David Srbecky | 42d0234 | 2019-04-02 13:52:50 +0100 | [diff] [blame] | 50 | lzma2Props.lzmaProps.level = level; |
David Srbecky | 82b9560 | 2019-02-15 18:52:47 +0000 | [diff] [blame] | 51 | lzma2Props.lzmaProps.reduceSize = src.size(); // Size of data that will be compressed. |
| 52 | lzma2Props.blockSize = kChunkSize; |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 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_; |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 76 | ArrayRef<const uint8_t> src_; |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 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); |
David Srbecky | cf1af73 | 2018-12-04 14:31:32 +0000 | [diff] [blame] | 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); |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 130 | } |
| 131 | |
David Srbecky | 154c57f | 2018-06-03 12:00:27 +0100 | [diff] [blame] | 132 | } // namespace art |