summaryrefslogtreecommitdiff
path: root/http/zstd_decompressor.cpp
blob: aa4239240f0130c04e3b41fd7eef42f61738bfaa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright OpenBMC Authors

#include "zstd_decompressor.hpp"

#include "logging.hpp"

#ifdef HAVE_ZSTD
#include <zstd.h>
#endif
#include <boost/asio/buffer.hpp>

#include <cstddef>
#include <optional>

#ifdef HAVE_ZSTD
ZstdDecompressor::ZstdDecompressor() : dctx(ZSTD_createDStream())
{
    ZSTD_initDStream(dctx);
}
#else
ZstdDecompressor::ZstdDecompressor() {};
#endif

std::optional<boost::asio::const_buffer> ZstdDecompressor::decompress(
    [[maybe_unused]] boost::asio::const_buffer buffIn)
{
#ifdef HAVE_ZSTD
    compressionBuf.clear();
    ZSTD_inBuffer input = {buffIn.data(), buffIn.size(), 0};

    // Note, this loop is prone to compression bombs, decompressing chunks that
    // appear very small, but decompress to be very large, given that they're
    // highly decompressible. This algorithm assumes that at this time, the
    // whole file will fit in ram.
    while (input.pos != input.size)
    {
        constexpr size_t frameSize = 4096;
        auto buffer = compressionBuf.prepare(frameSize);
        ZSTD_outBuffer output = {buffer.data(), buffer.size(), 0};
        const size_t ret = ZSTD_decompressStream(dctx, &output, &input);
        if (ZSTD_isError(ret) != 0)
        {
            BMCWEB_LOG_ERROR("Decompression Failed with code {}:{}", ret,
                             ZSTD_getErrorName(ret));
            return std::nullopt;
        }
        compressionBuf.commit(output.pos);
    }
    return compressionBuf.cdata();
#else
    BMCWEB_LOG_CRITICAL("Attempt to decompress, but libzstd not enabled");

    return std::nullopt;
#endif
}

ZstdDecompressor::~ZstdDecompressor()
{
#ifdef HAVE_ZSTD
    ZSTD_freeDStream(dctx);
#endif
}