1353952Sdim//===-- LZMA.cpp ------------------------------------------------*- C++ -*-===//
2353952Sdim//
3353952Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353952Sdim// See https://llvm.org/LICENSE.txt for license information.
5353952Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353952Sdim//
7353952Sdim//===----------------------------------------------------------------------===//
8353952Sdim
9353952Sdim#include "lldb/Host/Config.h"
10353952Sdim#include "llvm/ADT/StringRef.h"
11353952Sdim#include "llvm/Support/Error.h"
12353952Sdim
13353952Sdim#if LLDB_ENABLE_LZMA
14353952Sdim#include <lzma.h>
15353952Sdim#endif // LLDB_ENABLE_LZMA
16353952Sdim
17353952Sdimnamespace lldb_private {
18353952Sdim
19353952Sdimnamespace lzma {
20353952Sdim
21353952Sdim#if !LLDB_ENABLE_LZMA
22353952Sdimbool isAvailable() { return false; }
23353952Sdimllvm::Expected<uint64_t>
24353952SdimgetUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
25353952Sdim  llvm_unreachable("lzma::getUncompressedSize is unavailable");
26353952Sdim}
27353952Sdim
28353952Sdimllvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
29353952Sdim                       llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
30353952Sdim  llvm_unreachable("lzma::uncompress is unavailable");
31353952Sdim}
32353952Sdim
33353952Sdim#else // LLDB_ENABLE_LZMA
34353952Sdim
35353952Sdimbool isAvailable() { return true; }
36353952Sdim
37353952Sdimstatic const char *convertLZMACodeToString(lzma_ret Code) {
38353952Sdim  switch (Code) {
39353952Sdim  case LZMA_STREAM_END:
40353952Sdim    return "lzma error: LZMA_STREAM_END";
41353952Sdim  case LZMA_NO_CHECK:
42353952Sdim    return "lzma error: LZMA_NO_CHECK";
43353952Sdim  case LZMA_UNSUPPORTED_CHECK:
44353952Sdim    return "lzma error: LZMA_UNSUPPORTED_CHECK";
45353952Sdim  case LZMA_GET_CHECK:
46353952Sdim    return "lzma error: LZMA_GET_CHECK";
47353952Sdim  case LZMA_MEM_ERROR:
48353952Sdim    return "lzma error: LZMA_MEM_ERROR";
49353952Sdim  case LZMA_MEMLIMIT_ERROR:
50353952Sdim    return "lzma error: LZMA_MEMLIMIT_ERROR";
51353952Sdim  case LZMA_FORMAT_ERROR:
52353952Sdim    return "lzma error: LZMA_FORMAT_ERROR";
53353952Sdim  case LZMA_OPTIONS_ERROR:
54353952Sdim    return "lzma error: LZMA_OPTIONS_ERROR";
55353952Sdim  case LZMA_DATA_ERROR:
56353952Sdim    return "lzma error: LZMA_DATA_ERROR";
57353952Sdim  case LZMA_BUF_ERROR:
58353952Sdim    return "lzma error: LZMA_BUF_ERROR";
59353952Sdim  case LZMA_PROG_ERROR:
60353952Sdim    return "lzma error: LZMA_PROG_ERROR";
61353952Sdim  default:
62353952Sdim    llvm_unreachable("unknown or unexpected lzma status code");
63353952Sdim  }
64353952Sdim}
65353952Sdim
66353952Sdimllvm::Expected<uint64_t>
67353952SdimgetUncompressedSize(llvm::ArrayRef<uint8_t> InputBuffer) {
68353952Sdim  lzma_stream_flags opts{};
69353952Sdim  if (InputBuffer.size() < LZMA_STREAM_HEADER_SIZE) {
70353952Sdim    return llvm::createStringError(
71353952Sdim        llvm::inconvertibleErrorCode(),
72353952Sdim        "size of xz-compressed blob (%lu bytes) is smaller than the "
73353952Sdim        "LZMA_STREAM_HEADER_SIZE (%lu bytes)",
74353952Sdim        InputBuffer.size(), LZMA_STREAM_HEADER_SIZE);
75353952Sdim  }
76353952Sdim
77353952Sdim  // Decode xz footer.
78353952Sdim  lzma_ret xzerr = lzma_stream_footer_decode(
79353952Sdim      &opts, InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE).data());
80353952Sdim  if (xzerr != LZMA_OK) {
81353952Sdim    return llvm::createStringError(llvm::inconvertibleErrorCode(),
82353952Sdim                                   "lzma_stream_footer_decode()=%s",
83353952Sdim                                   convertLZMACodeToString(xzerr));
84353952Sdim  }
85353952Sdim  if (InputBuffer.size() < (opts.backward_size + LZMA_STREAM_HEADER_SIZE)) {
86353952Sdim    return llvm::createStringError(
87353952Sdim        llvm::inconvertibleErrorCode(),
88353952Sdim        "xz-compressed buffer size (%lu bytes) too small (required at "
89353952Sdim        "least %lu bytes) ",
90353952Sdim        InputBuffer.size(), (opts.backward_size + LZMA_STREAM_HEADER_SIZE));
91353952Sdim  }
92353952Sdim
93353952Sdim  // Decode xz index.
94353952Sdim  lzma_index *xzindex;
95353952Sdim  uint64_t memlimit(UINT64_MAX);
96353952Sdim  size_t inpos = 0;
97353952Sdim  xzerr = lzma_index_buffer_decode(
98353952Sdim      &xzindex, &memlimit, nullptr,
99353952Sdim      InputBuffer.take_back(LZMA_STREAM_HEADER_SIZE + opts.backward_size)
100353952Sdim          .data(),
101353952Sdim      &inpos, InputBuffer.size());
102353952Sdim  if (xzerr != LZMA_OK) {
103353952Sdim    return llvm::createStringError(llvm::inconvertibleErrorCode(),
104353952Sdim                                   "lzma_index_buffer_decode()=%s",
105353952Sdim                                   convertLZMACodeToString(xzerr));
106353952Sdim  }
107353952Sdim
108353952Sdim  // Get size of uncompressed file to construct an in-memory buffer of the
109353952Sdim  // same size on the calling end (if needed).
110353952Sdim  uint64_t uncompressedSize = lzma_index_uncompressed_size(xzindex);
111353952Sdim
112353952Sdim  // Deallocate xz index as it is no longer needed.
113353952Sdim  lzma_index_end(xzindex, nullptr);
114353952Sdim
115353952Sdim  return uncompressedSize;
116353952Sdim}
117353952Sdim
118353952Sdimllvm::Error uncompress(llvm::ArrayRef<uint8_t> InputBuffer,
119353952Sdim                       llvm::SmallVectorImpl<uint8_t> &Uncompressed) {
120353952Sdim  llvm::Expected<uint64_t> uncompressedSize = getUncompressedSize(InputBuffer);
121353952Sdim
122353952Sdim  if (auto err = uncompressedSize.takeError())
123353952Sdim    return err;
124353952Sdim
125353952Sdim  Uncompressed.resize(*uncompressedSize);
126353952Sdim
127353952Sdim  // Decompress xz buffer to buffer.
128353952Sdim  uint64_t memlimit = UINT64_MAX;
129353952Sdim  size_t inpos = 0;
130353952Sdim  size_t outpos = 0;
131353952Sdim  lzma_ret ret = lzma_stream_buffer_decode(
132353952Sdim      &memlimit, 0, nullptr, InputBuffer.data(), &inpos, InputBuffer.size(),
133353952Sdim      Uncompressed.data(), &outpos, Uncompressed.size());
134353952Sdim  if (ret != LZMA_OK) {
135353952Sdim    return llvm::createStringError(llvm::inconvertibleErrorCode(),
136353952Sdim                                   "lzma_stream_buffer_decode()=%s",
137353952Sdim                                   convertLZMACodeToString(ret));
138353952Sdim  }
139353952Sdim
140353952Sdim  return llvm::Error::success();
141353952Sdim}
142353952Sdim
143353952Sdim#endif // LLDB_ENABLE_LZMA
144353952Sdim
145353952Sdim} // end of namespace lzma
146353952Sdim} // namespace lldb_private
147