Home | History | Annotate | Download | only in elf
      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