Line data Source code
1 : #ifndef THINGER_UTIL_COMPRESSION_HPP
2 : #define THINGER_UTIL_COMPRESSION_HPP
3 :
4 : #include <string>
5 : #include <optional>
6 : #include <zlib.h>
7 :
8 : namespace thinger::util {
9 :
10 : class gzip {
11 : public:
12 : // Compress string to gzip format
13 48 : static std::optional<std::string> compress(const std::string& data) {
14 48 : z_stream strm{};
15 : // windowBits = 15 + 16 enables gzip encoding
16 48 : if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
17 0 : return std::nullopt;
18 : }
19 :
20 48 : strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
21 48 : strm.avail_in = static_cast<uInt>(data.size());
22 :
23 48 : std::string result;
24 48 : result.resize(deflateBound(&strm, data.size()));
25 :
26 48 : strm.next_out = reinterpret_cast<Bytef*>(result.data());
27 48 : strm.avail_out = static_cast<uInt>(result.size());
28 :
29 48 : int ret = deflate(&strm, Z_FINISH);
30 48 : deflateEnd(&strm);
31 :
32 48 : if (ret != Z_STREAM_END) {
33 0 : return std::nullopt;
34 : }
35 :
36 48 : result.resize(strm.total_out);
37 48 : return result;
38 48 : }
39 :
40 : // Decompress gzip data
41 48 : static std::optional<std::string> decompress(const std::string& data) {
42 48 : z_stream strm{};
43 : // windowBits = 15 + 16 enables gzip decoding
44 48 : if (inflateInit2(&strm, 15 + 16) != Z_OK) {
45 0 : return std::nullopt;
46 : }
47 :
48 48 : strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
49 48 : strm.avail_in = static_cast<uInt>(data.size());
50 :
51 48 : std::string result;
52 : char buffer[16384];
53 :
54 : int ret;
55 : do {
56 96 : strm.next_out = reinterpret_cast<Bytef*>(buffer);
57 96 : strm.avail_out = sizeof(buffer);
58 :
59 96 : ret = inflate(&strm, Z_NO_FLUSH);
60 96 : if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
61 0 : inflateEnd(&strm);
62 0 : return std::nullopt;
63 : }
64 :
65 96 : result.append(buffer, sizeof(buffer) - strm.avail_out);
66 96 : } while (ret != Z_STREAM_END);
67 :
68 48 : inflateEnd(&strm);
69 48 : return result;
70 48 : }
71 :
72 : // Check if data might be gzip compressed (by checking magic bytes)
73 : static bool is_gzip(const std::string& data) {
74 : return data.size() >= 2 &&
75 : static_cast<unsigned char>(data[0]) == 0x1f &&
76 : static_cast<unsigned char>(data[1]) == 0x8b;
77 : }
78 : };
79 :
80 : class deflate {
81 : public:
82 : // Compress string using deflate (zlib format)
83 3 : static std::optional<std::string> compress(const std::string& data) {
84 3 : z_stream strm{};
85 : // windowBits = 15 for zlib format
86 3 : if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) {
87 0 : return std::nullopt;
88 : }
89 :
90 3 : strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
91 3 : strm.avail_in = static_cast<uInt>(data.size());
92 :
93 3 : std::string result;
94 3 : result.resize(deflateBound(&strm, data.size()));
95 :
96 3 : strm.next_out = reinterpret_cast<Bytef*>(result.data());
97 3 : strm.avail_out = static_cast<uInt>(result.size());
98 :
99 3 : int ret = ::deflate(&strm, Z_FINISH);
100 3 : deflateEnd(&strm);
101 :
102 3 : if (ret != Z_STREAM_END) {
103 0 : return std::nullopt;
104 : }
105 :
106 3 : result.resize(strm.total_out);
107 3 : return result;
108 3 : }
109 :
110 : // Decompress deflate data (zlib format)
111 3 : static std::optional<std::string> decompress(const std::string& data) {
112 3 : z_stream strm{};
113 3 : if (inflateInit(&strm) != Z_OK) {
114 0 : return std::nullopt;
115 : }
116 :
117 3 : strm.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data()));
118 3 : strm.avail_in = static_cast<uInt>(data.size());
119 :
120 3 : std::string result;
121 : char buffer[16384];
122 :
123 : int ret;
124 : do {
125 3 : strm.next_out = reinterpret_cast<Bytef*>(buffer);
126 3 : strm.avail_out = sizeof(buffer);
127 :
128 3 : ret = inflate(&strm, Z_NO_FLUSH);
129 3 : if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
130 0 : inflateEnd(&strm);
131 0 : return std::nullopt;
132 : }
133 :
134 3 : result.append(buffer, sizeof(buffer) - strm.avail_out);
135 3 : } while (ret != Z_STREAM_END);
136 :
137 3 : inflateEnd(&strm);
138 3 : return result;
139 3 : }
140 : };
141 :
142 : } // namespace thinger::util
143 :
144 : #endif // THINGER_UTIL_COMPRESSION_HPP
|