1*f485bd44SEd Tanous // SPDX-License-Identifier: Apache-2.0
2*f485bd44SEd Tanous // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*f485bd44SEd Tanous
4*f485bd44SEd Tanous #include "zstd_compressor.hpp"
5*f485bd44SEd Tanous
6*f485bd44SEd Tanous #include "logging.hpp"
7*f485bd44SEd Tanous
8*f485bd44SEd Tanous #include <boost/asio/buffer.hpp>
9*f485bd44SEd Tanous
10*f485bd44SEd Tanous #include <cstdint>
11*f485bd44SEd Tanous
12*f485bd44SEd Tanous #ifdef HAVE_ZSTD
13*f485bd44SEd Tanous #include <zstd.h>
14*f485bd44SEd Tanous #endif
15*f485bd44SEd Tanous
16*f485bd44SEd Tanous #include <cstddef>
17*f485bd44SEd Tanous #include <optional>
18*f485bd44SEd Tanous #include <span>
19*f485bd44SEd Tanous
20*f485bd44SEd Tanous namespace bmcweb
21*f485bd44SEd Tanous {
22*f485bd44SEd Tanous
init(size_t sourceSize)23*f485bd44SEd Tanous bool ZstdCompressor::init([[maybe_unused]] size_t sourceSize)
24*f485bd44SEd Tanous {
25*f485bd44SEd Tanous #ifdef HAVE_ZSTD
26*f485bd44SEd Tanous if (cctx != nullptr)
27*f485bd44SEd Tanous {
28*f485bd44SEd Tanous BMCWEB_LOG_ERROR("ZstdCompressor already initialized");
29*f485bd44SEd Tanous return false;
30*f485bd44SEd Tanous }
31*f485bd44SEd Tanous cctx = ZSTD_createCCtx();
32*f485bd44SEd Tanous if (cctx == nullptr)
33*f485bd44SEd Tanous {
34*f485bd44SEd Tanous BMCWEB_LOG_ERROR("Failed to create ZstdCompressor");
35*f485bd44SEd Tanous return false;
36*f485bd44SEd Tanous }
37*f485bd44SEd Tanous
38*f485bd44SEd Tanous // 3 is the default compression level for zstd, but set it explicitly
39*f485bd44SEd Tanous // so we can tune later if needed]
40*f485bd44SEd Tanous size_t ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_compressionLevel, 3);
41*f485bd44SEd Tanous if (ZSTD_isError(ret) != 0U)
42*f485bd44SEd Tanous {
43*f485bd44SEd Tanous BMCWEB_LOG_ERROR("Failed to set compression level {}:{}", ret,
44*f485bd44SEd Tanous ZSTD_getErrorName(ret));
45*f485bd44SEd Tanous return false;
46*f485bd44SEd Tanous }
47*f485bd44SEd Tanous ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
48*f485bd44SEd Tanous if (ZSTD_isError(ret) != 0U)
49*f485bd44SEd Tanous {
50*f485bd44SEd Tanous BMCWEB_LOG_ERROR("Failed to set checksum flag {}:{}", ret,
51*f485bd44SEd Tanous ZSTD_getErrorName(ret));
52*f485bd44SEd Tanous return false;
53*f485bd44SEd Tanous }
54*f485bd44SEd Tanous ret = ZSTD_CCtx_setParameter(cctx, ZSTD_c_contentSizeFlag, 1);
55*f485bd44SEd Tanous if (ZSTD_isError(ret) != 0U)
56*f485bd44SEd Tanous {
57*f485bd44SEd Tanous BMCWEB_LOG_ERROR("Failed to set contentsize flag {}:{}", ret,
58*f485bd44SEd Tanous ZSTD_getErrorName(ret));
59*f485bd44SEd Tanous return false;
60*f485bd44SEd Tanous }
61*f485bd44SEd Tanous
62*f485bd44SEd Tanous ret = ZSTD_CCtx_setPledgedSrcSize(cctx, sourceSize);
63*f485bd44SEd Tanous if (ZSTD_isError(ret) != 0U)
64*f485bd44SEd Tanous {
65*f485bd44SEd Tanous BMCWEB_LOG_ERROR("Failed to set pledged src size {}:{}", ret,
66*f485bd44SEd Tanous ZSTD_getErrorName(ret));
67*f485bd44SEd Tanous return false;
68*f485bd44SEd Tanous }
69*f485bd44SEd Tanous return true;
70*f485bd44SEd Tanous #else
71*f485bd44SEd Tanous BMCWEB_LOG_CRITICAL("ZstdCompressor not compiled in");
72*f485bd44SEd Tanous return false;
73*f485bd44SEd Tanous #endif
74*f485bd44SEd Tanous }
75*f485bd44SEd Tanous
compress(std::span<const uint8_t> buffIn,bool more)76*f485bd44SEd Tanous std::optional<std::span<const uint8_t>> ZstdCompressor::compress(
77*f485bd44SEd Tanous [[maybe_unused]] std::span<const uint8_t> buffIn,
78*f485bd44SEd Tanous [[maybe_unused]] bool more)
79*f485bd44SEd Tanous {
80*f485bd44SEd Tanous #ifdef HAVE_ZSTD
81*f485bd44SEd Tanous if (cctx == nullptr)
82*f485bd44SEd Tanous {
83*f485bd44SEd Tanous BMCWEB_LOG_ERROR("ZstdCompressor not initialized");
84*f485bd44SEd Tanous return std::nullopt;
85*f485bd44SEd Tanous }
86*f485bd44SEd Tanous compressionBuf.clear();
87*f485bd44SEd Tanous ZSTD_inBuffer input = {buffIn.data(), buffIn.size(), 0};
88*f485bd44SEd Tanous
89*f485bd44SEd Tanous while (true)
90*f485bd44SEd Tanous {
91*f485bd44SEd Tanous constexpr size_t frameSize = 4096;
92*f485bd44SEd Tanous auto buffer = compressionBuf.prepare(frameSize);
93*f485bd44SEd Tanous ZSTD_outBuffer output = {buffer.data(), buffer.size(), 0};
94*f485bd44SEd Tanous ZSTD_EndDirective dir = ZSTD_e_end;
95*f485bd44SEd Tanous if (more)
96*f485bd44SEd Tanous {
97*f485bd44SEd Tanous dir = ZSTD_e_continue;
98*f485bd44SEd Tanous }
99*f485bd44SEd Tanous size_t remaining = ZSTD_compressStream2(cctx, &output, &input, dir);
100*f485bd44SEd Tanous if (ZSTD_isError(remaining) != 0U)
101*f485bd44SEd Tanous {
102*f485bd44SEd Tanous return std::nullopt;
103*f485bd44SEd Tanous }
104*f485bd44SEd Tanous compressionBuf.commit(output.pos);
105*f485bd44SEd Tanous if (more)
106*f485bd44SEd Tanous {
107*f485bd44SEd Tanous if (input.pos == input.size)
108*f485bd44SEd Tanous {
109*f485bd44SEd Tanous break;
110*f485bd44SEd Tanous }
111*f485bd44SEd Tanous }
112*f485bd44SEd Tanous else
113*f485bd44SEd Tanous {
114*f485bd44SEd Tanous if (remaining == 0)
115*f485bd44SEd Tanous {
116*f485bd44SEd Tanous break;
117*f485bd44SEd Tanous }
118*f485bd44SEd Tanous }
119*f485bd44SEd Tanous }
120*f485bd44SEd Tanous boost::asio::const_buffer buf = compressionBuf.cdata();
121*f485bd44SEd Tanous return std::span(static_cast<const uint8_t*>(buf.data()), buf.size());
122*f485bd44SEd Tanous #else
123*f485bd44SEd Tanous BMCWEB_LOG_CRITICAL("Attempt to compress, but libzstd not enabled");
124*f485bd44SEd Tanous
125*f485bd44SEd Tanous return std::nullopt;
126*f485bd44SEd Tanous #endif
127*f485bd44SEd Tanous }
128*f485bd44SEd Tanous
~ZstdCompressor()129*f485bd44SEd Tanous ZstdCompressor::~ZstdCompressor()
130*f485bd44SEd Tanous {
131*f485bd44SEd Tanous #ifdef HAVE_ZSTD
132*f485bd44SEd Tanous ZSTD_freeCCtx(cctx);
133*f485bd44SEd Tanous #endif
134*f485bd44SEd Tanous }
135*f485bd44SEd Tanous } // namespace bmcweb
136