1*5ffd83dbSDimitry Andric //===-- LZMA.cpp ----------------------------------------------------------===//
29dba64beSDimitry Andric //
39dba64beSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
49dba64beSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
59dba64beSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
69dba64beSDimitry Andric //
79dba64beSDimitry Andric //===----------------------------------------------------------------------===//
89dba64beSDimitry Andric 
99dba64beSDimitry Andric #include "lldb/Host/Config.h"
109dba64beSDimitry Andric #include "llvm/ADT/StringRef.h"
119dba64beSDimitry Andric #include "llvm/Support/Error.h"
129dba64beSDimitry Andric 
139dba64beSDimitry Andric #if LLDB_ENABLE_LZMA
149dba64beSDimitry Andric #include <lzma.h>
159dba64beSDimitry Andric #endif // LLDB_ENABLE_LZMA
169dba64beSDimitry Andric 
179dba64beSDimitry Andric namespace lldb_private {
189dba64beSDimitry Andric 
199dba64beSDimitry Andric namespace lzma {
209dba64beSDimitry Andric 
219dba64beSDimitry Andric #if !LLDB_ENABLE_LZMA
isAvailable()229dba64beSDimitry Andric bool isAvailable() { return false; }
239dba64beSDimitry Andric llvm::Expected<uint64_t>
getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer)249dba64beSDimitry Andric getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
259dba64beSDimitry Andric   llvm_unreachable("lzma::getUncompressedSize is unavailable");
269dba64beSDimitry Andric }
279dba64beSDimitry Andric 
uncompress(llvm::ArrayRef<uint8_t> InputBuffer,llvm::SmallVectorImpl<uint8_t> & Uncompressed)289dba64beSDimitry Andric llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
299dba64beSDimitry Andric                        llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
309dba64beSDimitry Andric   llvm_unreachable("lzma::uncompress is unavailable");
319dba64beSDimitry Andric }
329dba64beSDimitry Andric 
339dba64beSDimitry Andric #else // LLDB_ENABLE_LZMA
349dba64beSDimitry Andric 
359dba64beSDimitry Andric bool isAvailable() { return true; }
369dba64beSDimitry Andric 
379dba64beSDimitry Andric static const char *convertLZMACodeToString(lzma_ret Code) {
389dba64beSDimitry Andric   switch (Code) {
399dba64beSDimitry Andric   case LZMA_STREAM_END:
409dba64beSDimitry Andric     return "lzma error: LZMA_STREAM_END";
419dba64beSDimitry Andric   case LZMA_NO_CHECK:
429dba64beSDimitry Andric     return "lzma error: LZMA_NO_CHECK";
439dba64beSDimitry Andric   case LZMA_UNSUPPORTED_CHECK:
449dba64beSDimitry Andric     return "lzma error: LZMA_UNSUPPORTED_CHECK";
459dba64beSDimitry Andric   case LZMA_GET_CHECK:
469dba64beSDimitry Andric     return "lzma error: LZMA_GET_CHECK";
479dba64beSDimitry Andric   case LZMA_MEM_ERROR:
489dba64beSDimitry Andric     return "lzma error: LZMA_MEM_ERROR";
499dba64beSDimitry Andric   case LZMA_MEMLIMIT_ERROR:
509dba64beSDimitry Andric     return "lzma error: LZMA_MEMLIMIT_ERROR";
519dba64beSDimitry Andric   case LZMA_FORMAT_ERROR:
529dba64beSDimitry Andric     return "lzma error: LZMA_FORMAT_ERROR";
539dba64beSDimitry Andric   case LZMA_OPTIONS_ERROR:
549dba64beSDimitry Andric     return "lzma error: LZMA_OPTIONS_ERROR";
559dba64beSDimitry Andric   case LZMA_DATA_ERROR:
569dba64beSDimitry Andric     return "lzma error: LZMA_DATA_ERROR";
579dba64beSDimitry Andric   case LZMA_BUF_ERROR:
589dba64beSDimitry Andric     return "lzma error: LZMA_BUF_ERROR";
599dba64beSDimitry Andric   case LZMA_PROG_ERROR:
609dba64beSDimitry Andric     return "lzma error: LZMA_PROG_ERROR";
619dba64beSDimitry Andric   default:
629dba64beSDimitry Andric     llvm_unreachable("unknown or unexpected lzma status code");
639dba64beSDimitry Andric   }
649dba64beSDimitry Andric }
659dba64beSDimitry Andric 
669dba64beSDimitry Andric llvm::Expected<uint64_t>
679dba64beSDimitry Andric getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
689dba64beSDimitry Andric   lzma_stream_flags opts{};
699dba64beSDimitry Andric   if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) {
709dba64beSDimitry Andric     return llvm::createStringError(
719dba64beSDimitry Andric         llvm::inconvertibleErrorCode(),
729dba64beSDimitry Andric         "size of xz-compressed blob (%lu bytes) is smaller than the "
739dba64beSDimitry Andric         "LZMA_STREAM_HEADER_SIZE (%lu bytes)",
749dba64beSDimitry Andric         InputBuffer.size(), LZMA_STREAM_HEADER_SIZE);
759dba64beSDimitry Andric   }
769dba64beSDimitry Andric 
779dba64beSDimitry Andric   // Decode xz footer.
789dba64beSDimitry Andric   lzma_ret xzerr = lzma_stream_footer_decode(
799dba64beSDimitry Andric       &opts, InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data());
809dba64beSDimitry Andric   if (xzerr != LZMA_OK) {
819dba64beSDimitry Andric     return llvm::createStringError(llvm::inconvertibleErrorCode(),
829dba64beSDimitry Andric                                    "lzma_stream_footer_decode()=%s",
839dba64beSDimitry Andric                                    convertLZMACodeToString(xzerr));
849dba64beSDimitry Andric   }
859dba64beSDimitry Andric   if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) {
869dba64beSDimitry Andric     return llvm::createStringError(
879dba64beSDimitry Andric         llvm::inconvertibleErrorCode(),
889dba64beSDimitry Andric         "xz-compressed buffer size (%lu bytes) too small (required at "
899dba64beSDimitry Andric         "least %lu bytes) ",
909dba64beSDimitry Andric         InputBuffer.size(), (opts.backward_size + LZMA_STREAM_HEADER_SIZE));
919dba64beSDimitry Andric   }
929dba64beSDimitry Andric 
939dba64beSDimitry Andric   // Decode xz index.
949dba64beSDimitry Andric   lzma_index *xzindex;
959dba64beSDimitry Andric   uint64_t memlimit(UINT64_MAX);
969dba64beSDimitry Andric   size_t inpos = 0;
979dba64beSDimitry Andric   xzerr = lzma_index_buffer_decode(
989dba64beSDimitry Andric       &xzindex, &memlimit, nullptr,
999dba64beSDimitry Andric       InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size)
1009dba64beSDimitry Andric           .data(),
1019dba64beSDimitry Andric       &inpos, InputBuffer.size());
1029dba64beSDimitry Andric   if (xzerr != LZMA_OK) {
1039dba64beSDimitry Andric     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1049dba64beSDimitry Andric                                    "lzma_index_buffer_decode()=%s",
1059dba64beSDimitry Andric                                    convertLZMACodeToString(xzerr));
1069dba64beSDimitry Andric   }
1079dba64beSDimitry Andric 
1089dba64beSDimitry Andric   // Get size of uncompressed file to construct an in-memory buffer of the
1099dba64beSDimitry Andric   // same size on the calling end (if needed).
1109dba64beSDimitry Andric   uint64_t uncompressedSize = lzma_index_uncompressed_size(xzindex);
1119dba64beSDimitry Andric 
1129dba64beSDimitry Andric   // Deallocate xz index as it is no longer needed.
1139dba64beSDimitry Andric   lzma_index_end(xzindex, nullptr);
1149dba64beSDimitry Andric 
1159dba64beSDimitry Andric   return uncompressedSize;
1169dba64beSDimitry Andric }
1179dba64beSDimitry Andric 
1189dba64beSDimitry Andric llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
1199dba64beSDimitry Andric                        llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
1209dba64beSDimitry Andric   llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer);
1219dba64beSDimitry Andric 
1229dba64beSDimitry Andric   if (auto err = uncompressedSize.takeError())
1239dba64beSDimitry Andric     return err;
1249dba64beSDimitry Andric 
1259dba64beSDimitry Andric   Uncompressed.resize(*uncompressedSize);
1269dba64beSDimitry Andric 
1279dba64beSDimitry Andric   // Decompress xz buffer to buffer.
1289dba64beSDimitry Andric   uint64_t memlimit = UINT64_MAX;
1299dba64beSDimitry Andric   size_t inpos = 0;
1309dba64beSDimitry Andric   size_t outpos = 0;
1319dba64beSDimitry Andric   lzma_ret ret = lzma_stream_buffer_decode(
1329dba64beSDimitry Andric       &memlimit, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(),
1339dba64beSDimitry Andric       Uncompressed.data(), &outpos, Uncompressed.size());
1349dba64beSDimitry Andric   if (ret != LZMA_OK) {
1359dba64beSDimitry Andric     return llvm::createStringError(llvm::inconvertibleErrorCode(),
1369dba64beSDimitry Andric                                    "lzma_stream_buffer_decode()=%s",
1379dba64beSDimitry Andric                                    convertLZMACodeToString(ret));
1389dba64beSDimitry Andric   }
1399dba64beSDimitry Andric 
1409dba64beSDimitry Andric   return llvm::Error::success();
1419dba64beSDimitry Andric }
1429dba64beSDimitry Andric 
1439dba64beSDimitry Andric #endif // LLDB_ENABLE_LZMA
1449dba64beSDimitry Andric 
1459dba64beSDimitry Andric } // end of namespace lzma
1469dba64beSDimitry Andric } // namespace lldb_private
147