xref: /llvm-project/llvm/lib/Support/Compression.cpp (revision a41977dd0f4fd7aaad5ec380d50782296a690b47)
1 //===--- Compression.cpp - Compression implementation ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file implements compression functions.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "llvm/Support/Compression.h"
14 #include "llvm/ADT/SmallVector.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Config/config.h"
17 #include "llvm/Support/Compiler.h"
18 #include "llvm/Support/Error.h"
19 #include "llvm/Support/ErrorHandling.h"
20 #if LLVM_ENABLE_ZLIB
21 #include <zlib.h>
22 #endif
23 #if LLVM_ENABLE_ZSTD
24 #include <zstd.h>
25 #endif
26 
27 using namespace llvm;
28 using namespace llvm::compression;
29 
30 const char *compression::getReasonIfUnsupported(compression::Format F) {
31   switch (F) {
32   case compression::Format::Zlib:
33     if (zlib::isAvailable())
34       return nullptr;
35     return "LLVM was not built with LLVM_ENABLE_ZLIB or did not find zlib at "
36            "build time";
37   case compression::Format::Zstd:
38     if (zstd::isAvailable())
39       return nullptr;
40     return "LLVM was not built with LLVM_ENABLE_ZSTD or did not find zstd at "
41            "build time";
42   }
43   llvm_unreachable("");
44 }
45 
46 void compression::compress(Params P, ArrayRef<uint8_t> Input,
47                            SmallVectorImpl<uint8_t> &Output) {
48   switch (P.format) {
49   case compression::Format::Zlib:
50     zlib::compress(Input, Output, P.level);
51     break;
52   case compression::Format::Zstd:
53     zstd::compress(Input, Output, P.level);
54     break;
55   }
56 }
57 
58 Error compression::decompress(compression::Format F, ArrayRef<uint8_t> Input,
59                               SmallVectorImpl<uint8_t> &Output,
60                               size_t UncompressedSize) {
61   switch (F) {
62   case compression::Format::Zlib:
63     return zlib::uncompress(Input, Output, UncompressedSize);
64   case compression::Format::Zstd:
65     return zstd::uncompress(Input, Output, UncompressedSize);
66   }
67   llvm_unreachable("");
68 }
69 
70 Error compression::decompress(DebugCompressionType T, ArrayRef<uint8_t> Input,
71                               SmallVectorImpl<uint8_t> &Output,
72                               size_t UncompressedSize) {
73   return decompress(formatFor(T), Input, Output, UncompressedSize);
74 }
75 
76 #if LLVM_ENABLE_ZLIB
77 
78 static StringRef convertZlibCodeToString(int Code) {
79   switch (Code) {
80   case Z_MEM_ERROR:
81     return "zlib error: Z_MEM_ERROR";
82   case Z_BUF_ERROR:
83     return "zlib error: Z_BUF_ERROR";
84   case Z_STREAM_ERROR:
85     return "zlib error: Z_STREAM_ERROR";
86   case Z_DATA_ERROR:
87     return "zlib error: Z_DATA_ERROR";
88   case Z_OK:
89   default:
90     llvm_unreachable("unknown or unexpected zlib status code");
91   }
92 }
93 
94 bool zlib::isAvailable() { return true; }
95 
96 void zlib::compress(ArrayRef<uint8_t> Input,
97                     SmallVectorImpl<uint8_t> &CompressedBuffer, int Level) {
98   unsigned long CompressedSize = ::compressBound(Input.size());
99   CompressedBuffer.resize_for_overwrite(CompressedSize);
100   int Res = ::compress2((Bytef *)CompressedBuffer.data(), &CompressedSize,
101                         (const Bytef *)Input.data(), Input.size(), Level);
102   if (Res == Z_MEM_ERROR)
103     report_bad_alloc_error("Allocation failed");
104   assert(Res == Z_OK);
105   // Tell MemorySanitizer that zlib output buffer is fully initialized.
106   // This avoids a false report when running LLVM with uninstrumented ZLib.
107   __msan_unpoison(CompressedBuffer.data(), CompressedSize);
108   if (CompressedSize < CompressedBuffer.size())
109     CompressedBuffer.truncate(CompressedSize);
110 }
111 
112 Error zlib::uncompress(ArrayRef<uint8_t> Input, uint8_t *UncompressedBuffer,
113                        size_t &UncompressedSize) {
114   int Res =
115       ::uncompress((Bytef *)UncompressedBuffer, (uLongf *)&UncompressedSize,
116                    (const Bytef *)Input.data(), Input.size());
117   // Tell MemorySanitizer that zlib output buffer is fully initialized.
118   // This avoids a false report when running LLVM with uninstrumented ZLib.
119   __msan_unpoison(UncompressedBuffer, UncompressedSize);
120   return Res ? make_error<StringError>(convertZlibCodeToString(Res),
121                                        inconvertibleErrorCode())
122              : Error::success();
123 }
124 
125 Error zlib::uncompress(ArrayRef<uint8_t> Input,
126                        SmallVectorImpl<uint8_t> &UncompressedBuffer,
127                        size_t UncompressedSize) {
128   UncompressedBuffer.resize_for_overwrite(UncompressedSize);
129   Error E =
130       zlib::uncompress(Input, UncompressedBuffer.data(), UncompressedSize);
131   if (UncompressedSize < UncompressedBuffer.size())
132     UncompressedBuffer.truncate(UncompressedSize);
133   return E;
134 }
135 
136 #else
137 bool zlib::isAvailable() { return false; }
138 void zlib::compress(ArrayRef<uint8_t> Input,
139                     SmallVectorImpl<uint8_t> &CompressedBuffer, int Level) {
140   llvm_unreachable("zlib::compress is unavailable");
141 }
142 Error zlib::uncompress(ArrayRef<uint8_t> Input, uint8_t *UncompressedBuffer,
143                        size_t &UncompressedSize) {
144   llvm_unreachable("zlib::uncompress is unavailable");
145 }
146 Error zlib::uncompress(ArrayRef<uint8_t> Input,
147                        SmallVectorImpl<uint8_t> &UncompressedBuffer,
148                        size_t UncompressedSize) {
149   llvm_unreachable("zlib::uncompress is unavailable");
150 }
151 #endif
152 
153 #if LLVM_ENABLE_ZSTD
154 
155 bool zstd::isAvailable() { return true; }
156 
157 void zstd::compress(ArrayRef<uint8_t> Input,
158                     SmallVectorImpl<uint8_t> &CompressedBuffer, int Level) {
159   unsigned long CompressedBufferSize = ::ZSTD_compressBound(Input.size());
160   CompressedBuffer.resize_for_overwrite(CompressedBufferSize);
161   unsigned long CompressedSize =
162       ::ZSTD_compress((char *)CompressedBuffer.data(), CompressedBufferSize,
163                       (const char *)Input.data(), Input.size(), Level);
164   if (ZSTD_isError(CompressedSize))
165     report_bad_alloc_error("Allocation failed");
166   // Tell MemorySanitizer that zstd output buffer is fully initialized.
167   // This avoids a false report when running LLVM with uninstrumented ZLib.
168   __msan_unpoison(CompressedBuffer.data(), CompressedSize);
169   if (CompressedSize < CompressedBuffer.size())
170     CompressedBuffer.truncate(CompressedSize);
171 }
172 
173 Error zstd::uncompress(ArrayRef<uint8_t> Input, uint8_t *UncompressedBuffer,
174                        size_t &UncompressedSize) {
175   const size_t Res =
176       ::ZSTD_decompress(UncompressedBuffer, UncompressedSize,
177                         (const uint8_t *)Input.data(), Input.size());
178   UncompressedSize = Res;
179   // Tell MemorySanitizer that zstd output buffer is fully initialized.
180   // This avoids a false report when running LLVM with uninstrumented ZLib.
181   __msan_unpoison(UncompressedBuffer, UncompressedSize);
182   return ZSTD_isError(Res) ? make_error<StringError>(ZSTD_getErrorName(Res),
183                                                      inconvertibleErrorCode())
184                            : Error::success();
185 }
186 
187 Error zstd::uncompress(ArrayRef<uint8_t> Input,
188                        SmallVectorImpl<uint8_t> &UncompressedBuffer,
189                        size_t UncompressedSize) {
190   UncompressedBuffer.resize_for_overwrite(UncompressedSize);
191   Error E =
192       zstd::uncompress(Input, UncompressedBuffer.data(), UncompressedSize);
193   if (UncompressedSize < UncompressedBuffer.size())
194     UncompressedBuffer.truncate(UncompressedSize);
195   return E;
196 }
197 
198 #else
199 bool zstd::isAvailable() { return false; }
200 void zstd::compress(ArrayRef<uint8_t> Input,
201                     SmallVectorImpl<uint8_t> &CompressedBuffer, int Level) {
202   llvm_unreachable("zstd::compress is unavailable");
203 }
204 Error zstd::uncompress(ArrayRef<uint8_t> Input, uint8_t *UncompressedBuffer,
205                        size_t &UncompressedSize) {
206   llvm_unreachable("zstd::uncompress is unavailable");
207 }
208 Error zstd::uncompress(ArrayRef<uint8_t> Input,
209                        SmallVectorImpl<uint8_t> &UncompressedBuffer,
210                        size_t UncompressedSize) {
211   llvm_unreachable("zstd::uncompress is unavailable");
212 }
213 #endif
214