From a1dac44d71d8222b04525e8861cfccbe5926c28a Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Fri, 18 Oct 2024 09:36:36 +0300 Subject: [PATCH] Cleanup and expose `FileAccessMemory`. --- core/io/file_access.cpp | 5 + core/io/file_access.h | 1 + core/io/file_access_memory.cpp | 161 +++++++++++++++++++------------- core/io/file_access_memory.h | 43 +++++---- doc/classes/FileAccess.xml | 7 ++ drivers/unix/os_unix.cpp | 2 + platform/windows/os_windows.cpp | 2 + 7 files changed, 141 insertions(+), 80 deletions(-) diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index d919243e6b7a..98c03d9985dd 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -76,6 +76,8 @@ Ref FileAccess::create_for_path(const String &p_path) { ret = create(ACCESS_USERDATA); } else if (p_path.begins_with("pipe://")) { ret = create(ACCESS_PIPE); + } else if (p_path.begins_with("mem://")) { + ret = create(ACCESS_MEMORY); } else { ret = create(ACCESS_FILESYSTEM); } @@ -212,6 +214,9 @@ String FileAccess::fix_path(const String &p_path) const { case ACCESS_PIPE: { return r_path; } break; + case ACCESS_MEMORY: { + return r_path; + } break; case ACCESS_FILESYSTEM: { return r_path; } break; diff --git a/core/io/file_access.h b/core/io/file_access.h index 2f4d1a860435..89e9fa3da389 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -51,6 +51,7 @@ class FileAccess : public RefCounted { ACCESS_USERDATA, ACCESS_FILESYSTEM, ACCESS_PIPE, + ACCESS_MEMORY, ACCESS_MAX }; diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 1541a5ed4a4d..7b897b3e02f3 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -34,129 +34,164 @@ #include "core/io/dir_access.h" #include "core/templates/rb_map.h" -static HashMap> *files = nullptr; - -void FileAccessMemory::register_file(const String &p_name, const Vector &p_data) { - if (!files) { - files = memnew((HashMap>)); - } - - String name; - if (ProjectSettings::get_singleton()) { - name = ProjectSettings::get_singleton()->globalize_path(p_name); - } else { - name = p_name; - } - //name = DirAccess::normalize_path(name); - - (*files)[name] = p_data; -} - -void FileAccessMemory::cleanup() { - if (!files) { - return; - } - - memdelete(files); -} +HashMap FileAccessMemory::files; Ref FileAccessMemory::create() { return memnew(FileAccessMemory); } bool FileAccessMemory::file_exists(const String &p_name) { - String name = fix_path(p_name); - //name = DirAccess::normalize_path(name); - - return files && (files->find(name) != nullptr); + String name = fix_path(p_name).simplify_path(); + return files.has(name); } Error FileAccessMemory::open_custom(const uint8_t *p_data, uint64_t p_len) { + close(); + data = (uint8_t *)p_data; length = p_len; - pos = 0; return OK; } Error FileAccessMemory::open_internal(const String &p_path, int p_mode_flags) { - ERR_FAIL_NULL_V(files, ERR_FILE_NOT_FOUND); - - String name = fix_path(p_path); - //name = DirAccess::normalize_path(name); + close(); - HashMap>::Iterator E = files->find(name); - ERR_FAIL_COND_V_MSG(!E, ERR_FILE_NOT_FOUND, "Can't find file '" + p_path + "'."); + String name = fix_path(p_path).simplify_path(); + HashMap::Iterator it = files.find(name); + if (it != nullptr) { + info = it->value; + } else { + info = memnew(FileAccessMemory::FileInfo); + files.insert(name, info); + } + ERR_FAIL_COND_V(!info, ERR_CANT_CREATE); - data = E->value.ptrw(); - length = E->value.size(); - pos = 0; + filename = name; + info->refc.increment(); + if (p_mode_flags == WRITE || p_mode_flags == WRITE_READ) { + info->data.clear(); + } + read_only = !(p_mode_flags & WRITE); return OK; } bool FileAccessMemory::is_open() const { - return data != nullptr; + return (data != nullptr) || (info != nullptr); } void FileAccessMemory::seek(uint64_t p_position) { - ERR_FAIL_NULL(data); + ERR_FAIL_COND(!data && !info); pos = p_position; } void FileAccessMemory::seek_end(int64_t p_position) { - ERR_FAIL_NULL(data); - pos = length + p_position; + ERR_FAIL_COND(!data && !info); + if (info) { + pos = (uint64_t)info->data.size() + p_position; + } else { + pos = length + p_position; + } } uint64_t FileAccessMemory::get_position() const { - ERR_FAIL_NULL_V(data, 0); + ERR_FAIL_COND_V(!data && !info, 0); return pos; } uint64_t FileAccessMemory::get_length() const { - ERR_FAIL_NULL_V(data, 0); - return length; + ERR_FAIL_COND_V(!data && !info, 0); + if (info) { + return (uint64_t)info->data.size(); + } else { + return length; + } } bool FileAccessMemory::eof_reached() const { - return pos >= length; + ERR_FAIL_COND_V(!data && !info, true); + if (info) { + return pos >= (uint64_t)info->data.size(); + } else { + return pos >= length; + } } uint64_t FileAccessMemory::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); - ERR_FAIL_NULL_V(data, -1); + ERR_FAIL_COND_V(!data && !info, -1); - uint64_t left = length - pos; - uint64_t read = MIN(p_length, left); - - if (read < p_length) { - WARN_PRINT("Reading less data than requested"); + uint64_t read = 0; + if (info) { + read = MIN(p_length, (uint64_t)info->data.size() - pos); + memcpy(p_dst, &info->data.ptr()[pos], read); + } else { + read = MIN(p_length, length - pos); + memcpy(p_dst, &data[pos], read); } - - memcpy(p_dst, &data[pos], read); pos += read; return read; } Error FileAccessMemory::get_error() const { - return pos >= length ? ERR_FILE_EOF : OK; + if (info) { + return pos >= (uint64_t)info->data.size() ? ERR_FILE_EOF : OK; + } else { + return pos >= length ? ERR_FILE_EOF : OK; + } } void FileAccessMemory::flush() { - ERR_FAIL_NULL(data); + ERR_FAIL_COND(!data && !info); } void FileAccessMemory::store_buffer(const uint8_t *p_src, uint64_t p_length) { ERR_FAIL_COND(!p_src && p_length > 0); + ERR_FAIL_COND(!data && !info); + ERR_FAIL_COND(read_only); + + uint64_t write = 0; + if (info) { + write = MIN(p_length, (uint64_t)info->data.size() - pos); + if (write < p_length) { + info->data.resize((uint64_t)info->data.size() + p_length); + write = MIN(p_length, (uint64_t)info->data.size() - pos); + } + memcpy(&info->data.ptrw()[pos], p_src, write); + } else { + write = MIN(p_length, length - pos); + memcpy(&data[pos], p_src, write); + } + pos += write; +} - uint64_t left = length - pos; - uint64_t write = MIN(p_length, left); +Error FileAccessMemory::resize(int64_t p_length) { + ERR_FAIL_COND_V(!data && !info, FAILED); + if (info) { + info->data.resize(p_length); + return OK; + } else { + return FAILED; + } +} - if (write < p_length) { - WARN_PRINT("Writing less data than requested"); +void FileAccessMemory::close() { + if (info) { + info->refc.decrement(); + if (info->refc.get() == 0) { + memdelete(info); + files.erase(filename); + } } + filename = String(); + info = nullptr; + read_only = false; + data = nullptr; + length = 0; + pos = 0; +} - memcpy(&data[pos], p_src, write); - pos += write; +FileAccessMemory::~FileAccessMemory() { + close(); } diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index 39e1528d9782..ec0ccbda0cc5 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -32,8 +32,19 @@ #define FILE_ACCESS_MEMORY_H #include "core/io/file_access.h" +#include "core/templates/safe_refcount.h" class FileAccessMemory : public FileAccess { + struct FileInfo { + PackedByteArray data; + SafeNumeric refc; + }; + + static HashMap files; + + String filename; + FileInfo *info = nullptr; + bool read_only = false; uint8_t *data = nullptr; uint64_t length = 0; mutable uint64_t pos = 0; @@ -41,29 +52,26 @@ class FileAccessMemory : public FileAccess { static Ref create(); public: - static void register_file(const String &p_name, const Vector &p_data); - static void cleanup(); - - virtual Error open_custom(const uint8_t *p_data, uint64_t p_len); ///< open a file - virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file - virtual bool is_open() const override; ///< true when file is open + virtual Error open_custom(const uint8_t *p_data, uint64_t p_len); + virtual Error open_internal(const String &p_path, int p_mode_flags) override; + virtual bool is_open() const override; - virtual void seek(uint64_t p_position) override; ///< seek to a given position - virtual void seek_end(int64_t p_position) override; ///< seek from the end of file - virtual uint64_t get_position() const override; ///< get position in the file - virtual uint64_t get_length() const override; ///< get size of the file + virtual void seek(uint64_t p_position) override; + virtual void seek_end(int64_t p_position) override; + virtual uint64_t get_position() const override; + virtual uint64_t get_length() const override; - virtual bool eof_reached() const override; ///< reading passed EOF + virtual bool eof_reached() const override; - virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; ///< get an array of bytes + virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override; - virtual Error get_error() const override; ///< get last error + virtual Error get_error() const override; - virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } + virtual Error resize(int64_t p_length) override; virtual void flush() override; - virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes + virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; - virtual bool file_exists(const String &p_name) override; ///< return true if a file exists + virtual bool file_exists(const String &p_name) override; virtual uint64_t _get_modified_time(const String &p_file) override { return 0; } virtual BitField _get_unix_permissions(const String &p_file) override { return 0; } @@ -74,9 +82,10 @@ class FileAccessMemory : public FileAccess { virtual bool _get_read_only_attribute(const String &p_file) override { return false; } virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; } - virtual void close() override {} + virtual void close() override; FileAccessMemory() {} + ~FileAccessMemory(); }; #endif // FILE_ACCESS_MEMORY_H diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml index 5888e3033909..6134a95cff9f 100644 --- a/doc/classes/FileAccess.xml +++ b/doc/classes/FileAccess.xml @@ -185,6 +185,7 @@ Returns the size of the file in bytes. + [b]Note:[/b] This method always return [code]0[/code] if file is a pipe. @@ -237,6 +238,7 @@ Returns the file cursor's position. + [b]Note:[/b] This method always return [code]0[/code] if file is a pipe. @@ -289,6 +291,9 @@ Creates a new [FileAccess] object and opens the file for writing or reading, depending on the flags. + To create named pipe, use [code]pipe://{pipe_name}[/code] as path. The pipe name string can be up to 240 characters long. + To create virtual file in memory, use [code]mem://{name}[/code] as path. + Pipes and memory files are automatically deleted when last [FileAccess] reference is closed. Returns [code]null[/code] if opening the file failed. You can use [method get_open_error] to check the error that occurred. @@ -336,6 +341,7 @@ Changes the file reading/writing cursor to the specified position (in bytes from the beginning of the file). + [b]Note:[/b] This method has no effect if file is a pipe. @@ -343,6 +349,7 @@ Changes the file reading/writing cursor to the specified position (in bytes from the end of the file). + [b]Note:[/b] This method has no effect if file is a pipe. [b]Note:[/b] This is an offset, so you should use negative numbers or the cursor will be at the end of the file. diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8a9b13006897..ecaf8b558405 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -35,6 +35,7 @@ #include "core/config/project_settings.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/io/file_access_memory.h" #include "drivers/unix/dir_access_unix.h" #include "drivers/unix/file_access_unix.h" #include "drivers/unix/file_access_unix_pipe.h" @@ -162,6 +163,7 @@ void OS_Unix::initialize_core() { FileAccess::make_default(FileAccess::ACCESS_USERDATA); FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); FileAccess::make_default(FileAccess::ACCESS_PIPE); + FileAccess::make_default(FileAccess::ACCESS_MEMORY); DirAccess::make_default(DirAccess::ACCESS_RESOURCES); DirAccess::make_default(DirAccess::ACCESS_USERDATA); DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM); diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index adc72a79e9cf..5eaaef62a4e3 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -38,6 +38,7 @@ #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/io/file_access_memory.h" #include "core/io/marshalls.h" #include "core/version_generated.gen.h" #include "drivers/unix/net_socket_posix.h" @@ -205,6 +206,7 @@ void OS_Windows::initialize() { FileAccess::make_default(FileAccess::ACCESS_USERDATA); FileAccess::make_default(FileAccess::ACCESS_FILESYSTEM); FileAccess::make_default(FileAccess::ACCESS_PIPE); + FileAccess::make_default(FileAccess::ACCESS_MEMORY); DirAccess::make_default(DirAccess::ACCESS_RESOURCES); DirAccess::make_default(DirAccess::ACCESS_USERDATA); DirAccess::make_default(DirAccess::ACCESS_FILESYSTEM);