LCOV - code coverage report
Current view: top level - util - compression.hpp (source / functions) Coverage Total Hit
Test: coverage_filtered.info Lines: 85.7 % 70 60
Test Date: 2026-02-20 15:38:22 Functions: 100.0 % 4 4

            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
        

Generated by: LCOV version 2.0-1