#include #include #include "fiko/io/web_io.hpp" #include "miniz.h" // ============================================================================ // gzip inflate (miniz raw deflate + elle gzip baslik/trailer). .FikoOp icin. // Streaming: cikti tampon parca parca buyutulur (12.6x sismeyi karsilar). // ============================================================================ namespace fiko::io { namespace { // gzip RFC 1952 baslik: 10 sabit bayt + opsiyonel FEXTRA/FNAME/FCOMMENT/FHCRC. // Basarili ise deflate govdesinin baslangic ofsetini dondurur; hata → 0 (ofset // 0 hicbir zaman gecerli govde degil cunku en az 10 baytlik baslik var). std::size_t gzipBaslikBoyu(const unsigned char* p, std::size_t n) { if (n < 18) return 0; // baslik(10)+min trailer(8) if (p[0] != 0x1F || p[1] != 0x8B) return 0; // magic if (p[2] != 0x08) return 0; // yalniz DEFLATE const unsigned char flg = p[3]; std::size_t o = 10; if (flg & 0x04) { // FEXTRA if (o + 2 > n) return 0; const std::size_t xlen = (std::size_t)p[o] | ((std::size_t)p[o + 1] << 8); o += 2 + xlen; } if (flg & 0x08) // FNAME (NUL-sonlu) while (o < n && p[o] != 0) ++o, void(); if (flg & 0x08) { if (o >= n) return 0; ++o; } if (flg & 0x10) // FCOMMENT (NUL-sonlu) while (o < n && p[o] != 0) ++o, void(); if (flg & 0x10) { if (o >= n) return 0; ++o; } if (flg & 0x02) o += 2; // FHCRC if (o + 8 > n) return 0; // trailer'a yer kalmali return o; } } // namespace bool gzipCoz(const std::string& girdi, std::string& cikti, std::string& hata) { cikti.clear(); const auto* p = reinterpret_cast(girdi.data()); const std::size_t n = girdi.size(); // gzip degil → duz metin (eski/sikistirilmamis .FikoOp uyumlulugu). if (n < 2 || p[0] != 0x1F || p[1] != 0x8B) { cikti = girdi; return true; } const std::size_t basH = gzipBaslikBoyu(p, n); if (basH == 0) { hata = "Bozuk gzip basligi (.FikoOp)"; return false; } // ISIZE (son 4 bayt, little-endian) = acilmis boyut mod 2^32 → on-rezervasyon. const std::uint32_t isize = (std::uint32_t)p[n - 4] | ((std::uint32_t)p[n - 3] << 8) | ((std::uint32_t)p[n - 2] << 16) | ((std::uint32_t)p[n - 1] << 24); mz_stream s{}; if (mz_inflateInit2(&s, -MZ_DEFAULT_WINDOW_BITS) != MZ_OK) { hata = "gzip inflate baslatilamadi"; return false; } s.next_in = p + basH; s.avail_in = (unsigned)(n - basH - 8); // trailer haric ham deflate // Tahmini boyut (ISIZE ipucu; 0/tasma ise girdi*12 ile basla). std::size_t rez = isize ? (std::size_t)isize : girdi.size() * 12; if (rez < 65536) rez = 65536; cikti.resize(rez); int rc = MZ_OK; while (true) { s.next_out = reinterpret_cast(cikti.data()) + s.total_out; s.avail_out = (unsigned)(cikti.size() - s.total_out); rc = mz_inflate(&s, MZ_NO_FLUSH); if (rc == MZ_STREAM_END) break; if (rc == MZ_OK || rc == MZ_BUF_ERROR) { if (s.avail_out == 0) { // tampon doldu → 1.5x buyut cikti.resize(cikti.size() + cikti.size() / 2 + 65536); continue; } if (rc == MZ_BUF_ERROR) { hata = "gzip: girdi beklenmedik sekilde bitti"; break; } continue; // MZ_OK, cikti hala var → devam } hata = "gzip inflate hatasi (kod " + std::to_string(rc) + ")"; break; } const std::size_t uzunluk = s.total_out; mz_inflateEnd(&s); if (rc != MZ_STREAM_END && hata.empty()) hata = "gzip: eksik akis"; if (!hata.empty()) { cikti.clear(); return false; } cikti.resize(uzunluk); return true; } } // namespace fiko::io