xref: /openbmc/bmcweb/http/zstd_decompressor.cpp (revision b25390694f7015224fbf02de247faec4c50429aa)
1*b2539069SEd Tanous // SPDX-License-Identifier: Apache-2.0
2*b2539069SEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*b2539069SEd Tanous 
4*b2539069SEd Tanous #include "zstd_decompressor.hpp"
5*b2539069SEd Tanous 
6*b2539069SEd Tanous #include "logging.hpp"
7*b2539069SEd Tanous 
8*b2539069SEd Tanous #ifdef HAVE_ZSTD
9*b2539069SEd Tanous #include <zstd.h>
10*b2539069SEd Tanous #endif
11*b2539069SEd Tanous #include <boost/asio/buffer.hpp>
12*b2539069SEd Tanous 
13*b2539069SEd Tanous #include <cstddef>
14*b2539069SEd Tanous #include <optional>
15*b2539069SEd Tanous 
16*b2539069SEd Tanous #ifdef HAVE_ZSTD
ZstdDecompressor()17*b2539069SEd Tanous ZstdDecompressor::ZstdDecompressor() : dctx(ZSTD_createDStream())
18*b2539069SEd Tanous {
19*b2539069SEd Tanous     ZSTD_initDStream(dctx);
20*b2539069SEd Tanous }
21*b2539069SEd Tanous #else
ZstdDecompressor()22*b2539069SEd Tanous ZstdDecompressor::ZstdDecompressor() {};
23*b2539069SEd Tanous #endif
24*b2539069SEd Tanous 
decompress(boost::asio::const_buffer buffIn)25*b2539069SEd Tanous std::optional<boost::asio::const_buffer> ZstdDecompressor::decompress(
26*b2539069SEd Tanous     [[maybe_unused]] boost::asio::const_buffer buffIn)
27*b2539069SEd Tanous {
28*b2539069SEd Tanous #ifdef HAVE_ZSTD
29*b2539069SEd Tanous     compressionBuf.clear();
30*b2539069SEd Tanous     ZSTD_inBuffer input = {buffIn.data(), buffIn.size(), 0};
31*b2539069SEd Tanous 
32*b2539069SEd Tanous     // Note, this loop is prone to compression bombs, decompressing chunks that
33*b2539069SEd Tanous     // appear very small, but decompress to be very large, given that they're
34*b2539069SEd Tanous     // highly decompressible. This algorithm assumes that at this time, the
35*b2539069SEd Tanous     // whole file will fit in ram.
36*b2539069SEd Tanous     while (input.pos != input.size)
37*b2539069SEd Tanous     {
38*b2539069SEd Tanous         constexpr size_t frameSize = 4096;
39*b2539069SEd Tanous         auto buffer = compressionBuf.prepare(frameSize);
40*b2539069SEd Tanous         ZSTD_outBuffer output = {buffer.data(), buffer.size(), 0};
41*b2539069SEd Tanous         const size_t ret = ZSTD_decompressStream(dctx, &output, &input);
42*b2539069SEd Tanous         if (ZSTD_isError(ret) != 0)
43*b2539069SEd Tanous         {
44*b2539069SEd Tanous             BMCWEB_LOG_ERROR("Decompression Failed with code {}:{}", ret,
45*b2539069SEd Tanous                              ZSTD_getErrorName(ret));
46*b2539069SEd Tanous             return std::nullopt;
47*b2539069SEd Tanous         }
48*b2539069SEd Tanous         compressionBuf.commit(output.pos);
49*b2539069SEd Tanous     }
50*b2539069SEd Tanous     return compressionBuf.cdata();
51*b2539069SEd Tanous #else
52*b2539069SEd Tanous     BMCWEB_LOG_CRITICAL("Attempt to decompress, but libzstd not enabled");
53*b2539069SEd Tanous 
54*b2539069SEd Tanous     return std::nullopt;
55*b2539069SEd Tanous #endif
56*b2539069SEd Tanous }
57*b2539069SEd Tanous 
~ZstdDecompressor()58*b2539069SEd Tanous ZstdDecompressor::~ZstdDecompressor()
59*b2539069SEd Tanous {
60*b2539069SEd Tanous #ifdef HAVE_ZSTD
61*b2539069SEd Tanous     ZSTD_freeDStream(dctx);
62*b2539069SEd Tanous #endif
63*b2539069SEd Tanous }
64