Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup and expose FileAccessMemory. #98287

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions core/io/file_access.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ Ref<FileAccess> 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);
}
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions core/io/file_access.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class FileAccess : public RefCounted {
ACCESS_USERDATA,
ACCESS_FILESYSTEM,
ACCESS_PIPE,
ACCESS_MEMORY,
ACCESS_MAX
};

Expand Down
161 changes: 98 additions & 63 deletions core/io/file_access_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,129 +34,164 @@
#include "core/io/dir_access.h"
#include "core/templates/rb_map.h"

static HashMap<String, Vector<uint8_t>> *files = nullptr;

void FileAccessMemory::register_file(const String &p_name, const Vector<uint8_t> &p_data) {
if (!files) {
files = memnew((HashMap<String, Vector<uint8_t>>));
}

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<String, FileAccessMemory::FileInfo *> FileAccessMemory::files;

Ref<FileAccess> 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<String, Vector<uint8_t>>::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<String, FileAccessMemory::FileInfo *>::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();
}
43 changes: 26 additions & 17 deletions core/io/file_access_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,38 +32,46 @@
#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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we expand this struct to also represent uint8_t *data so we can combine and simplify the implementation?

SafeNumeric<uint64_t> refc;
};

static HashMap<String, FileInfo *> files;

String filename;
FileInfo *info = nullptr;
bool read_only = false;
uint8_t *data = nullptr;
uint64_t length = 0;
mutable uint64_t pos = 0;

static Ref<FileAccess> create();

public:
static void register_file(const String &p_name, const Vector<uint8_t> &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<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
Expand All @@ -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
7 changes: 7 additions & 0 deletions doc/classes/FileAccess.xml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@
<return type="int" />
<description>
Returns the size of the file in bytes.
[b]Note:[/b] This method always return [code]0[/code] if file is a pipe.
</description>
</method>
<method name="get_line" qualifiers="const">
Expand Down Expand Up @@ -237,6 +238,7 @@
<return type="int" />
<description>
Returns the file cursor's position.
[b]Note:[/b] This method always return [code]0[/code] if file is a pipe.
</description>
</method>
<method name="get_read_only_attribute" qualifiers="static">
Expand Down Expand Up @@ -289,6 +291,9 @@
<param index="1" name="flags" type="int" enum="FileAccess.ModeFlags" />
<description>
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.
</description>
</method>
Expand Down Expand Up @@ -336,13 +341,15 @@
<param index="0" name="position" type="int" />
<description>
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.
</description>
</method>
<method name="seek_end">
<return type="void" />
<param index="0" name="position" type="int" default="0" />
<description>
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.
</description>
</method>
Expand Down
2 changes: 2 additions & 0 deletions drivers/unix/os_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -162,6 +163,7 @@ void OS_Unix::initialize_core() {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
FileAccess::make_default<FileAccessUnixPipe>(FileAccess::ACCESS_PIPE);
FileAccess::make_default<FileAccessMemory>(FileAccess::ACCESS_MEMORY);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
Expand Down
Loading
Loading