rpcs3/Utilities/File.h

843 lines
21 KiB
C
Raw Normal View History

#pragma once // No BOM and only basic ASCII in this header, or a neko will die
2015-04-24 21:38:11 +00:00
2020-12-12 12:01:29 +00:00
#include "util/types.hpp"
#include "util/shared_ptr.hpp"
2016-08-14 00:22:19 +00:00
#include "bit_set.h"
2016-02-01 21:55:43 +00:00
#include <memory>
#include <string>
#include <vector>
2017-02-11 16:05:35 +00:00
#include <algorithm>
2016-02-01 21:55:43 +00:00
namespace fs
2015-04-24 21:38:11 +00:00
{
2017-08-22 18:05:51 +00:00
#ifdef _WIN32
static constexpr auto& delim = "/\\";
static constexpr auto& wdelim = L"/\\";
2017-08-22 18:05:51 +00:00
using native_handle = void*;
#else
static constexpr auto& delim = "/";
static constexpr auto& wdelim = L"/";
2017-08-22 18:05:51 +00:00
using native_handle = int;
#endif
2016-02-01 21:55:43 +00:00
// File open mode flags
2016-08-07 19:01:27 +00:00
enum class open_mode : u32
{
2016-02-01 21:55:43 +00:00
read,
write,
append,
create,
trunc,
excl,
lock,
unread,
2022-01-29 19:10:48 +00:00
isfile,
2016-08-07 19:01:27 +00:00
__bitset_enum_max
};
2016-08-07 19:01:27 +00:00
constexpr auto read = +open_mode::read; // Enable reading
constexpr auto write = +open_mode::write; // Enable writing
constexpr auto append = +open_mode::append; // Always append to the end of the file
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
constexpr auto lock = +open_mode::lock; // Prevent opening the file more than once
constexpr auto unread = +open_mode::unread; // Aggressively prevent reading the opened file (do not use)
2022-01-29 19:10:48 +00:00
constexpr auto isfile = +open_mode::isfile; // Ensure valid fs::file handle is not of directory
2016-02-01 21:55:43 +00:00
constexpr auto write_new = write + create + excl;
constexpr auto rewrite = write + create + trunc;
2016-02-01 21:55:43 +00:00
// File seek mode
enum class seek_mode : u32
2016-01-05 23:52:48 +00:00
{
seek_set,
seek_cur,
seek_end,
};
2016-02-01 21:55:43 +00:00
constexpr auto seek_set = seek_mode::seek_set; // From beginning
constexpr auto seek_cur = seek_mode::seek_cur; // From current position
constexpr auto seek_end = seek_mode::seek_end; // From end
// File attributes (TODO)
2015-04-24 21:38:11 +00:00
struct stat_t
{
bool is_directory;
bool is_writable;
2015-04-25 19:15:53 +00:00
u64 size;
s64 atime;
s64 mtime;
s64 ctime;
using enable_bitcopy = std::true_type;
2022-09-13 13:08:55 +00:00
constexpr bool operator==(const stat_t&) const = default;
2015-04-24 21:38:11 +00:00
};
// Helper, layout is equal to iovec struct
struct iovec_clone
{
const void* iov_base;
2020-12-18 07:39:54 +00:00
usz iov_len;
};
2016-02-01 21:55:43 +00:00
// File handle base
struct file_base
{
2017-01-24 23:22:19 +00:00
virtual ~file_base();
2016-02-01 21:55:43 +00:00
[[noreturn]] virtual stat_t stat();
virtual void sync();
2016-02-01 21:55:43 +00:00
virtual bool trunc(u64 length) = 0;
virtual u64 read(void* buffer, u64 size) = 0;
virtual u64 write(const void* buffer, u64 size) = 0;
virtual u64 seek(s64 offset, seek_mode whence) = 0;
virtual u64 size() = 0;
2019-04-13 10:46:56 +00:00
virtual native_handle get_handle();
virtual u64 write_gather(const iovec_clone* buffers, u64 buf_count);
2016-02-01 21:55:43 +00:00
};
// Directory entry (TODO)
struct dir_entry : stat_t
{
2021-03-30 15:31:46 +00:00
std::string name{};
2021-02-17 19:58:10 +00:00
dir_entry()
: stat_t{}
{
}
using enable_bitcopy = std::false_type;
2016-02-01 21:55:43 +00:00
};
// Directory handle base
struct dir_base
{
2017-01-24 23:22:19 +00:00
virtual ~dir_base();
2016-02-01 21:55:43 +00:00
virtual bool read(dir_entry&) = 0;
virtual void rewind() = 0;
};
2017-04-24 15:45:58 +00:00
// Device information
struct device_stat
{
u64 block_size;
u64 total_size;
u64 total_free; // Total size of free space
u64 avail_free; // Free space available to unprivileged user
};
2016-02-01 21:55:43 +00:00
// Virtual device
struct device_base
{
const std::string fs_prefix;
device_base();
2017-01-24 23:22:19 +00:00
virtual ~device_base();
2016-02-01 21:55:43 +00:00
virtual bool stat(const std::string& path, stat_t& info) = 0;
2017-04-24 15:45:58 +00:00
virtual bool statfs(const std::string& path, device_stat& info) = 0;
virtual bool remove_dir(const std::string& path);
virtual bool create_dir(const std::string& path);
virtual bool rename(const std::string& from, const std::string& to);
virtual bool remove(const std::string& path);
virtual bool trunc(const std::string& path, u64 length);
virtual bool utime(const std::string& path, s64 atime, s64 mtime);
2016-02-01 21:55:43 +00:00
2016-08-07 19:01:27 +00:00
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
2016-02-01 21:55:43 +00:00
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
};
[[noreturn]] void xnull(const src_loc&);
[[noreturn]] void xfail(const src_loc&);
[[noreturn]] void xovfl();
constexpr struct pod_tag_t{} pod_tag;
2016-02-01 21:55:43 +00:00
// Get virtual device for specified path (nullptr for real path)
shared_ptr<device_base> get_virtual_device(const std::string& path);
2016-02-01 21:55:43 +00:00
// Set virtual device with specified name (nullptr for deletion)
shared_ptr<device_base> set_virtual_device(const std::string& name, shared_ptr<device_base> device);
2016-02-01 21:55:43 +00:00
// Try to get parent directory (returns empty string on failure)
std::string get_parent_dir(std::string_view path, u32 levels = 1);
2016-01-05 23:52:48 +00:00
// Get file information
2015-04-24 21:38:11 +00:00
bool stat(const std::string& path, stat_t& info);
// Check whether a file or a directory exists (not recommended, use is_file() or is_dir() instead)
2015-04-24 21:38:11 +00:00
bool exists(const std::string& path);
// Check whether the file exists and is NOT a directory
2016-01-05 23:52:48 +00:00
bool is_file(const std::string& path);
// Check whether the directory exists and is NOT a file
2016-01-05 23:52:48 +00:00
bool is_dir(const std::string& path);
2017-04-24 15:45:58 +00:00
// Get filesystem information
bool statfs(const std::string& path, device_stat& info);
// Delete empty directory
2016-01-05 23:52:48 +00:00
bool remove_dir(const std::string& path);
// Create directory
2016-01-05 23:52:48 +00:00
bool create_dir(const std::string& path);
// Create directories
2015-04-24 21:38:11 +00:00
bool create_path(const std::string& path);
// Rename (move) file or directory
bool rename(const std::string& from, const std::string& to, bool overwrite);
// Copy file contents
2015-04-24 21:38:11 +00:00
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
// Delete file
2016-01-05 23:52:48 +00:00
bool remove_file(const std::string& path);
// Change file size (possibly appending zeros)
2016-01-05 23:52:48 +00:00
bool truncate_file(const std::string& path, u64 length);
2015-04-24 21:38:11 +00:00
2017-01-26 12:01:21 +00:00
// Set file access/modification time
bool utime(const std::string& path, s64 atime, s64 mtime);
2021-02-23 10:09:20 +00:00
// Synchronize filesystems (TODO)
void sync();
class file final
2015-04-24 21:38:11 +00:00
{
2021-03-30 15:31:46 +00:00
std::unique_ptr<file_base> m_file{};
2015-04-24 21:38:11 +00:00
public:
2016-02-01 21:55:43 +00:00
// Default constructor
2015-04-25 19:15:53 +00:00
file() = default;
2015-04-24 21:38:11 +00:00
2016-02-01 21:55:43 +00:00
// Open file with specified mode
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
2016-02-01 21:55:43 +00:00
// Open memory for read
2020-12-18 07:39:54 +00:00
explicit file(const void* ptr, usz size);
2016-02-01 21:55:43 +00:00
2017-01-28 10:11:06 +00:00
// Open file with specified args (forward to constructor)
template <typename... Args>
bool open(Args&&... args)
{
m_file.reset();
2017-01-28 10:11:06 +00:00
*this = fs::file(std::forward<Args>(args)...);
return m_file.operator bool();
}
2016-02-01 21:55:43 +00:00
// Check whether the handle is valid (opened file)
explicit operator bool() const
{
2016-02-01 21:55:43 +00:00
return m_file.operator bool();
}
2015-04-24 21:38:11 +00:00
2016-02-01 21:55:43 +00:00
// Close the file explicitly
void close()
{
2016-02-01 21:55:43 +00:00
m_file.reset();
}
2016-02-01 21:55:43 +00:00
void reset(std::unique_ptr<file_base>&& ptr)
{
2016-02-01 21:55:43 +00:00
m_file = std::move(ptr);
}
2015-04-24 21:38:11 +00:00
2016-02-01 21:55:43 +00:00
std::unique_ptr<file_base> release()
{
2016-02-01 21:55:43 +00:00
return std::move(m_file);
}
// Change file size (possibly appending zero bytes)
bool trunc(u64 length,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->trunc(length);
}
// Get file information
stat_t stat(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->stat();
}
2015-04-24 21:38:11 +00:00
// Sync file buffers
void sync(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (!m_file) xnull({line, col, file, func});
return m_file->sync();
}
// Check if the handle is capable of reading (size * type_size) of bytes at the time of calling
bool strict_read_check(u64 size, u64 type_size = 1) const;
// Read the data from the file and return the amount of data written in buffer
u64 read(void* buffer, u64 count,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
2020-12-11 04:33:15 +00:00
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->read(buffer, count);
}
// Write the data to the file and return the amount of data actually written
u64 write(const void* buffer, u64 count,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
2020-12-11 04:33:15 +00:00
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->write(buffer, count);
}
// Change current position, returns resulting position
u64 seek(s64 offset, seek_mode whence = seek_set,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->seek(offset, whence);
}
// Get file size
u64 size(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->size();
}
// Get current position
u64 pos(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-02-01 21:55:43 +00:00
{
if (!m_file) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_file->seek(0, seek_cur);
}
// Write std::basic_string unconditionally
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
const file& write(const std::basic_string<T>& str,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (write(str.data(), str.size() * sizeof(T), line, col, file, func) != str.size() * sizeof(T)) xfail({line, col, file, func});
return *this;
}
2016-01-05 23:52:48 +00:00
// Write POD unconditionally
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
const file& write(const T& data,
pod_tag_t = pod_tag,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (write(std::addressof(data), sizeof(T), line, col, file, func) != sizeof(T)) xfail({line, col, file, func});
return *this;
}
2016-01-05 23:52:48 +00:00
// Write POD std::vector unconditionally
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
const file& write(const std::vector<T>& vec,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (write(vec.data(), vec.size() * sizeof(T), line, col, file, func) != vec.size() * sizeof(T)) xfail({line, col, file, func});
return *this;
}
// Read std::basic_string
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
bool read(std::basic_string<T>& str, usz _size = umax,
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION(),
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN()) const
2016-05-13 14:01:48 +00:00
{
if (!m_file) xnull({line, col, file, func});
if (_size != umax)
{
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
{
return false;
}
str.resize(_size);
}
return read(str.data(), sizeof(T) * str.size(), line, col, file, func) == sizeof(T) * str.size();
2016-05-13 14:01:48 +00:00
}
2016-01-05 23:52:48 +00:00
// Read POD, sizeof(T) is used
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
bool read(T& data,
pod_tag_t = pod_tag,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
return read(std::addressof(data), sizeof(T), line, col, file, func) == sizeof(T);
}
2016-05-13 14:01:48 +00:00
// Read POD std::vector
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
bool read(std::vector<T>& vec, usz _size = umax,
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION(),
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN()) const
2016-05-13 14:01:48 +00:00
{
if (!m_file) xnull({line, col, file, func});
if (_size != umax)
{
// If _size arg is too high std::bad_alloc may happen during resize and then we cannot error check
if ((_size >= 0x10'0000 / sizeof(T)) && !strict_read_check(_size, sizeof(T)))
{
return false;
}
vec.resize(_size);
}
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
2016-05-13 14:01:48 +00:00
}
2016-01-05 23:52:48 +00:00
// Read POD (experimental)
template <typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
T read(
2020-12-11 04:33:15 +00:00
pod_tag_t = pod_tag,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
2016-01-05 23:52:48 +00:00
T result;
if (!read(result, pod_tag, line, col, file, func)) xfail({line, col, file, func});
return result;
}
2016-01-05 23:52:48 +00:00
// Read full file to std::basic_string
template <typename T = char>
std::basic_string<T> to_string(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-01-05 23:52:48 +00:00
{
std::basic_string<T> result;
result.resize(size() / sizeof(T));
if (seek(0), !read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
2016-01-05 23:52:48 +00:00
return result;
}
2016-01-26 18:13:36 +00:00
// Read full file to std::vector
template<typename T> requires (std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>)
std::vector<T> to_vector(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
2016-01-26 18:13:36 +00:00
{
std::vector<T> result;
result.resize(size() / sizeof(T));
if (seek(0), !read(result, result.size(), file, func, line, col)) xfail({line, col, file, func});
2016-01-26 18:13:36 +00:00
return result;
}
2017-08-22 18:05:51 +00:00
// Get native handle if available
native_handle get_handle() const;
2019-04-07 20:55:38 +00:00
// Gathered write
u64 write_gather(const iovec_clone* buffers, u64 buf_count,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (!m_file) xnull({line, col, file, func});
return m_file->write_gather(buffers, buf_count);
}
};
2016-02-01 21:55:43 +00:00
class dir final
{
2021-03-30 15:31:46 +00:00
std::unique_ptr<dir_base> m_dir{};
2016-01-05 23:52:48 +00:00
public:
2016-02-01 21:55:43 +00:00
dir() = default;
2016-01-05 23:52:48 +00:00
2016-02-01 21:55:43 +00:00
// Open dir handle
explicit dir(const std::string& path)
2016-01-05 23:52:48 +00:00
{
2016-02-01 21:55:43 +00:00
open(path);
2016-01-05 23:52:48 +00:00
}
2016-02-01 21:55:43 +00:00
// Open specified directory
bool open(const std::string& path);
2016-01-05 23:52:48 +00:00
2016-02-01 21:55:43 +00:00
// Check whether the handle is valid (opened directory)
explicit operator bool() const
{
2016-02-01 21:55:43 +00:00
return m_dir.operator bool();
}
2015-04-24 21:38:11 +00:00
2016-02-01 21:55:43 +00:00
// Close the directory explicitly
void close()
{
2016-02-01 21:55:43 +00:00
m_dir.reset();
}
2015-04-25 19:15:53 +00:00
2016-02-01 21:55:43 +00:00
void reset(std::unique_ptr<dir_base>&& ptr)
{
2016-02-01 21:55:43 +00:00
m_dir = std::move(ptr);
}
2016-02-01 21:55:43 +00:00
std::unique_ptr<dir_base> release()
{
2016-02-01 21:55:43 +00:00
return std::move(m_dir);
}
2016-02-01 21:55:43 +00:00
// Get next directory entry
bool read(dir_entry& out,
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (!m_dir) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_dir->read(out);
}
2015-04-25 19:15:53 +00:00
2016-02-01 21:55:43 +00:00
// Reset to the beginning
void rewind(
u32 line = __builtin_LINE(),
u32 col = __builtin_COLUMN(),
const char* file = __builtin_FILE(),
const char* func = __builtin_FUNCTION()) const
{
if (!m_dir) xnull({line, col, file, func});
2016-02-01 21:55:43 +00:00
return m_dir->rewind();
}
2015-04-25 19:15:53 +00:00
class iterator
{
2017-11-15 17:56:31 +00:00
const dir* m_parent;
2021-03-30 15:31:46 +00:00
dir_entry m_entry{};
public:
enum class mode
{
from_first,
from_current
};
2017-11-15 17:56:31 +00:00
iterator(const dir* parent, mode mode_ = mode::from_first)
: m_parent(parent)
{
if (!m_parent)
{
return;
}
if (mode_ == mode::from_first)
{
2016-02-01 21:55:43 +00:00
m_parent->rewind();
}
2016-02-01 21:55:43 +00:00
if (!m_parent->read(m_entry))
{
m_parent = nullptr;
}
}
2021-03-30 15:31:46 +00:00
iterator(const iterator&) = default;
iterator(iterator&&) = default;
iterator& operator=(const iterator&) = default;
iterator& operator=(iterator&&) = default;
2016-02-01 21:55:43 +00:00
dir_entry& operator *()
{
return m_entry;
}
iterator& operator++()
{
2017-11-15 17:56:31 +00:00
*this = {m_parent, mode::from_current};
return *this;
}
bool operator !=(const iterator& rhs) const
{
return m_parent != rhs.m_parent;
}
};
2017-11-15 17:56:31 +00:00
iterator begin() const
{
2017-11-15 17:56:31 +00:00
return {m_dir ? this : nullptr};
}
2017-11-15 17:56:31 +00:00
iterator end() const
{
2017-11-15 17:56:31 +00:00
return {nullptr};
}
2015-04-25 19:15:53 +00:00
};
// Get configuration directory
2016-01-05 23:52:48 +00:00
const std::string& get_config_dir();
// Get common cache directory
const std::string& get_cache_dir();
// Temporary directory
const std::string& get_temp_dir();
2021-02-21 19:55:07 +00:00
// Unique pending file creation destined to be renamed to the destination file
struct pending_file
{
2021-03-30 15:31:46 +00:00
fs::file file{};
2021-02-21 19:55:07 +00:00
2021-02-23 09:08:22 +00:00
// This is meant to modify files atomically, overwriting is likely
2021-02-21 19:55:07 +00:00
bool commit(bool overwrite = true);
pending_file(std::string_view path);
pending_file(const pending_file&) = delete;
pending_file& operator=(const pending_file&) = delete;
2021-02-21 19:55:07 +00:00
~pending_file();
private:
2021-03-30 15:31:46 +00:00
std::string m_path{}; // Pending file path
std::string m_dest{}; // Destination file path
2021-02-21 19:55:07 +00:00
};
2016-02-01 21:55:43 +00:00
// Delete directory and all its contents recursively
bool remove_all(const std::string& path, bool remove_root = true, bool is_no_dir_ok = false);
2016-02-01 21:55:43 +00:00
// Get size of all files recursively
u64 get_dir_size(const std::string& path, u64 rounding_alignment = 1);
2016-05-13 14:01:48 +00:00
enum class error : uint
{
ok = 0,
inval,
noent,
exist,
2017-02-11 23:52:20 +00:00
acces,
2017-09-12 17:05:38 +00:00
notempty,
2019-09-02 02:50:44 +00:00
readonly,
isdir,
toolong,
2020-05-02 06:04:05 +00:00
nospace,
2020-03-07 08:18:23 +00:00
unknown
2016-05-13 14:01:48 +00:00
};
// Error code returned
extern thread_local error g_tls_error;
2017-02-11 16:05:35 +00:00
template <typename T>
struct container_stream final : file_base
{
// T can be a reference, but this is not recommended
using value_type = typename std::remove_reference_t<T>::value_type;
T obj;
u64 pos;
container_stream(T&& obj, const stat_t& init_stat = {})
2017-02-11 16:05:35 +00:00
: obj(std::forward<T>(obj))
, pos(0)
, m_stat(init_stat)
2017-02-11 16:05:35 +00:00
{
}
~container_stream() override
{
}
bool trunc(u64 length) override
{
obj.resize(length);
update_time(true);
2017-02-11 16:05:35 +00:00
return true;
}
u64 read(void* buffer, u64 size) override
{
const u64 end = obj.size();
if (pos < end)
{
// Get readable size
if (const u64 max = std::min<u64>(size, end - pos))
{
std::copy(obj.cbegin() + pos, obj.cbegin() + pos + max, static_cast<value_type*>(buffer));
pos = pos + max;
update_time();
2017-02-11 16:05:35 +00:00
return max;
}
}
return 0;
}
u64 write(const void* buffer, u64 size) override
{
const u64 old_size = obj.size();
if (old_size + size < old_size || pos + size < pos)
2017-02-11 16:05:35 +00:00
{
xovfl();
2017-02-11 16:05:35 +00:00
}
if (pos > old_size)
{
// Reserve memory
obj.reserve(pos + size);
2017-02-11 16:05:35 +00:00
// Fill gap if necessary (default-initialized)
obj.resize(pos);
}
const auto src = static_cast<const value_type*>(buffer);
// Overwrite existing part
const u64 overlap = std::min<u64>(obj.size() - pos, size);
std::copy(src, src + overlap, obj.begin() + pos);
// Append new data
obj.insert(obj.end(), src + overlap, src + size);
pos += size;
if (size) update_time(true);
2017-02-11 16:05:35 +00:00
return size;
}
u64 seek(s64 offset, seek_mode whence) override
{
2017-03-23 18:32:59 +00:00
const s64 new_pos =
whence == fs::seek_set ? offset :
whence == fs::seek_cur ? offset + pos :
whence == fs::seek_end ? offset + size() : -1;
2017-03-23 18:32:59 +00:00
if (new_pos < 0)
{
fs::g_tls_error = fs::error::inval;
return -1;
}
pos = new_pos;
return pos;
2017-02-11 16:05:35 +00:00
}
u64 size() override
{
return obj.size();
}
stat_t stat() override
{
return m_stat;
}
private:
stat_t m_stat{};
void update_time(bool write = false)
{
// TODO: Accurate timestamps
m_stat.atime++;
if (write)
{
2022-09-13 13:08:55 +00:00
m_stat.mtime++;
m_stat.mtime = std::max(m_stat.atime, m_stat.mtime);
m_stat.ctime = m_stat.mtime;
}
}
2017-02-11 16:05:35 +00:00
};
template <typename T>
file make_stream(T&& container = T{}, const stat_t& stat = stat_t{})
2017-02-11 16:05:35 +00:00
{
file result;
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container), stat));
2017-02-11 16:05:35 +00:00
return result;
}
2017-03-23 20:16:20 +00:00
template <bool Flush = false, typename... Args>
2017-03-23 20:16:20 +00:00
bool write_file(const std::string& path, bs_t<fs::open_mode> mode, const Args&... args)
{
// Always use write flag, remove read flag
if (fs::file f{path, mode + fs::write - fs::read})
2017-03-23 20:16:20 +00:00
{
if constexpr (sizeof...(args) == 2u && (std::is_pointer_v<Args> || ...))
{
// Specialization for [const void*, usz] args
f.write(args...);
}
else
{
// Write args sequentially
(f.write(args), ...);
}
if constexpr (Flush)
{
f.sync();
}
return true;
2017-03-23 20:16:20 +00:00
}
return false;
}
2018-08-12 22:06:03 +00:00
file make_gather(std::vector<file>);
stx::generator<dir_entry&> list_dir_recursively(std::string path);
2015-04-25 19:15:53 +00:00
}