|
11 | 11 | //===----------------------------------------------------------------------===// |
12 | 12 |
|
13 | 13 | #include "llvm/Support/Compression.h" |
| 14 | +#include "llvm/ADT/ScopeExit.h" |
14 | 15 | #include "llvm/ADT/SmallVector.h" |
15 | 16 | #include "llvm/ADT/StringRef.h" |
16 | 17 | #include "llvm/Config/config.h" |
@@ -55,6 +56,18 @@ void compression::compress(Params P, ArrayRef<uint8_t> Input, |
55 | 56 | } |
56 | 57 | } |
57 | 58 |
|
| 59 | +void compression::compressToStream(Params P, ArrayRef<uint8_t> Input, |
| 60 | + raw_ostream &OS) { |
| 61 | + switch (P.format) { |
| 62 | + case compression::Format::Zlib: |
| 63 | + zlib::compressToStream(Input, OS, P.level); |
| 64 | + break; |
| 65 | + case compression::Format::Zstd: |
| 66 | + zstd::compressToStream(Input, OS, P.level, P.zstdEnableLdm); |
| 67 | + break; |
| 68 | + } |
| 69 | +} |
| 70 | + |
58 | 71 | Error compression::decompress(DebugCompressionType T, ArrayRef<uint8_t> Input, |
59 | 72 | uint8_t *Output, size_t UncompressedSize) { |
60 | 73 | switch (formatFor(T)) { |
@@ -120,6 +133,49 @@ void zlib::compress(ArrayRef<uint8_t> Input, |
120 | 133 | CompressedBuffer.truncate(CompressedSize); |
121 | 134 | } |
122 | 135 |
|
| 136 | +void zlib::compressToStream(ArrayRef<uint8_t> Input, raw_ostream &OS, |
| 137 | + int Level) { |
| 138 | + // Allocate a fixed size buffer to hold the output. |
| 139 | + constexpr size_t OutBufferSize = 4096; |
| 140 | + auto OutBuffer = std::make_unique<Bytef[]>(OutBufferSize); |
| 141 | + |
| 142 | + z_stream ZStream; |
| 143 | + ZStream.zalloc = Z_NULL; |
| 144 | + ZStream.zfree = Z_NULL; |
| 145 | + ZStream.opaque = Z_NULL; |
| 146 | + |
| 147 | + int ZErr = deflateInit(&ZStream, Level); |
| 148 | + if (ZErr != Z_OK) |
| 149 | + report_bad_alloc_error("Failed to create ZStream"); |
| 150 | + |
| 151 | + // Ensure that the z_stream is cleaned up on all exit paths. |
| 152 | + auto DeflateEndOnExit = make_scope_exit([&]() { deflateEnd(&ZStream); }); |
| 153 | + |
| 154 | + ZStream.next_in = |
| 155 | + reinterpret_cast<Bytef *>(const_cast<uint8_t *>(Input.data())); |
| 156 | + ZStream.avail_in = Input.size(); |
| 157 | + |
| 158 | + // Repeatedly deflate into the output buffer and flush it into the |
| 159 | + // output stream. Repeat until we have drained the entire compression |
| 160 | + // state. |
| 161 | + while (ZErr != Z_STREAM_END) { |
| 162 | + ZStream.next_out = OutBuffer.get(); |
| 163 | + ZStream.avail_out = OutBufferSize; |
| 164 | + |
| 165 | + ZErr = deflate(&ZStream, Z_FINISH); |
| 166 | + if (ZErr == Z_STREAM_ERROR || ZErr == Z_BUF_ERROR) |
| 167 | + report_fatal_error(convertZlibCodeToString(ZErr)); |
| 168 | + |
| 169 | + // Tell MemorySanitizer that zlib output buffer is fully initialized. |
| 170 | + // This avoids a false report when running LLVM with uninstrumented ZLib. |
| 171 | + __msan_unpoison(OutputBuffer.data(), OutBufferSize - ZStream.avail_out); |
| 172 | + |
| 173 | + if (ZStream.avail_out < OutBufferSize) |
| 174 | + OS.write(reinterpret_cast<char *>(OutBuffer.get()), |
| 175 | + OutBufferSize - ZStream.avail_out); |
| 176 | + } |
| 177 | +} |
| 178 | + |
123 | 179 | Error zlib::decompress(ArrayRef<uint8_t> Input, uint8_t *Output, |
124 | 180 | size_t &UncompressedSize) { |
125 | 181 | int Res = ::uncompress((Bytef *)Output, (uLongf *)&UncompressedSize, |
@@ -148,6 +204,10 @@ void zlib::compress(ArrayRef<uint8_t> Input, |
148 | 204 | SmallVectorImpl<uint8_t> &CompressedBuffer, int Level) { |
149 | 205 | llvm_unreachable("zlib::compress is unavailable"); |
150 | 206 | } |
| 207 | +void zlib::compressToStream(ArrayRef<uint8_t> Input, raw_ostream &OS, |
| 208 | + int Level = DefaultCompression) { |
| 209 | + llvm_unreachable("zlib::compressToStream is unavailable"); |
| 210 | +} |
151 | 211 | Error zlib::decompress(ArrayRef<uint8_t> Input, uint8_t *UncompressedBuffer, |
152 | 212 | size_t &UncompressedSize) { |
153 | 213 | llvm_unreachable("zlib::decompress is unavailable"); |
@@ -201,6 +261,51 @@ void zstd::compress(ArrayRef<uint8_t> Input, |
201 | 261 | CompressedBuffer.truncate(CompressedSize); |
202 | 262 | } |
203 | 263 |
|
| 264 | +void zstd::compressToStream(ArrayRef<uint8_t> Input, raw_ostream &OS, int Level, |
| 265 | + bool EnableLdm) { |
| 266 | + // Allocate a buffer to hold the output. |
| 267 | + size_t OutBufferSize = ZSTD_CStreamOutSize(); |
| 268 | + auto OutBuffer = std::make_unique<char[]>(OutBufferSize); |
| 269 | + |
| 270 | + ZSTD_CStream *CStream = ZSTD_createCStream(); |
| 271 | + if (!CStream) |
| 272 | + report_bad_alloc_error("Failed to create ZSTD_CCtx"); |
| 273 | + |
| 274 | + // Ensure that the ZSTD_CStream is cleaned up on all exit paths. |
| 275 | + auto FreeCStreamOnExit = |
| 276 | + make_scope_exit([=]() { ZSTD_freeCStream(CStream); }); |
| 277 | + |
| 278 | + if (ZSTD_isError(ZSTD_CCtx_setParameter( |
| 279 | + CStream, ZSTD_c_enableLongDistanceMatching, EnableLdm ? 1 : 0))) { |
| 280 | + report_bad_alloc_error("Failed to set ZSTD_c_enableLongDistanceMatching"); |
| 281 | + } |
| 282 | + |
| 283 | + if (ZSTD_isError( |
| 284 | + ZSTD_CCtx_setParameter(CStream, ZSTD_c_compressionLevel, Level))) { |
| 285 | + report_bad_alloc_error("Failed to set ZSTD_c_compressionLevel"); |
| 286 | + } |
| 287 | + |
| 288 | + ZSTD_inBuffer ZInput = {Input.data(), Input.size(), 0}; |
| 289 | + |
| 290 | + // Repeatedly compress into the output buffer and flush it into the |
| 291 | + // output stream. Repeat until we have drained the entire compression |
| 292 | + // state. |
| 293 | + size_t ZRet; |
| 294 | + do { |
| 295 | + ZSTD_outBuffer ZOutput = {OutBuffer.get(), OutBufferSize, 0}; |
| 296 | + ZRet = ZSTD_compressStream2(CStream, &ZOutput, &ZInput, ZSTD_e_end); |
| 297 | + if (ZSTD_isError(ZRet)) |
| 298 | + report_fatal_error(ZSTD_getErrorName(ZRet)); |
| 299 | + |
| 300 | + // Tell MemorySanitizer that zstd output buffer is fully initialized. |
| 301 | + // This avoids a false report when running LLVM with uninstrumented ZStd. |
| 302 | + __msan_unpoison(OutputBuffer.data(), ZOutput.pos); |
| 303 | + |
| 304 | + if (ZOutput.pos > 0) |
| 305 | + OS.write(reinterpret_cast<char *>(OutBuffer.get()), ZOutput.pos); |
| 306 | + } while (ZRet != 0); |
| 307 | +} |
| 308 | + |
204 | 309 | Error zstd::decompress(ArrayRef<uint8_t> Input, uint8_t *Output, |
205 | 310 | size_t &UncompressedSize) { |
206 | 311 | const size_t Res = ::ZSTD_decompress( |
@@ -231,6 +336,11 @@ void zstd::compress(ArrayRef<uint8_t> Input, |
231 | 336 | bool EnableLdm) { |
232 | 337 | llvm_unreachable("zstd::compress is unavailable"); |
233 | 338 | } |
| 339 | +void zstd::compressToStream(ArrayRef<uint8_t> Input, raw_ostream &OS, |
| 340 | + int Level = DefaultCompression, |
| 341 | + bool EnableLdm = false) { |
| 342 | + llvm_unreachable("zstd::compressToStream is unavailable"); |
| 343 | +} |
234 | 344 | Error zstd::decompress(ArrayRef<uint8_t> Input, uint8_t *Output, |
235 | 345 | size_t &UncompressedSize) { |
236 | 346 | llvm_unreachable("zstd::decompress is unavailable"); |
|
0 commit comments