diff --git a/runtime-light/stdlib/file/file-system-functions.h b/runtime-light/stdlib/file/file-system-functions.h index bdc3ece9c7..6fdef0c134 100644 --- a/runtime-light/stdlib/file/file-system-functions.h +++ b/runtime-light/stdlib/file/file-system-functions.h @@ -16,9 +16,7 @@ #include #include -#include "runtime-common/core/allocator/script-allocator.h" #include "runtime-common/core/runtime-core.h" -#include "runtime-common/core/std/containers.h" #include "runtime-common/stdlib/array/array-functions.h" #include "runtime-common/stdlib/string/string-functions.h" #include "runtime-light/coroutine/task.h" @@ -204,40 +202,24 @@ inline Optional f$file_get_contents(const string& stream) noexcept { } inline Optional> f$file(const string& name) noexcept { - struct stat stat_buf {}; - - auto expected_file{kphp::fs::file::open(name.c_str(), "r")}; + auto expected_file{kphp::fs::file::open({name.c_str(), name.size()}, "r")}; if (!expected_file.has_value()) { return false; } - if (!k2::stat({name.c_str(), name.size()}, std::addressof(stat_buf)).has_value()) { - return false; - } - if (!S_ISREG(stat_buf.st_mode)) { - kphp::log::warning("regular file expected as first argument in function file, \"{}\" is given", name.c_str()); - return false; - } - const size_t size{static_cast(stat_buf.st_size)}; - if (size > string::max_size()) { - kphp::log::warning("file \"{}\" is too large", name.c_str()); + auto expected_file_content{std::move(*expected_file).get_contents()}; + if (!expected_file_content.has_value()) { + kphp::log::warning("file::get_contents returned code {}", expected_file_content.error()); return false; } - - kphp::stl::vector file_content; - file_content.resize(size); - { - auto file{std::move(*expected_file)}; - if (auto expected_read_result{file.read(file_content)}; !expected_read_result.has_value() || *expected_read_result < size) { - return false; - } - } + auto file_content{std::move(*expected_file_content)}; + const size_t size{file_content.size()}; array result; int32_t prev{-1}; for (size_t i{0}; i < size; i++) { - if (static_cast(file_content[i]) == '\n' || i + 1 == size) { - result.push_back(string{reinterpret_cast(file_content.data()) + prev + 1, static_cast(i - prev)}); + if (file_content[i] == '\n' || i + 1 == size) { + result.push_back(file_content.substr(prev + 1, static_cast(i - prev))); prev = i; } } diff --git a/runtime-light/stdlib/file/resource.h b/runtime-light/stdlib/file/resource.h index 5300ce96e0..f3eb83e2ea 100644 --- a/runtime-light/stdlib/file/resource.h +++ b/runtime-light/stdlib/file/resource.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -45,6 +47,73 @@ inline constexpr std::string_view UDP_SCHEME_PREFIX = "udp://"; // ================================================================================================ +class mmap { + k2::descriptor m_descriptor{k2::INVALID_PLATFORM_DESCRIPTOR}; + void* m_addr{nullptr}; + size_t m_length{}; + + mmap(k2::descriptor descriptor, void* addr, size_t length) noexcept + : m_descriptor{descriptor}, + m_addr{addr}, + m_length{length} {} + +public: + mmap(mmap&& other) noexcept + : m_descriptor{std::exchange(other.m_descriptor, k2::INVALID_PLATFORM_DESCRIPTOR)}, + m_addr{std::exchange(other.m_addr, nullptr)}, + m_length{std::exchange(other.m_length, {})} {} + + mmap& operator=(mmap&& other) noexcept { + if (this != std::addressof(other)) { + std::ignore = close(); + m_descriptor = std::exchange(other.m_descriptor, k2::INVALID_PLATFORM_DESCRIPTOR); + m_addr = std::exchange(other.m_addr, nullptr); + m_length = std::exchange(other.m_length, {}); + } + return *this; + } + + ~mmap() { + std::ignore = close(); + } + + mmap(const mmap&) = delete; + mmap& operator=(const mmap&) = delete; + + static auto create(size_t length, int32_t prot, int32_t flags, k2::descriptor fd, uint64_t offset) noexcept -> std::expected; + + auto madvise(int32_t advise) noexcept -> std::expected; + auto data() const noexcept -> std::span; + auto close() noexcept -> std::expected; +}; + +inline auto mmap::create(size_t length, int32_t prot, int32_t flags, k2::descriptor fd, uint64_t offset) noexcept -> std::expected { + k2::descriptor descriptor{k2::INVALID_PLATFORM_DESCRIPTOR}; + auto* addr{k2::mmap(std::addressof(descriptor), nullptr, length, prot, flags, fd, offset)}; + if (addr == MAP_FAILED) [[unlikely]] { + return std::unexpected{k2::errno_efault}; + } + return mmap{descriptor, addr, length}; +} + +inline auto mmap::madvise(int32_t advise) noexcept -> std::expected { + return k2::madvise(m_addr, m_length, advise); +} + +inline auto mmap::data() const noexcept -> std::span { + return {reinterpret_cast(m_addr), m_length}; +} + +inline auto mmap::close() noexcept -> std::expected { + if (m_descriptor == k2::INVALID_PLATFORM_DESCRIPTOR) [[unlikely]] { + return std::unexpected{k2::errno_enodev}; + } + k2::free_descriptor(std::exchange(m_descriptor, k2::INVALID_PLATFORM_DESCRIPTOR)); + return {}; +} + +// ================================================================================================ + struct resource : public refcountable_polymorphic_php_classes { const char* get_class() const noexcept final { return "resource"; @@ -146,7 +215,29 @@ inline auto file::pread(std::span buf, uint64_t offset) noexcept -> s } inline auto file::get_contents() noexcept -> std::expected { - return std::unexpected{m_descriptor != k2::INVALID_PLATFORM_DESCRIPTOR ? k2::errno_efault : k2::errno_enodev}; + struct stat stat_buf {}; + + if (auto expected{k2::fstat(m_descriptor, std::addressof(stat_buf))}; !expected.has_value()) { + return std::unexpected{expected.error()}; + } + if (!S_ISREG(stat_buf.st_mode)) { + return std::unexpected{k2::errno_einval}; + } + + const size_t size{static_cast(stat_buf.st_size)}; + if (size > string::max_size()) { + return std::unexpected{k2::errno_erange}; + } + + auto expected_mmap{kphp::fs::mmap::create(size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, m_descriptor, 0)}; + if (!expected_mmap.has_value()) { + return std::unexpected{expected_mmap.error()}; + } + + auto& mmap{*expected_mmap}; + std::ignore = mmap.madvise(MADV_SEQUENTIAL); + auto data{mmap.data()}; + return string{reinterpret_cast(data.data()), static_cast(data.size())}; } inline auto file::flush() noexcept -> std::expected { diff --git a/tests/phpt/dl/464_gzip.php b/tests/phpt/dl/464_gzip.php index 3e540200b8..0de0bce821 100644 --- a/tests/phpt/dl/464_gzip.php +++ b/tests/phpt/dl/464_gzip.php @@ -1,4 +1,4 @@ -@ok k2_skip +@ok