| //===-- LZMA.cpp ----------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Host/Config.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Error.h" | |
| #if LLDB_ENABLE_LZMA | |
| #include <lzma.h> | |
| #endif // LLDB_ENABLE_LZMA | |
| namespace lldb_private { | |
| namespace lzma { | |
| #if !LLDB_ENABLE_LZMA | |
| bool isAvailable() { return false; } | |
| llvm::Expected<uint64_t> | |
| getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) { | |
| llvm_unreachable("lzma::getUncompressedSize is unavailable"); | |
| } | |
| llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer, | |
| llvm::SmallVectorImpl<uint8_t> &Uncompressed) { | |
| llvm_unreachable("lzma::uncompress is unavailable"); | |
| } | |
| #else // LLDB_ENABLE_LZMA | |
| bool isAvailable() { return true; } | |
| static const char *convertLZMACodeToString(lzma_ret Code) { | |
| switch (Code) { | |
| case LZMA_STREAM_END: | |
| return "lzma error: LZMA_STREAM_END"; | |
| case LZMA_NO_CHECK: | |
| return "lzma error: LZMA_NO_CHECK"; | |
| case LZMA_UNSUPPORTED_CHECK: | |
| return "lzma error: LZMA_UNSUPPORTED_CHECK"; | |
| case LZMA_GET_CHECK: | |
| return "lzma error: LZMA_GET_CHECK"; | |
| case LZMA_MEM_ERROR: | |
| return "lzma error: LZMA_MEM_ERROR"; | |
| case LZMA_MEMLIMIT_ERROR: | |
| return "lzma error: LZMA_MEMLIMIT_ERROR"; | |
| case LZMA_FORMAT_ERROR: | |
| return "lzma error: LZMA_FORMAT_ERROR"; | |
| case LZMA_OPTIONS_ERROR: | |
| return "lzma error: LZMA_OPTIONS_ERROR"; | |
| case LZMA_DATA_ERROR: | |
| return "lzma error: LZMA_DATA_ERROR"; | |
| case LZMA_BUF_ERROR: | |
| return "lzma error: LZMA_BUF_ERROR"; | |
| case LZMA_PROG_ERROR: | |
| return "lzma error: LZMA_PROG_ERROR"; | |
| default: | |
| llvm_unreachable("unknown or unexpected lzma status code"); | |
| } | |
| } | |
| llvm::Expected<uint64_t> | |
| getUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) { | |
| lzma_stream_flags opts{}; | |
| if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "size of xz-compressed blob (%lu bytes) is smaller than the " | |
| "LZMA_STREAM_HEADER_SIZE (%lu bytes)", | |
| InputBuffer.size(), LZMA_STREAM_HEADER_SIZE); | |
| } | |
| // Decode xz footer. | |
| lzma_ret xzerr = lzma_stream_footer_decode( | |
| &opts, InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data()); | |
| if (xzerr != LZMA_OK) { | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "lzma_stream_footer_decode()=%s", | |
| convertLZMACodeToString(xzerr)); | |
| } | |
| if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) { | |
| return llvm::createStringError( | |
| llvm::inconvertibleErrorCode(), | |
| "xz-compressed buffer size (%lu bytes) too small (required at " | |
| "least %lu bytes) ", | |
| InputBuffer.size(), (opts.backward_size + LZMA_STREAM_HEADER_SIZE)); | |
| } | |
| // Decode xz index. | |
| lzma_index *xzindex; | |
| uint64_t memlimit(UINT64_MAX); | |
| size_t inpos = 0; | |
| xzerr = lzma_index_buffer_decode( | |
| &xzindex, &memlimit, nullptr, | |
| InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size) | |
| .data(), | |
| &inpos, InputBuffer.size()); | |
| if (xzerr != LZMA_OK) { | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "lzma_index_buffer_decode()=%s", | |
| convertLZMACodeToString(xzerr)); | |
| } | |
| // Get size of uncompressed file to construct an in-memory buffer of the | |
| // same size on the calling end (if needed). | |
| uint64_t uncompressedSize = lzma_index_uncompressed_size(xzindex); | |
| // Deallocate xz index as it is no longer needed. | |
| lzma_index_end(xzindex, nullptr); | |
| return uncompressedSize; | |
| } | |
| llvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer, | |
| llvm::SmallVectorImpl<uint8_t> &Uncompressed) { | |
| llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer); | |
| if (auto err = uncompressedSize.takeError()) | |
| return err; | |
| Uncompressed.resize(*uncompressedSize); | |
| // Decompress xz buffer to buffer. | |
| uint64_t memlimit = UINT64_MAX; | |
| size_t inpos = 0; | |
| size_t outpos = 0; | |
| lzma_ret ret = lzma_stream_buffer_decode( | |
| &memlimit, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(), | |
| Uncompressed.data(), &outpos, Uncompressed.size()); | |
| if (ret != LZMA_OK) { | |
| return llvm::createStringError(llvm::inconvertibleErrorCode(), | |
| "lzma_stream_buffer_decode()=%s", | |
| convertLZMACodeToString(ret)); | |
| } | |
| return llvm::Error::success(); | |
| } | |
| #endif // LLDB_ENABLE_LZMA | |
| } // end of namespace lzma | |
| } // namespace lldb_private |