Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 8 additions & 27 deletions runtime-light/stdlib/file/file-system-functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
#include <unistd.h>
#include <utility>

#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"
Expand Down Expand Up @@ -204,40 +202,23 @@ inline Optional<string> f$file_get_contents(const string& stream) noexcept {
}

inline Optional<array<string>> 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<size_t>(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<std::byte, kphp::memory::script_allocator> 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)};

array<string> result;
int32_t prev{-1};
for (size_t i{0}; i < size; i++) {
if (static_cast<char>(file_content[i]) == '\n' || i + 1 == size) {
result.push_back(string{reinterpret_cast<char*>(file_content.data()) + prev + 1, static_cast<string::size_type>(i - prev)});
for (size_t i{0}; i < file_content.size(); i++) {
Comment thread
apolyakov marked this conversation as resolved.
Outdated
if (static_cast<char>(file_content[i]) == '\n' || i + 1 == file_content.size()) {
result.push_back(string{file_content.buffer() + prev + 1, static_cast<string::size_type>(i - prev)});
Comment thread
apolyakov marked this conversation as resolved.
Outdated
prev = i;
}
}
Expand Down
85 changes: 84 additions & 1 deletion runtime-light/stdlib/file/resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
#include <type_traits>
#include <utility>

#include <sys/mman.h>
#include <sys/stat.h>
Comment thread
apolyakov marked this conversation as resolved.
Outdated

#include "runtime-common/core/allocator/script-allocator.h"
#include "runtime-common/core/class-instance/refcountable-php-classes.h"
#include "runtime-common/core/runtime-core.h"
Expand All @@ -25,6 +28,7 @@
#include "runtime-light/coroutine/task.h"
#include "runtime-light/k2-platform/k2-api.h"
#include "runtime-light/server/http/http-server-state.h"
#include "runtime-light/stdlib/diagnostics/logs.h"
Comment thread
apolyakov marked this conversation as resolved.
Outdated
#include "runtime-light/stdlib/output/output-state.h"
#include "runtime-light/streams/stream.h"

Expand All @@ -45,6 +49,64 @@ inline constexpr std::string_view UDP_SCHEME_PREFIX = "udp://";

// ================================================================================================

class mmap {
k2::descriptor m_descriptor{k2::INVALID_PLATFORM_DESCRIPTOR};
std::span<const std::byte> m_data;

mmap(k2::descriptor descriptor, void* addr, size_t length) noexcept
: m_descriptor{descriptor},
m_data{reinterpret_cast<const std::byte*>(addr), length} {}

public:
mmap(mmap&& other) noexcept
: m_descriptor{std::exchange(other.m_descriptor, k2::INVALID_PLATFORM_DESCRIPTOR)},
m_data{std::exchange(other.m_data, {})} {}

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_data = std::exchange(other.m_data, {});
}
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<mmap, int32_t>;

auto data() noexcept -> std::span<const std::byte>;
Comment thread
apolyakov marked this conversation as resolved.
Outdated
auto close() noexcept -> std::expected<void, int32_t>;
};

inline auto mmap::create(size_t length, int32_t prot, int32_t flags, k2::descriptor fd, uint64_t offset) noexcept -> std::expected<mmap, int32_t> {
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::data() noexcept -> std::span<const std::byte> {
return m_data;
}

inline auto mmap::close() noexcept -> std::expected<void, int32_t> {
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<may_be_mixed_base> {
const char* get_class() const noexcept final {
return "resource";
Expand Down Expand Up @@ -146,7 +208,28 @@ inline auto file::pread(std::span<std::byte> buf, uint64_t offset) noexcept -> s
}

inline auto file::get_contents() noexcept -> std::expected<string, int32_t> {
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<size_t>(stat_buf.st_size)};
if (size > string::max_size()) {
return std::unexpected{k2::errno_enomem};
Comment thread
apolyakov marked this conversation as resolved.
Outdated
}

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 data{expected_mmap->data()};
std::ignore = k2::madvise(reinterpret_cast<void*>(const_cast<std::byte*>(data.data())), data.size(), MADV_SEQUENTIAL);
Comment thread
apolyakov marked this conversation as resolved.
Outdated
return string{reinterpret_cast<const char*>(data.data()), static_cast<string::size_type>(data.size())};
}

inline auto file::flush() noexcept -> std::expected<void, int32_t> {
Expand Down
2 changes: 1 addition & 1 deletion tests/phpt/dl/464_gzip.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@ok k2_skip
@ok
<?php
#ifndef KPHP
if(!function_exists('gzdecode')){
Expand Down
Loading