diff options
Diffstat (limited to 'source/Host/common/LZMA.cpp')
-rw-r--r-- | source/Host/common/LZMA.cpp | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/source/Host/common/LZMA.cpp b/source/Host/common/LZMA.cpp new file mode 100644 index 0000000000000..02be8a09df66a --- /dev/null +++ b/source/Host/common/LZMA.cpp @@ -0,0 +1,146 @@ +//===-- LZMA.cpp ------------------------------------------------*- C++ -*-===// +// +// 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 |