From 3cfcca5fc6e1e9aa22b69edb8daa5c0735d05d10 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 16 Dec 2025 11:50:08 +0300 Subject: [PATCH 01/39] f$deflate_init --- runtime-light/stdlib/zlib/deflatecontext.h | 21 ++++ runtime-light/stdlib/zlib/zlib-functions.cpp | 110 +++++++++++++++++++ runtime-light/stdlib/zlib/zlib-functions.h | 6 + 3 files changed, 137 insertions(+) create mode 100644 runtime-light/stdlib/zlib/deflatecontext.h diff --git a/runtime-light/stdlib/zlib/deflatecontext.h b/runtime-light/stdlib/zlib/deflatecontext.h new file mode 100644 index 0000000000..d085787aee --- /dev/null +++ b/runtime-light/stdlib/zlib/deflatecontext.h @@ -0,0 +1,21 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2024 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include "zlib/zlib.h" + +#include "runtime-common/core/class-instance/refcountable-php-classes.h" +#include "runtime-common/stdlib/visitors/dummy-visitor-methods.h" + +struct C$DeflateContext : public refcountable_php_classes, private DummyVisitorMethods { + C$DeflateContext() = default; + using DummyVisitorMethods::accept; + + ~C$DeflateContext() { + deflateEnd(&stream); + } + + z_stream stream{}; +}; diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index d0783e0bee..a8969aa141 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -15,6 +15,7 @@ #include "zlib/zlib.h" #include "common/containers/final_action.h" +#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-common/core/runtime-core.h" #include "runtime-light/stdlib/diagnostics/logs.h" #include "runtime-light/stdlib/string/string-state.h" @@ -36,6 +37,21 @@ voidpf zlib_static_alloc(voidpf opaque, uInt items, uInt size) noexcept { void zlib_static_free([[maybe_unused]] voidpf opaque, [[maybe_unused]] voidpf address) noexcept {} +voidpf zlib_dynamic_alloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) noexcept { + auto* mem{kphp::memory::script::calloc(items, size)}; + if (mem == nullptr) [[unlikely]] { + kphp::log::warning("zlib dynamic alloc: can't allocate {} bytes", items * size); + } + return mem; +} + +void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept { + if (address == nullptr) [[unlikely]] { + return; + } + kphp::memory::script::free(address); +} + } // namespace namespace kphp::zlib { @@ -115,3 +131,97 @@ std::optional decode(std::span data, int64_t encoding) noexc } } // namespace kphp::zlib + +class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { + int32_t level{-1}; + int32_t memory{8}; + int32_t window{15}; + auto strategy{Z_DEFAULT_STRATEGY}; + auto extract_int_option{[](int32_t lbound, int32_t ubound, const array_iterator& option, int32_t& dst) noexcept { + const mixed& value = option.get_value(); + if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { + dst = value.as_int(); + return true; + } else { + kphp::log::warning("deflate_init() : option {} should be number between {}..{}", option.get_string_key().c_str(), lbound, ubound); + return false; + } + }}; + switch (encoding) { + case kphp::zlib::ENCODING_RAW: + case kphp::zlib::ENCODING_DEFLATE: + case kphp::zlib::ENCODING_GZIP: + break; + default: + kphp::log::warning("deflate_init() : encoding should be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_DEFLATE, ZLIB_ENCODING_GZIP"); + return {}; + } + for (const auto& option : options) { + mixed value; + if (!option.is_string_key()) { + kphp::log::warning("deflate_init() : unsupported option"); + return {}; + } + if (option.get_string_key() == string("level")) { + if (!extract_int_option(-1, 9, option, level)) { + return {}; + } + } else if (option.get_string_key() == string("memory")) { + if (!extract_int_option(1, 9, option, memory)) { + return {}; + } + } else if (option.get_string_key() == string("window")) { + if (!extract_int_option(8, 15, option, window)) { + return {}; + } + } else if (option.get_string_key() == string("strategy")) { + value = option.get_value(); + if (value.is_int()) { + switch (value.as_int()) { + case Z_FILTERED: + case Z_HUFFMAN_ONLY: + case Z_RLE: + case Z_FIXED: + case Z_DEFAULT_STRATEGY: + strategy = value.as_int(); + break; + default: + kphp::log::warning( + "deflate_init() : option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); + return {}; + } + } else { + kphp::log::warning("deflate_init() : option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); + return {}; + } + } else if (option.get_string_key() == string("dictionary")) { + kphp::log::warning("deflate_init() : option dictionary isn't supported yet"); + return {}; + } else { + kphp::log::warning("deflate_init() : unknown option name \"{}\"", option.get_string_key().c_str()); + return {}; + } + } + + class_instance context; + context.alloc(); + + z_stream* stream{std::addressof(context.get()->stream)}; + stream->zalloc = zlib_dynamic_alloc; + stream->zfree = zlib_dynamic_free; + stream->opaque = nullptr; + + if (encoding < 0) { + encoding += 15 - window; + } else { + encoding -= 15 - window; + } + + auto err{deflateInit2(stream, level, Z_DEFLATED, encoding, memory, strategy)}; + if (err != Z_OK) { + kphp::log::warning("deflate_init() : zlib error {}", zError(err)); + context.destroy(); + return {}; + } + return context; +} diff --git a/runtime-light/stdlib/zlib/zlib-functions.h b/runtime-light/stdlib/zlib/zlib-functions.h index 968a141f36..ed7514f2f1 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.h +++ b/runtime-light/stdlib/zlib/zlib-functions.h @@ -10,6 +10,7 @@ #include #include "runtime-common/core/runtime-core.h" +#include "runtime-light/stdlib/zlib/deflatecontext.h" namespace kphp::zlib { @@ -53,3 +54,8 @@ inline string f$gzdeflate(const string& data, int64_t level = kphp::zlib::MIN_CO inline string f$gzinflate(const string& data) noexcept { return kphp::zlib::decode({data.c_str(), static_cast(data.size())}, kphp::zlib::ENCODING_RAW).value_or(string{}); } + +class_instance f$deflate_init(int64_t encoding, const array& options = {}) noexcept; + +Optional f$deflate_add(const class_instance& context, const string& data, int64_t flush_type = Z_SYNC_FLUSH) noexcept; + From a276c4798376742bc37b80b03e931084ed5a9056 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 16 Dec 2025 13:22:51 +0300 Subject: [PATCH 02/39] f$deflate_add & tests --- .../kphp-light/stdlib/zlib-functions.txt | 22 +++---- runtime-light/stdlib/zlib/zlib-functions.cpp | 64 +++++++++++++++++-- tests/phpt/pk/016_gzip.php | 2 +- 3 files changed, 69 insertions(+), 19 deletions(-) diff --git a/builtin-functions/kphp-light/stdlib/zlib-functions.txt b/builtin-functions/kphp-light/stdlib/zlib-functions.txt index c340c48be7..68d5e2fdff 100644 --- a/builtin-functions/kphp-light/stdlib/zlib-functions.txt +++ b/builtin-functions/kphp-light/stdlib/zlib-functions.txt @@ -18,6 +18,15 @@ function gzdeflate ($str ::: string, $level ::: int = -1) ::: string; function gzinflate ($str ::: string) ::: string; +function deflate_init(int $encoding, array $options = []) ::: ?DeflateContext; + +function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH) ::: string | false; + +final class DeflateContext { + private function __construct() ::: DeflateContext; +} + + // ===== UNSUPPORTED ===== /** @kphp-extern-func-info stub generation-required */ @@ -36,16 +45,3 @@ define('ZLIB_HUFFMAN_ONLY', 2); define('ZLIB_RLE', 3); define('ZLIB_FIXED', 4); define('ZLIB_DEFAULT_STRATEGY', 0); - -/** @kphp-generate-stub-class */ -final class DeflateContext { - /** @kphp-extern-func-info stub generation-required */ - private function __construct() ::: DeflateContext; -} - -// todo: deflate_init php signature has type array instead mixed -/** @kphp-extern-func-info stub generation-required */ -function deflate_init(int $encoding, mixed $options = []) ::: ?DeflateContext; -/** @kphp-extern-func-info stub generation-required */ -function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZLIB_SYNC_FLUSH) ::: string | false; - diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index a8969aa141..7fdd868739 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -162,19 +162,19 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, const string& data, int64_t flush_type) noexcept { + switch (flush_type) { + case Z_BLOCK: + case Z_NO_FLUSH: + case Z_PARTIAL_FLUSH: + case Z_SYNC_FLUSH: + case Z_FULL_FLUSH: + case Z_FINISH: + break; + default: + kphp::log::warning( + "deflate_add() : flush type should be one of ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_FINISH, ZLIB_BLOCK, ZLIB_TREES"); + return {}; + } + + z_stream* stream{std::addressof(context.get()->stream)}; + auto out_size{deflateBound(stream, data.size()) + 30}; + out_size = out_size < 64 ? 64 : out_size; + string out{static_cast(out_size), false}; + stream->next_in = const_cast(reinterpret_cast(data.c_str())); + stream->next_out = reinterpret_cast(out.buffer()); + stream->avail_in = data.size(); + stream->avail_out = out_size; + + auto status{Z_OK}; + uint64_t buffer_used{}; + do { + if (stream->avail_out == 0) { + out.reserve_at_least(out_size + 64); + out_size += 64; + stream->avail_out = 64; + stream->next_out = reinterpret_cast(std::next(out.buffer(), buffer_used)); + } + status = deflate(stream, flush_type); + buffer_used = out_size - stream->avail_out; + } while (status == Z_OK && stream->avail_out == 0); + + std::ptrdiff_t len{}; + switch (status) { + case Z_OK: + len = std::distance(reinterpret_cast(out.buffer()), stream->next_out); + out.shrink(len); + return out; + case Z_STREAM_END: + len = std::distance(reinterpret_cast(out.buffer()), stream->next_out); + deflateReset(stream); + out.shrink(len); + return out; + default: + kphp::log::warning("deflate_add() : zlib error {}", zError(status)); + return {}; + } +} diff --git a/tests/phpt/pk/016_gzip.php b/tests/phpt/pk/016_gzip.php index 93e7c980a7..9032555ed8 100644 --- a/tests/phpt/pk/016_gzip.php +++ b/tests/phpt/pk/016_gzip.php @@ -1,4 +1,4 @@ -@ok k2_skip +@ok Date: Tue, 16 Dec 2025 13:34:19 +0300 Subject: [PATCH 03/39] format --- runtime-light/stdlib/zlib/zlib-functions.h | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.h b/runtime-light/stdlib/zlib/zlib-functions.h index ed7514f2f1..d23dc1345f 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.h +++ b/runtime-light/stdlib/zlib/zlib-functions.h @@ -58,4 +58,3 @@ inline string f$gzinflate(const string& data) noexcept { class_instance f$deflate_init(int64_t encoding, const array& options = {}) noexcept; Optional f$deflate_add(const class_instance& context, const string& data, int64_t flush_type = Z_SYNC_FLUSH) noexcept; - From 0034e3b1480f5373bfa9c79a1c7fb040628653c6 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 12:37:17 +0300 Subject: [PATCH 04/39] noexcept --- runtime-light/stdlib/zlib/deflatecontext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/deflatecontext.h b/runtime-light/stdlib/zlib/deflatecontext.h index d085787aee..2f9e40b854 100644 --- a/runtime-light/stdlib/zlib/deflatecontext.h +++ b/runtime-light/stdlib/zlib/deflatecontext.h @@ -10,7 +10,7 @@ #include "runtime-common/stdlib/visitors/dummy-visitor-methods.h" struct C$DeflateContext : public refcountable_php_classes, private DummyVisitorMethods { - C$DeflateContext() = default; + C$DeflateContext() noexcept = default; using DummyVisitorMethods::accept; ~C$DeflateContext() { From 9ec93d86dae0f2a6be6604074085ee35230010db Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 12:38:41 +0300 Subject: [PATCH 05/39] std::addressof --- runtime-light/stdlib/zlib/deflatecontext.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/deflatecontext.h b/runtime-light/stdlib/zlib/deflatecontext.h index 2f9e40b854..5ea4f94a99 100644 --- a/runtime-light/stdlib/zlib/deflatecontext.h +++ b/runtime-light/stdlib/zlib/deflatecontext.h @@ -14,7 +14,7 @@ struct C$DeflateContext : public refcountable_php_classes, pri using DummyVisitorMethods::accept; ~C$DeflateContext() { - deflateEnd(&stream); + deflateEnd(std::addressof(stream)); } z_stream stream{}; From d94ed8921ca838cc2b2b61ede87a8582b05a9237 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 12:42:01 +0300 Subject: [PATCH 06/39] brace init --- runtime-light/stdlib/zlib/zlib-functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 7fdd868739..fcfa10b906 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -138,7 +138,7 @@ class_instance f$deflate_init(int64_t encoding, const array& option, int32_t& dst) noexcept { - const mixed& value = option.get_value(); + const mixed& value{option.get_value()}; if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { dst = value.as_int(); return true; From 53d5b08bb35361bc6636abab4f75af00848489ca Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 13:00:53 +0300 Subject: [PATCH 07/39] rewrite key comparing --- runtime-light/stdlib/zlib/zlib-functions.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index fcfa10b906..fd478ee67b 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -162,19 +162,20 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array Date: Thu, 18 Dec 2025 19:17:33 +0300 Subject: [PATCH 08/39] small fixes --- .../kphp-light/stdlib/zlib-functions.txt | 28 +++++++++---------- runtime-light/stdlib/zlib/zlib-functions.cpp | 18 ++++++------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/builtin-functions/kphp-light/stdlib/zlib-functions.txt b/builtin-functions/kphp-light/stdlib/zlib-functions.txt index 68d5e2fdff..2121dc2f37 100644 --- a/builtin-functions/kphp-light/stdlib/zlib-functions.txt +++ b/builtin-functions/kphp-light/stdlib/zlib-functions.txt @@ -6,6 +6,20 @@ define('ZLIB_ENCODING_RAW', -0x0f); define('ZLIB_ENCODING_DEFLATE', 0x0f); define('ZLIB_ENCODING_GZIP', 0x1f); +define('ZLIB_NO_FLUSH', 0); +define('ZLIB_PARTIAL_FLUSH', 1); +define('ZLIB_SYNC_FLUSH', 2); +define('ZLIB_FULL_FLUSH', 3); +define('ZLIB_FINISH', 4); +define('ZLIB_BLOCK', 5); +define('ZLIB_TREES', 6); + +define('ZLIB_FILTERED', 1); +define('ZLIB_HUFFMAN_ONLY', 2); +define('ZLIB_RLE', 3); +define('ZLIB_FIXED', 4); +define('ZLIB_DEFAULT_STRATEGY', 0); + function gzcompress ($str ::: string, $level ::: int = -1): string; function gzuncompress ($str ::: string): string; @@ -31,17 +45,3 @@ final class DeflateContext { /** @kphp-extern-func-info stub generation-required */ function md5_file ($s ::: string, $raw_output ::: bool = false) ::: string | false; - -define('ZLIB_NO_FLUSH', 0); -define('ZLIB_PARTIAL_FLUSH', 1); -define('ZLIB_SYNC_FLUSH', 2); -define('ZLIB_FULL_FLUSH', 3); -define('ZLIB_FINISH', 4); -define('ZLIB_BLOCK', 5); -define('ZLIB_TREES', 6); - -define('ZLIB_FILTERED', 1); -define('ZLIB_HUFFMAN_ONLY', 2); -define('ZLIB_RLE', 3); -define('ZLIB_FIXED', 4); -define('ZLIB_DEFAULT_STRATEGY', 0); diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index fd478ee67b..3660aec0ff 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -153,13 +153,13 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, break; default: kphp::log::warning( - "deflate_add() : flush type should be one of ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_FINISH, ZLIB_BLOCK, ZLIB_TREES"); + "flush type should be one of ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_FINISH, ZLIB_BLOCK, ZLIB_TREES"); return {}; } @@ -276,7 +276,7 @@ Optional f$deflate_add(const class_instance& context, out.shrink(len); return out; default: - kphp::log::warning("deflate_add() : zlib error {}", zError(status)); + kphp::log::warning("zlib error {}", zError(status)); return {}; } } From 9df0e8360da2ade257dfc284fbdc3bf95c92d754 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 19:19:36 +0300 Subject: [PATCH 09/39] constexpr --- runtime-light/stdlib/zlib/zlib-functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 3660aec0ff..3ace06039c 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -137,7 +137,7 @@ class_instance f$deflate_init(int64_t encoding, const array& option, int32_t& dst) noexcept { + constexpr auto extract_int_option{[](int32_t lbound, int32_t ubound, const array_iterator& option, int32_t& dst) noexcept { const mixed& value{option.get_value()}; if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { dst = value.as_int(); From a184c56b8d59c6dfaf6fad94ccebacd187ba8c34 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Thu, 18 Dec 2025 19:22:22 +0300 Subject: [PATCH 10/39] move statement to if --- runtime-light/stdlib/zlib/zlib-functions.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 3ace06039c..e8e4aff085 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -89,8 +89,7 @@ std::optional encode(std::span data, int64_t level, int64_t zstrm.avail_out = out_size_upper_bound; zstrm.next_out = reinterpret_cast(runtime_ctx.static_SB.buffer()); - const auto deflate_res{deflate(std::addressof(zstrm), Z_FINISH)}; - if (deflate_res != Z_STREAM_END) [[unlikely]] { + if (const auto deflate_res{deflate(std::addressof(zstrm), Z_FINISH)}; deflate_res != Z_STREAM_END) [[unlikely]] { kphp::log::warning("can't encode data of length {} due to zlib error {}", data.size(), deflate_res); return {}; } @@ -120,9 +119,8 @@ std::optional decode(std::span data, int64_t encoding) noexc runtime_ctx.static_SB.clean().reserve(StringInstanceState::STATIC_BUFFER_LENGTH); zstrm.avail_out = StringInstanceState::STATIC_BUFFER_LENGTH; zstrm.next_out = reinterpret_cast(runtime_ctx.static_SB.buffer()); - const auto inflate_res{inflate(std::addressof(zstrm), Z_NO_FLUSH)}; - if (inflate_res != Z_STREAM_END) [[unlikely]] { + if (const auto inflate_res{inflate(std::addressof(zstrm), Z_NO_FLUSH)}; inflate_res != Z_STREAM_END) [[unlikely]] { kphp::log::warning("can't decode data of length {} due to zlib error {}", data.size(), inflate_res); return {}; } @@ -138,8 +136,7 @@ class_instance f$deflate_init(int64_t encoding, const array& option, int32_t& dst) noexcept { - const mixed& value{option.get_value()}; - if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { + if (const mixed & value{option.get_value()}; value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { dst = value.as_int(); return true; } else { @@ -187,8 +184,7 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, case Z_FINISH: break; default: - kphp::log::warning( - "flush type should be one of ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_FINISH, ZLIB_BLOCK, ZLIB_TREES"); + kphp::log::warning("flush type should be one of ZLIB_NO_FLUSH, ZLIB_PARTIAL_FLUSH, ZLIB_SYNC_FLUSH, ZLIB_FULL_FLUSH, ZLIB_FINISH, ZLIB_BLOCK, ZLIB_TREES"); return {}; } From 35588e63c3687a26e2218edd899dc79e7779e3cf Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Fri, 19 Dec 2025 14:22:26 +0300 Subject: [PATCH 11/39] fix message --- runtime-light/stdlib/zlib/zlib-functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index e8e4aff085..614661ff61 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -140,7 +140,7 @@ class_instance f$deflate_init(int64_t encoding, const array Date: Fri, 19 Dec 2025 16:37:52 +0300 Subject: [PATCH 12/39] fixes --- .../stdlib/zlib/{deflatecontext.h => deflate-context.h} | 5 +++++ runtime-light/stdlib/zlib/zlib-functions.h | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) rename runtime-light/stdlib/zlib/{deflatecontext.h => deflate-context.h} (72%) diff --git a/runtime-light/stdlib/zlib/deflatecontext.h b/runtime-light/stdlib/zlib/deflate-context.h similarity index 72% rename from runtime-light/stdlib/zlib/deflatecontext.h rename to runtime-light/stdlib/zlib/deflate-context.h index 5ea4f94a99..bd68d02044 100644 --- a/runtime-light/stdlib/zlib/deflatecontext.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -13,6 +13,11 @@ struct C$DeflateContext : public refcountable_php_classes, pri C$DeflateContext() noexcept = default; using DummyVisitorMethods::accept; + C$DeflateContext(const C$DeflateContext&) = delete; + C$DeflateContext(C$DeflateContext&&) = delete; + C$DeflateContext& operator=(const C$DeflateContext&) = delete; + C$DeflateContext& operator=(C$DeflateContext&&) = delete; + ~C$DeflateContext() { deflateEnd(std::addressof(stream)); } diff --git a/runtime-light/stdlib/zlib/zlib-functions.h b/runtime-light/stdlib/zlib/zlib-functions.h index d23dc1345f..3168e3d4af 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.h +++ b/runtime-light/stdlib/zlib/zlib-functions.h @@ -10,7 +10,7 @@ #include #include "runtime-common/core/runtime-core.h" -#include "runtime-light/stdlib/zlib/deflatecontext.h" +#include "runtime-light/stdlib/zlib/deflate-context.h" namespace kphp::zlib { From 493d2616df414314f8f81ceb8ad559aff80d03d0 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Fri, 19 Dec 2025 16:38:11 +0300 Subject: [PATCH 13/39] 2025 --- runtime-light/stdlib/zlib/deflate-context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/deflate-context.h b/runtime-light/stdlib/zlib/deflate-context.h index bd68d02044..4b030981d2 100644 --- a/runtime-light/stdlib/zlib/deflate-context.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -1,5 +1,5 @@ // Compiler for PHP (aka KPHP) -// Copyright (c) 2024 LLC «V Kontakte» +// Copyright (c) 2025 LLC «V Kontakte» // Distributed under the GPL v3 License, see LICENSE.notice.txt #pragma once From 252a1e87b11d931ab0d9e45fba3a5fb594324785 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Fri, 19 Dec 2025 16:41:32 +0300 Subject: [PATCH 14/39] do not check `zlib_dynamic_free` arg --- runtime-light/stdlib/zlib/zlib-functions.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 614661ff61..e2d53743f7 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -46,9 +46,6 @@ voidpf zlib_dynamic_alloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) } void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept { - if (address == nullptr) [[unlikely]] { - return; - } kphp::memory::script::free(address); } From c31bb20369e55901975ae54dc14c6544415aaa28 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Fri, 19 Dec 2025 16:50:17 +0300 Subject: [PATCH 15/39] move value declaration --- runtime-light/stdlib/zlib/zlib-functions.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index e2d53743f7..6f3bd59c1b 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -141,6 +141,7 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array Date: Fri, 19 Dec 2025 17:03:36 +0300 Subject: [PATCH 16/39] constants --- runtime-light/stdlib/zlib/zlib-functions.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 6f3bd59c1b..45ac6d02b4 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -221,6 +221,9 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, const string& data, int64_t flush_type) noexcept { + constexpr uint64_t EXTRA_OUT_SIZE{30}; + constexpr uint64_t MIN_OUT_SIZE{64}; + switch (flush_type) { case Z_BLOCK: case Z_NO_FLUSH: @@ -235,8 +238,8 @@ Optional f$deflate_add(const class_instance& context, } z_stream* stream{std::addressof(context.get()->stream)}; - auto out_size{deflateBound(stream, data.size()) + 30}; - out_size = out_size < 64 ? 64 : out_size; + auto out_size{deflateBound(stream, data.size()) + EXTRA_OUT_SIZE}; + out_size = out_size < MIN_OUT_SIZE ? MIN_OUT_SIZE : out_size; string out{static_cast(out_size), false}; stream->next_in = const_cast(reinterpret_cast(data.c_str())); stream->next_out = reinterpret_cast(out.buffer()); @@ -247,9 +250,9 @@ Optional f$deflate_add(const class_instance& context, uint64_t buffer_used{}; do { if (stream->avail_out == 0) { - out.reserve_at_least(out_size + 64); - out_size += 64; - stream->avail_out = 64; + out.reserve_at_least(out_size + MIN_OUT_SIZE); + out_size += MIN_OUT_SIZE; + stream->avail_out = MIN_OUT_SIZE; stream->next_out = reinterpret_cast(std::next(out.buffer(), buffer_used)); } status = deflate(stream, flush_type); From db995c81914c80e849b7c891b70982361e48490d Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 14:08:19 +0300 Subject: [PATCH 17/39] alloc -> calloc --- runtime-light/stdlib/zlib/zlib-functions.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 45ac6d02b4..cda39ce072 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -37,10 +37,10 @@ voidpf zlib_static_alloc(voidpf opaque, uInt items, uInt size) noexcept { void zlib_static_free([[maybe_unused]] voidpf opaque, [[maybe_unused]] voidpf address) noexcept {} -voidpf zlib_dynamic_alloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) noexcept { +voidpf zlib_dynamic_calloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) noexcept { auto* mem{kphp::memory::script::calloc(items, size)}; if (mem == nullptr) [[unlikely]] { - kphp::log::warning("zlib dynamic alloc: can't allocate {} bytes", items * size); + kphp::log::warning("zlib dynamic calloc: can't allocate {} bytes", items * size); } return mem; } @@ -202,7 +202,7 @@ class_instance f$deflate_init(int64_t encoding, const arraystream)}; - stream->zalloc = zlib_dynamic_alloc; + stream->zalloc = zlib_dynamic_calloc; stream->zfree = zlib_dynamic_free; stream->opaque = nullptr; From 487b127bd8e587427d356140ac861cba21556f5e Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 14:30:16 +0300 Subject: [PATCH 18/39] format --- runtime-light/stdlib/zlib/zlib-functions.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index cda39ce072..01b8f8c277 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -133,7 +133,8 @@ class_instance f$deflate_init(int64_t encoding, const array& option, int32_t& dst) noexcept { - if (const mixed & value{option.get_value()}; value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { + const mixed& value{option.get_value()}; + if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { dst = value.as_int(); return true; } else { From 4083e72ce2b1cd4849345b7dbc0bbe8cc179c4f5 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 14:35:02 +0300 Subject: [PATCH 19/39] a number --- runtime-light/stdlib/zlib/zlib-functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 01b8f8c277..20cec49e5b 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -138,7 +138,7 @@ class_instance f$deflate_init(int64_t encoding, const array Date: Mon, 12 Jan 2026 14:41:32 +0300 Subject: [PATCH 20/39] default values of options --- runtime-light/stdlib/zlib/zlib-functions.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 20cec49e5b..0254011f2d 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -128,9 +128,13 @@ std::optional decode(std::span data, int64_t encoding) noexc } // namespace kphp::zlib class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { - int32_t level{-1}; - int32_t memory{8}; - int32_t window{15}; + constexpr int32_t default_level{-1}; + constexpr int32_t default_memory{8}; + constexpr int32_t default_window{15}; + + int32_t level{default_level}; + int32_t memory{default_memory}; + int32_t window{default_window}; auto strategy{Z_DEFAULT_STRATEGY}; constexpr auto extract_int_option{[](int32_t lbound, int32_t ubound, const array_iterator& option, int32_t& dst) noexcept { const mixed& value{option.get_value()}; From 91f5b7fe916953bdcba0aa603ae6cffcd77d8556 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 14:43:17 +0300 Subject: [PATCH 21/39] #include "zlib/zlib.h" --- runtime-light/stdlib/zlib/zlib-functions.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime-light/stdlib/zlib/zlib-functions.h b/runtime-light/stdlib/zlib/zlib-functions.h index 3168e3d4af..02de5c5fb5 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.h +++ b/runtime-light/stdlib/zlib/zlib-functions.h @@ -9,6 +9,8 @@ #include #include +#include "zlib/zlib.h" + #include "runtime-common/core/runtime-core.h" #include "runtime-light/stdlib/zlib/deflate-context.h" From 64bb8a96ffb9bd8cf10eb68738d3b0447e4b58e3 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 14:44:08 +0300 Subject: [PATCH 22/39] swap two lines --- runtime-light/stdlib/zlib/deflate-context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/deflate-context.h b/runtime-light/stdlib/zlib/deflate-context.h index 4b030981d2..f43d622479 100644 --- a/runtime-light/stdlib/zlib/deflate-context.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -10,8 +10,8 @@ #include "runtime-common/stdlib/visitors/dummy-visitor-methods.h" struct C$DeflateContext : public refcountable_php_classes, private DummyVisitorMethods { - C$DeflateContext() noexcept = default; using DummyVisitorMethods::accept; + C$DeflateContext() noexcept = default; C$DeflateContext(const C$DeflateContext&) = delete; C$DeflateContext(C$DeflateContext&&) = delete; From 283b98ff5be1d219b66f3105e16571384d96ef77 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 15:50:49 +0300 Subject: [PATCH 23/39] small refactoring --- runtime-light/stdlib/zlib/zlib-functions.cpp | 66 ++++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 0254011f2d..b5a166c27f 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -128,25 +128,12 @@ std::optional decode(std::span data, int64_t encoding) noexc } // namespace kphp::zlib class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { + using namespace std::literals; + constexpr int32_t default_level{-1}; constexpr int32_t default_memory{8}; constexpr int32_t default_window{15}; - int32_t level{default_level}; - int32_t memory{default_memory}; - int32_t window{default_window}; - auto strategy{Z_DEFAULT_STRATEGY}; - constexpr auto extract_int_option{[](int32_t lbound, int32_t ubound, const array_iterator& option, int32_t& dst) noexcept { - const mixed& value{option.get_value()}; - if (value.is_int() && value.as_int() >= lbound && value.as_int() <= ubound) { - dst = value.as_int(); - return true; - } else { - kphp::log::warning("option {} should be a number between {}..{}", option.get_string_key().c_str(), lbound, ubound); - return false; - } - }}; - switch (encoding) { case kphp::zlib::ENCODING_RAW: case kphp::zlib::ENCODING_DEFLATE: @@ -157,6 +144,21 @@ class_instance f$deflate_init(int64_t encoding, const array= min && val.as_int() <= max) { + dst = val.as_int(); + return true; + } + kphp::log::warning("option {} should be a number between {}..{}", opt.get_string_key().c_str(), min, max); + return false; + }}; + for (const auto& option : options) { if (!option.is_string_key()) { kphp::log::warning("unsupported option"); @@ -164,27 +166,29 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const arrayzfree = zlib_dynamic_free; stream->opaque = nullptr; - if (encoding < 0) { - encoding += 15 - window; - } else { - encoding -= 15 - window; - } + const auto window_bits{encoding < 0 ? encoding + (15 - window) : encoding - (15 - window)}; - if (auto err{deflateInit2(stream, level, Z_DEFLATED, encoding, memory, strategy)}; err != Z_OK) { + if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { kphp::log::warning("zlib error {}", zError(err)); context.destroy(); return {}; From cf25e79316dcf0528a6c5786bab1ac4ce3911f6b Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 15:53:20 +0300 Subject: [PATCH 24/39] using make_instance --- runtime-light/stdlib/zlib/zlib-functions.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index b5a166c27f..5cd530cb36 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -207,8 +207,7 @@ class_instance f$deflate_init(int64_t encoding, const array context; - context.alloc(); + auto context{make_instance()}; z_stream* stream{std::addressof(context.get()->stream)}; stream->zalloc = zlib_dynamic_calloc; From d4e8751cfc83114345e2cd78c912eb254ac9403f Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 18:01:30 +0300 Subject: [PATCH 25/39] yet another refactoring --- runtime-light/stdlib/zlib/zlib-functions.cpp | 89 ++++++++++++-------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 5cd530cb36..a445e73c24 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -22,6 +22,8 @@ namespace { +enum class zlib_format : int8_t { raw, deflate, gzip }; + voidpf zlib_static_alloc(voidpf opaque, uInt items, uInt size) noexcept { auto* buf_pos_ptr{reinterpret_cast(opaque)}; auto required_mem{static_cast(items * size)}; @@ -49,6 +51,31 @@ void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept kphp::memory::script::free(address); } +std::optional parse_encoding(int64_t encoding) noexcept { + if (encoding == kphp::zlib::ENCODING_GZIP) { + return zlib_format::gzip; + } + if (encoding == kphp::zlib::ENCODING_RAW) { + return zlib_format::raw; + } + if (encoding == kphp::zlib::ENCODING_DEFLATE) { + return zlib_format::deflate; + } + return std::nullopt; +} + +int32_t to_zlib_window_bits(zlib_format format, int32_t window) noexcept { + switch (format) { + case zlib_format::gzip: + return 16 + window; + case zlib_format::raw: + return -window; + case zlib_format::deflate: + return window; + } + return window; +} + } // namespace namespace kphp::zlib { @@ -130,67 +157,57 @@ std::optional decode(std::span data, int64_t encoding) noexc class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { using namespace std::literals; - constexpr int32_t default_level{-1}; constexpr int32_t default_memory{8}; constexpr int32_t default_window{15}; - switch (encoding) { - case kphp::zlib::ENCODING_RAW: - case kphp::zlib::ENCODING_DEFLATE: - case kphp::zlib::ENCODING_GZIP: - break; - default: + const auto opt_format{parse_encoding(encoding)}; + if (!opt_format.has_value()) [[unlikely]] { kphp::log::warning("encoding should be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_DEFLATE, ZLIB_ENCODING_GZIP"); return {}; } - int32_t level{default_level}; + int32_t level{Z_DEFAULT_COMPRESSION}; int32_t memory{default_memory}; int32_t window{default_window}; - auto strategy{Z_DEFAULT_STRATEGY}; - - constexpr auto extract_int{[](int32_t min, int32_t max, const auto& opt, int32_t& dst) noexcept { - const mixed& val{opt.get_value()}; - if (val.is_int() && val.as_int() >= min && val.as_int() <= max) { - dst = val.as_int(); - return true; - } - kphp::log::warning("option {} should be a number between {}..{}", opt.get_string_key().c_str(), min, max); - return false; - }}; + int32_t strategy{Z_DEFAULT_STRATEGY}; - for (const auto& option : options) { - if (!option.is_string_key()) { + for (const auto& it : options) { + if (!it.is_string_key()) { kphp::log::warning("unsupported option"); return {}; } - const auto& key{option.get_string_key()}; - if (const std::string_view key_view{key.c_str(), key.size()}; key_view == "level"sv) { - if (!extract_int(-1, 9, option, level)) { + const auto& key{it.get_string_key()}; + const std::string_view key_view{key.c_str(), key.size()}; + const mixed& val{it.get_value()}; + + if (key_view == "level"sv) { + if (val.is_int() && val.as_int() >= -1 && val.as_int() <= 9) { + level = val.as_int(); + } else { + kphp::log::warning("option level should be a number between -1..9"); return {}; } } else if (key_view == "memory"sv) { - if (!extract_int(1, 9, option, memory)) { + if (val.is_int() && val.as_int() >= 1 && val.as_int() <= 9) { + memory = val.as_int(); + } else { + kphp::log::warning("option memory should be a number between 1..9"); return {}; } } else if (key_view == "window"sv) { - if (!extract_int(8, 15, option, window)) { + if (val.is_int() && val.as_int() >= 8 && val.as_int() <= 15) { + window = val.as_int(); + } else { + kphp::log::warning("option window should be a number between 8..15"); return {}; } } else if (key_view == "strategy"sv) { - const mixed& val{option.get_value()}; if (val.is_int()) { const auto s{val.as_int()}; - switch (s) { - case Z_FILTERED: - case Z_HUFFMAN_ONLY: - case Z_RLE: - case Z_FIXED: - case Z_DEFAULT_STRATEGY: + if (s == Z_DEFAULT_STRATEGY || s == Z_FILTERED || s == Z_HUFFMAN_ONLY || s == Z_RLE || s == Z_FIXED) { strategy = s; - break; - default: + } else { kphp::log::warning("option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); return {}; } @@ -214,7 +231,7 @@ class_instance f$deflate_init(int64_t encoding, const arrayzfree = zlib_dynamic_free; stream->opaque = nullptr; - const auto window_bits{encoding < 0 ? encoding + (15 - window) : encoding - (15 - window)}; + const int32_t window_bits{to_zlib_window_bits(*opt_format, window)}; if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { kphp::log::warning("zlib error {}", zError(err)); From 5f3208b96a4c6bf5bc26f7df8914e2e45a9b82d0 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 18:03:49 +0300 Subject: [PATCH 26/39] do not use `zError` --- runtime-light/stdlib/zlib/zlib-functions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index a445e73c24..b67fc576a5 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -234,7 +234,7 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, out.shrink(len); return out; default: - kphp::log::warning("zlib error {}", zError(status)); + kphp::log::warning("zlib error {}", status); return {}; } } From bd66d0f5729ce5bea38d38e9368cc7c7050e04c9 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 18:11:36 +0300 Subject: [PATCH 27/39] hide `destroy` invocation --- runtime-light/stdlib/zlib/zlib-functions.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index b67fc576a5..9984419819 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -235,7 +235,6 @@ class_instance f$deflate_init(int64_t encoding, const array Date: Mon, 12 Jan 2026 20:54:45 +0300 Subject: [PATCH 28/39] include --- runtime-light/stdlib/zlib/deflate-context.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime-light/stdlib/zlib/deflate-context.h b/runtime-light/stdlib/zlib/deflate-context.h index f43d622479..1e1e6815be 100644 --- a/runtime-light/stdlib/zlib/deflate-context.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -4,6 +4,8 @@ #pragma once +#include + #include "zlib/zlib.h" #include "runtime-common/core/class-instance/refcountable-php-classes.h" From c1a1f495a946e3eb4164a92fdc5324ae8b321b98 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 21:35:37 +0300 Subject: [PATCH 29/39] remove zlib_format --- runtime-light/stdlib/zlib/zlib-functions.cpp | 40 ++++++++------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 9984419819..e2d52c00f5 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -22,8 +22,6 @@ namespace { -enum class zlib_format : int8_t { raw, deflate, gzip }; - voidpf zlib_static_alloc(voidpf opaque, uInt items, uInt size) noexcept { auto* buf_pos_ptr{reinterpret_cast(opaque)}; auto required_mem{static_cast(items * size)}; @@ -51,29 +49,24 @@ void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept kphp::memory::script::free(address); } -std::optional parse_encoding(int64_t encoding) noexcept { - if (encoding == kphp::zlib::ENCODING_GZIP) { - return zlib_format::gzip; - } - if (encoding == kphp::zlib::ENCODING_RAW) { - return zlib_format::raw; - } - if (encoding == kphp::zlib::ENCODING_DEFLATE) { - return zlib_format::deflate; - } - return std::nullopt; -} - -int32_t to_zlib_window_bits(zlib_format format, int32_t window) noexcept { - switch (format) { - case zlib_format::gzip: +/** + * Maps KPHP encoding constants and custom window size to zlib windowBits. + * zlib windowBits logic: + * 8..15 -> ZLIB header + * -8..-15 -> RAW (no header) + * 24..31 -> GZIP (16 + window) + */ +int32_t to_zlib_window_bits(int64_t encoding, int32_t window) noexcept { + switch (encoding) { + case kphp::zlib::ENCODING_GZIP: return 16 + window; - case zlib_format::raw: + case kphp::zlib::ENCODING_RAW: return -window; - case zlib_format::deflate: + case kphp::zlib::ENCODING_DEFLATE: return window; + default: + kphp::log::error("to_zlib_window_bits: unknown encoding {}", encoding); } - return window; } } // namespace @@ -160,8 +153,7 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const arrayzfree = zlib_dynamic_free; stream->opaque = nullptr; - const int32_t window_bits{to_zlib_window_bits(*opt_format, window)}; + const int32_t window_bits{to_zlib_window_bits(encoding, window)}; if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { kphp::log::warning("zlib error {}", err); From 89c14b9f1fbaa8333f563408ac2804d0b3e1c0fa Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Mon, 12 Jan 2026 21:43:37 +0300 Subject: [PATCH 30/39] move "using namespace std::literals" into for loop --- runtime-light/stdlib/zlib/zlib-functions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index e2d52c00f5..8202acb1b6 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -148,8 +148,6 @@ std::optional decode(std::span data, int64_t encoding) noexc } // namespace kphp::zlib class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { - using namespace std::literals; - constexpr int32_t default_memory{8}; constexpr int32_t default_window{15}; @@ -164,6 +162,8 @@ class_instance f$deflate_init(int64_t encoding, const array Date: Mon, 12 Jan 2026 21:47:02 +0300 Subject: [PATCH 31/39] const auto& val --- runtime-light/stdlib/zlib/zlib-functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 8202acb1b6..980f67c9ba 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -171,7 +171,7 @@ class_instance f$deflate_init(int64_t encoding, const array= -1 && val.as_int() <= 9) { From 6c414ec2bd26058afe3d82f2de4f4daf3a0666b1 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 13 Jan 2026 12:36:27 +0300 Subject: [PATCH 32/39] constants --- runtime-light/stdlib/zlib/zlib-functions.cpp | 6 +++--- runtime-light/stdlib/zlib/zlib-functions.h | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 980f67c9ba..016120d55f 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -174,21 +174,21 @@ class_instance f$deflate_init(int64_t encoding, const array= -1 && val.as_int() <= 9) { + if (val.is_int() && val.as_int() >= kphp::zlib::MIN_COMPRESSION_LEVEL && val.as_int() <= kphp::zlib::MAX_COMPRESSION_LEVEL) { level = val.as_int(); } else { kphp::log::warning("option level should be a number between -1..9"); return {}; } } else if (key_view == "memory"sv) { - if (val.is_int() && val.as_int() >= 1 && val.as_int() <= 9) { + if (val.is_int() && val.as_int() >= kphp::zlib::MIN_MEMORY_LEVEL && val.as_int() <= kphp::zlib::MAX_MEMORY_LEVEL) { memory = val.as_int(); } else { kphp::log::warning("option memory should be a number between 1..9"); return {}; } } else if (key_view == "window"sv) { - if (val.is_int() && val.as_int() >= 8 && val.as_int() <= 15) { + if (val.is_int() && val.as_int() >= kphp::zlib::MIN_WINDOW_SIZE && val.as_int() <= kphp::zlib::MAX_WINDOW_SIZE) { window = val.as_int(); } else { kphp::log::warning("option window should be a number between 8..15"); diff --git a/runtime-light/stdlib/zlib/zlib-functions.h b/runtime-light/stdlib/zlib/zlib-functions.h index 02de5c5fb5..0da9ef7e16 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.h +++ b/runtime-light/stdlib/zlib/zlib-functions.h @@ -16,13 +16,19 @@ namespace kphp::zlib { -inline constexpr int64_t ENCODING_RAW = -0x0f; -inline constexpr int64_t ENCODING_DEFLATE = 0x0f; -inline constexpr int64_t ENCODING_GZIP = 0x1f; +inline constexpr int64_t ENCODING_RAW{-0x0f}; +inline constexpr int64_t ENCODING_DEFLATE{0x0f}; +inline constexpr int64_t ENCODING_GZIP{0x1f}; -inline constexpr int64_t MIN_COMPRESSION_LEVEL = -1; -inline constexpr int64_t MAX_COMPRESSION_LEVEL = 9; -inline constexpr int64_t DEFAULT_COMPRESSION_LEVEL = 6; +inline constexpr int64_t MIN_COMPRESSION_LEVEL{-1}; +inline constexpr int64_t MAX_COMPRESSION_LEVEL{9}; +inline constexpr int64_t DEFAULT_COMPRESSION_LEVEL{6}; + +inline constexpr int64_t MIN_MEMORY_LEVEL{1}; +inline constexpr int64_t MAX_MEMORY_LEVEL{9}; + +inline constexpr int64_t MIN_WINDOW_SIZE{8}; +inline constexpr int64_t MAX_WINDOW_SIZE{15}; std::optional encode(std::span data, int64_t level, int64_t encoding) noexcept; From 702ec51bee2d103bb0fbf1f2921f2824e134bfc0 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 13 Jan 2026 12:55:30 +0300 Subject: [PATCH 33/39] early return --- runtime-light/stdlib/zlib/zlib-functions.cpp | 31 ++++++++------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 016120d55f..8f4ae68c96 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -174,39 +174,34 @@ class_instance f$deflate_init(int64_t encoding, const array= kphp::zlib::MIN_COMPRESSION_LEVEL && val.as_int() <= kphp::zlib::MAX_COMPRESSION_LEVEL) { - level = val.as_int(); - } else { + if (!val.is_int() || val.as_int() < kphp::zlib::MIN_COMPRESSION_LEVEL || val.as_int() > kphp::zlib::MAX_COMPRESSION_LEVEL) { kphp::log::warning("option level should be a number between -1..9"); return {}; } + level = val.as_int(); } else if (key_view == "memory"sv) { - if (val.is_int() && val.as_int() >= kphp::zlib::MIN_MEMORY_LEVEL && val.as_int() <= kphp::zlib::MAX_MEMORY_LEVEL) { - memory = val.as_int(); - } else { + if (!val.is_int() || val.as_int() < kphp::zlib::MIN_MEMORY_LEVEL || val.as_int() > kphp::zlib::MAX_MEMORY_LEVEL) { kphp::log::warning("option memory should be a number between 1..9"); return {}; } + memory = val.as_int(); } else if (key_view == "window"sv) { - if (val.is_int() && val.as_int() >= kphp::zlib::MIN_WINDOW_SIZE && val.as_int() <= kphp::zlib::MAX_WINDOW_SIZE) { - window = val.as_int(); - } else { + if (!val.is_int() || val.as_int() < kphp::zlib::MIN_WINDOW_SIZE || val.as_int() > kphp::zlib::MAX_WINDOW_SIZE) { kphp::log::warning("option window should be a number between 8..15"); return {}; } + window = val.as_int(); } else if (key_view == "strategy"sv) { - if (val.is_int()) { - const auto s{val.as_int()}; - if (s == Z_DEFAULT_STRATEGY || s == Z_FILTERED || s == Z_HUFFMAN_ONLY || s == Z_RLE || s == Z_FIXED) { - strategy = s; - } else { - kphp::log::warning("option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); - return {}; - } - } else { + if (!val.is_int()) { kphp::log::warning("option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); return {}; } + const auto s{val.as_int()}; + if (s != Z_DEFAULT_STRATEGY && s != Z_FILTERED && s != Z_HUFFMAN_ONLY && s != Z_RLE && s != Z_FIXED) { + kphp::log::warning("option strategy should be one of ZLIB_FILTERED, ZLIB_HUFFMAN_ONLY, ZLIB_RLE, ZLIB_FIXED or ZLIB_DEFAULT_STRATEGY"); + return {}; + } + strategy = s; } else if (key_view == "dictionary"sv) { kphp::log::warning("option dictionary isn't supported yet"); return {}; From bc4ee2d90373bf9fb6d01c67887e6fd6070e84bf Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 13 Jan 2026 12:58:44 +0300 Subject: [PATCH 34/39] static local constants --- runtime-light/stdlib/zlib/zlib-functions.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 8f4ae68c96..d7276a42bf 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -148,8 +148,8 @@ std::optional decode(std::span data, int64_t encoding) noexc } // namespace kphp::zlib class_instance f$deflate_init(int64_t encoding, const array& options) noexcept { - constexpr int32_t default_memory{8}; - constexpr int32_t default_window{15}; + static constexpr int32_t DEFAULT_MEMORY{8}; + static constexpr int32_t DEFAULT_WINDOW{15}; if (encoding != kphp::zlib::ENCODING_RAW && encoding != kphp::zlib::ENCODING_DEFLATE && encoding != kphp::zlib::ENCODING_GZIP) { kphp::log::warning("encoding should be one of ZLIB_ENCODING_RAW, ZLIB_ENCODING_DEFLATE, ZLIB_ENCODING_GZIP"); @@ -157,8 +157,8 @@ class_instance f$deflate_init(int64_t encoding, const array f$deflate_init(int64_t encoding, const array f$deflate_add(const class_instance& context, const string& data, int64_t flush_type) noexcept { - constexpr uint64_t EXTRA_OUT_SIZE{30}; - constexpr uint64_t MIN_OUT_SIZE{64}; + static constexpr uint64_t EXTRA_OUT_SIZE{30}; + static constexpr uint64_t MIN_OUT_SIZE{64}; switch (flush_type) { case Z_BLOCK: From d77b5f3f09e449793aeaed0e2962c15306a3d670 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Tue, 13 Jan 2026 13:03:21 +0300 Subject: [PATCH 35/39] switch-case scopes --- runtime-light/stdlib/zlib/zlib-functions.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index d7276a42bf..4b0abad72f 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -266,19 +266,21 @@ Optional f$deflate_add(const class_instance& context, buffer_used = out_size - stream->avail_out; } while (status == Z_OK && stream->avail_out == 0); - std::ptrdiff_t len{}; switch (status) { - case Z_OK: - len = std::distance(reinterpret_cast(out.buffer()), stream->next_out); + case Z_OK: { + auto len{std::distance(reinterpret_cast(out.buffer()), stream->next_out)}; out.shrink(len); return out; - case Z_STREAM_END: - len = std::distance(reinterpret_cast(out.buffer()), stream->next_out); + } + case Z_STREAM_END: { + auto len{std::distance(reinterpret_cast(out.buffer()), stream->next_out)}; deflateReset(stream); out.shrink(len); return out; - default: + } + default: { kphp::log::warning("zlib error {}", status); return {}; } + } } From 9bcabd6f8bb38849ad1f3cce087e08af9a5e2d56 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Wed, 14 Jan 2026 12:58:54 +0300 Subject: [PATCH 36/39] do not call `deflateEnd` if `deflateInit2` returned error --- runtime-light/stdlib/zlib/deflate-context.h | 7 +++++-- runtime-light/stdlib/zlib/zlib-functions.cpp | 9 ++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/runtime-light/stdlib/zlib/deflate-context.h b/runtime-light/stdlib/zlib/deflate-context.h index 1e1e6815be..219ccc287c 100644 --- a/runtime-light/stdlib/zlib/deflate-context.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -5,6 +5,7 @@ #pragma once #include +#include #include "zlib/zlib.h" @@ -21,8 +22,10 @@ struct C$DeflateContext : public refcountable_php_classes, pri C$DeflateContext& operator=(C$DeflateContext&&) = delete; ~C$DeflateContext() { - deflateEnd(std::addressof(stream)); + if (stream.has_value()) [[likely]] { + deflateEnd(std::addressof(*stream)); + } } - z_stream stream{}; + std::optional stream; }; diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 4b0abad72f..56bcb0f604 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -213,14 +213,12 @@ class_instance f$deflate_init(int64_t encoding, const array()}; - z_stream* stream{std::addressof(context.get()->stream)}; - stream->zalloc = zlib_dynamic_calloc; - stream->zfree = zlib_dynamic_free; - stream->opaque = nullptr; + z_stream* stream{std::addressof(context.get()->stream.emplace(z_stream{.zalloc = zlib_dynamic_calloc, .zfree = zlib_dynamic_free, .opaque = nullptr}))}; const int32_t window_bits{to_zlib_window_bits(encoding, window)}; if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { + context.get()->stream = std::nullopt; kphp::log::warning("zlib error {}", err); return {}; } @@ -244,7 +242,8 @@ Optional f$deflate_add(const class_instance& context, return {}; } - z_stream* stream{std::addressof(context.get()->stream)}; + kphp::log::assertion(context.get()->stream.has_value()); + z_stream* stream{std::addressof(*context.get()->stream)}; auto out_size{deflateBound(stream, data.size()) + EXTRA_OUT_SIZE}; out_size = out_size < MIN_OUT_SIZE ? MIN_OUT_SIZE : out_size; string out{static_cast(out_size), false}; From 5d7f0a179b39ce8194c5c34baeb066b900da0a92 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Wed, 14 Jan 2026 13:05:27 +0300 Subject: [PATCH 37/39] to_zlib_window_bits -> lambda --- runtime-light/stdlib/zlib/zlib-functions.cpp | 33 +++++++------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index 56bcb0f604..e10fe268b4 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -49,26 +49,6 @@ void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept kphp::memory::script::free(address); } -/** - * Maps KPHP encoding constants and custom window size to zlib windowBits. - * zlib windowBits logic: - * 8..15 -> ZLIB header - * -8..-15 -> RAW (no header) - * 24..31 -> GZIP (16 + window) - */ -int32_t to_zlib_window_bits(int64_t encoding, int32_t window) noexcept { - switch (encoding) { - case kphp::zlib::ENCODING_GZIP: - return 16 + window; - case kphp::zlib::ENCODING_RAW: - return -window; - case kphp::zlib::ENCODING_DEFLATE: - return window; - default: - kphp::log::error("to_zlib_window_bits: unknown encoding {}", encoding); - } -} - } // namespace namespace kphp::zlib { @@ -215,7 +195,18 @@ class_instance f$deflate_init(int64_t encoding, const arraystream.emplace(z_stream{.zalloc = zlib_dynamic_calloc, .zfree = zlib_dynamic_free, .opaque = nullptr}))}; - const int32_t window_bits{to_zlib_window_bits(encoding, window)}; + const int32_t window_bits{[encoding, window] noexcept { + switch (encoding) { + case kphp::zlib::ENCODING_GZIP: + return 16 + window; + case kphp::zlib::ENCODING_RAW: + return -window; + case kphp::zlib::ENCODING_DEFLATE: + return window; + default: + kphp::log::error("deflate_init: unknown encoding {}", encoding); + } + }()}; if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { context.get()->stream = std::nullopt; From 42a3d3412376b25a8cbdfa22066c53903d923a12 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Wed, 14 Jan 2026 14:29:19 +0300 Subject: [PATCH 38/39] move md5_file from zlib-functions.txt into file-functions.txt --- builtin-functions/kphp-light/stdlib/file-functions.txt | 3 +++ builtin-functions/kphp-light/stdlib/zlib-functions.txt | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/builtin-functions/kphp-light/stdlib/file-functions.txt b/builtin-functions/kphp-light/stdlib/file-functions.txt index 334aec33d2..b3ee9e6737 100644 --- a/builtin-functions/kphp-light/stdlib/file-functions.txt +++ b/builtin-functions/kphp-light/stdlib/file-functions.txt @@ -102,3 +102,6 @@ function stream_context_create ($options ::: mixed = array()) ::: mixed; function fprintf ($stream, $format ::: string, ...$args) ::: int; /** @kphp-extern-func-info stub */ function fputcsv ($stream, $fields ::: array, $delimiter = ",", $enclosure = "\"", $escape = "\\") ::: int | false; + +/** @kphp-extern-func-info stub generation-required */ +function md5_file ($s ::: string, $raw_output ::: bool = false) ::: string | false; diff --git a/builtin-functions/kphp-light/stdlib/zlib-functions.txt b/builtin-functions/kphp-light/stdlib/zlib-functions.txt index 2121dc2f37..953472780d 100644 --- a/builtin-functions/kphp-light/stdlib/zlib-functions.txt +++ b/builtin-functions/kphp-light/stdlib/zlib-functions.txt @@ -39,9 +39,3 @@ function deflate_add(DeflateContext $context, string $data, int $flush_mode = ZL final class DeflateContext { private function __construct() ::: DeflateContext; } - - -// ===== UNSUPPORTED ===== - -/** @kphp-extern-func-info stub generation-required */ -function md5_file ($s ::: string, $raw_output ::: bool = false) ::: string | false; From 4f05a770af36b87d51c48f7a8a7dfe6333a9bf38 Mon Sep 17 00:00:00 2001 From: Karim Shamazov Date: Wed, 14 Jan 2026 15:28:29 +0300 Subject: [PATCH 39/39] make init method --- runtime-light/stdlib/zlib/deflate-context.h | 42 +++++++++++++++++--- runtime-light/stdlib/zlib/zlib-functions.cpp | 26 ++---------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/runtime-light/stdlib/zlib/deflate-context.h b/runtime-light/stdlib/zlib/deflate-context.h index 219ccc287c..f3ac736a05 100644 --- a/runtime-light/stdlib/zlib/deflate-context.h +++ b/runtime-light/stdlib/zlib/deflate-context.h @@ -4,15 +4,33 @@ #pragma once +#include #include #include #include "zlib/zlib.h" +#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-common/core/class-instance/refcountable-php-classes.h" #include "runtime-common/stdlib/visitors/dummy-visitor-methods.h" +#include "runtime-light/stdlib/diagnostics/logs.h" -struct C$DeflateContext : public refcountable_php_classes, private DummyVisitorMethods { +class C$DeflateContext : public refcountable_php_classes, private DummyVisitorMethods { + std::optional m_stream; + + static voidpf dynamic_calloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) noexcept { + auto* mem{kphp::memory::script::calloc(items, size)}; + if (mem == nullptr) [[unlikely]] { + kphp::log::warning("zlib dynamic calloc: can't allocate {} bytes", items * size); + } + return mem; + } + + static void dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept { + kphp::memory::script::free(address); + } + +public: using DummyVisitorMethods::accept; C$DeflateContext() noexcept = default; @@ -21,11 +39,25 @@ struct C$DeflateContext : public refcountable_php_classes, pri C$DeflateContext& operator=(const C$DeflateContext&) = delete; C$DeflateContext& operator=(C$DeflateContext&&) = delete; - ~C$DeflateContext() { - if (stream.has_value()) [[likely]] { - deflateEnd(std::addressof(*stream)); + bool init(int32_t level, int32_t window_bits, int32_t memory, int32_t strategy) noexcept { + z_stream* stream{std::addressof(m_stream.emplace(z_stream{.zalloc = dynamic_calloc, .zfree = dynamic_free, .opaque = nullptr}))}; + + if (auto err{deflateInit2(stream, level, Z_DEFLATED, window_bits, memory, strategy)}; err != Z_OK) { + kphp::log::warning("zlib error {}", err); + return false; } + + return true; + } + + z_stream& operator*() noexcept { + kphp::log::assertion(m_stream.has_value()); + return *m_stream; } - std::optional stream; + ~C$DeflateContext() { + if (m_stream.has_value()) [[likely]] { + deflateEnd(std::addressof(*m_stream)); + } + } }; diff --git a/runtime-light/stdlib/zlib/zlib-functions.cpp b/runtime-light/stdlib/zlib/zlib-functions.cpp index e10fe268b4..5798b577ed 100644 --- a/runtime-light/stdlib/zlib/zlib-functions.cpp +++ b/runtime-light/stdlib/zlib/zlib-functions.cpp @@ -15,7 +15,6 @@ #include "zlib/zlib.h" #include "common/containers/final_action.h" -#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-common/core/runtime-core.h" #include "runtime-light/stdlib/diagnostics/logs.h" #include "runtime-light/stdlib/string/string-state.h" @@ -37,18 +36,6 @@ voidpf zlib_static_alloc(voidpf opaque, uInt items, uInt size) noexcept { void zlib_static_free([[maybe_unused]] voidpf opaque, [[maybe_unused]] voidpf address) noexcept {} -voidpf zlib_dynamic_calloc([[maybe_unused]] voidpf opaque, uInt items, uInt size) noexcept { - auto* mem{kphp::memory::script::calloc(items, size)}; - if (mem == nullptr) [[unlikely]] { - kphp::log::warning("zlib dynamic calloc: can't allocate {} bytes", items * size); - } - return mem; -} - -void zlib_dynamic_free([[maybe_unused]] voidpf opaque, voidpf address) noexcept { - kphp::memory::script::free(address); -} - } // namespace namespace kphp::zlib { @@ -191,10 +178,6 @@ class_instance f$deflate_init(int64_t encoding, const array()}; - - z_stream* stream{std::addressof(context.get()->stream.emplace(z_stream{.zalloc = zlib_dynamic_calloc, .zfree = zlib_dynamic_free, .opaque = nullptr}))}; - const int32_t window_bits{[encoding, window] noexcept { switch (encoding) { case kphp::zlib::ENCODING_GZIP: @@ -208,9 +191,9 @@ class_instance f$deflate_init(int64_t encoding, const arraystream = std::nullopt; - kphp::log::warning("zlib error {}", err); + auto context{make_instance()}; + + if (!context->init(level, window_bits, memory, strategy)) { return {}; } return context; @@ -233,8 +216,7 @@ Optional f$deflate_add(const class_instance& context, return {}; } - kphp::log::assertion(context.get()->stream.has_value()); - z_stream* stream{std::addressof(*context.get()->stream)}; + z_stream* stream{std::addressof(**context.get())}; auto out_size{deflateBound(stream, data.size()) + EXTRA_OUT_SIZE}; out_size = out_size < MIN_OUT_SIZE ? MIN_OUT_SIZE : out_size; string out{static_cast(out_size), false};