vfs: refactor VFSNode allocation and handling of backend-specific data

VFSNode no longer has the generic void* data1 and data2 fields.
Introduced a macro system that allows backends to easily "subclass"
VFSNode to add extra fields.
This commit is contained in:
Andrei Alexeyev 2023-01-11 09:45:16 +01:00
parent b42899cc6a
commit 3169b16d10
No known key found for this signature in database
GPG key ID: 72D26128040B9690
25 changed files with 416 additions and 406 deletions

View file

@ -17,10 +17,6 @@
#include "rwops_zlib.h"
#include "rwops_zstd.h"
#ifdef TAISEI_BUILDCONF_USE_ZIP
#include "rwops_zipfile.h"
#endif
#ifdef DEBUG
#include "rwops_trace.h"
#endif

View file

@ -11,12 +11,6 @@ rwops_src = files(
'rwops_zstd.c',
)
if dep_zip.found()
rwops_src += files(
'rwops_zipfile.c',
)
endif
if is_debug_build
rwops_src += files(
'rwops_trace.c',

View file

@ -1,16 +0,0 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#pragma once
#include "taisei.h"
#include <SDL.h>
#include "vfs/zipfile_impl.h"
SDL_RWops *SDL_RWFromZipFile(VFSNode *znode, VFSZipPathData *pdata) attr_nonnull_all;

View file

@ -16,14 +16,14 @@
// NOTE: Largely based on readonly_wrapper. Sorry for the copypasta.
// This is currently hardcoded to only support transparent decompression of .zst files.
VFS_NODE_TYPE(VFSDecompNode, {
VFSNode *wrapped;
bool compr_zstd;
});
#define ZST_SUFFIX ".zst"
#define WRAPPED(n) ((VFSNode*)(n)->data1)
struct decomp_data {
uint compr_zstd : 1;
};
static_assert_nomsg(sizeof(struct decomp_data) <= sizeof(void*));
#define WRAPPED(node) VFS_NODE_CAST(VFSDecompNode, node)->wrapped
static char *vfs_decomp_repr(VFSNode *node) {
char *wrapped_repr = vfs_node_repr(WRAPPED(node), false);
@ -64,9 +64,8 @@ static bool vfs_decomp_mkdir(VFSNode *parent, const char *subdir) {
static VFSNode *vfs_decomp_locate(VFSNode *dirnode, const char *path) {
VFSNode *wrapped = WRAPPED(dirnode);
VFSNode *child = vfs_node_locate(wrapped, path);
struct decomp_data data = { 0 };
VFSNode *ochild = NULL;
bool zstd = false;
if(child != NULL && !vfs_node_query(child).exists) {
ochild = child;
@ -90,7 +89,7 @@ static VFSNode *vfs_decomp_locate(VFSNode *dirnode, const char *path) {
vfs_decref(child);
child = ochild;
} else {
data.compr_zstd = true;
zstd = true;
if(ochild) {
vfs_decref(ochild);
@ -106,7 +105,10 @@ static VFSNode *vfs_decomp_locate(VFSNode *dirnode, const char *path) {
VFSNode *wrapped_child = NOT_NULL(vfs_decomp_wrap(child));
vfs_decref(child);
memcpy(&wrapped_child->data2, &data, sizeof(data));
if(zstd) {
VFS_NODE_CAST(VFSDecompNode, wrapped_child)->compr_zstd = true;
}
return wrapped_child;
}
@ -117,16 +119,13 @@ static SDL_RWops *vfs_decomp_open(VFSNode *filenode, VFSOpenMode mode) {
return NULL;
}
struct decomp_data data;
memcpy(&data, &filenode->data2, sizeof(data));
SDL_RWops *raw = vfs_node_open(WRAPPED(filenode), mode);
if(!raw) {
return NULL;
}
if(data.compr_zstd) {
if(VFS_NODE_CAST(VFSDecompNode, filenode)->compr_zstd) {
return SDL_RWWrapZstdReaderSeekable(raw, -1, true);
}
@ -203,7 +202,7 @@ static void vfs_decomp_iter_stop(VFSNode *node, void **opaque) {
}
}
static VFSNodeFuncs vfs_funcs_decomp = {
VFS_NODE_FUNCS(VFSDecompNode, {
.repr = vfs_decomp_repr,
.query = vfs_decomp_query,
.free = vfs_decomp_free,
@ -215,7 +214,7 @@ static VFSNodeFuncs vfs_funcs_decomp = {
.open = vfs_decomp_open,
.mount = vfs_decomp_mount,
.unmount = vfs_decomp_unmount,
};
});
VFSNode *vfs_decomp_wrap(VFSNode *base) {
if(base == NULL) {
@ -224,10 +223,9 @@ VFSNode *vfs_decomp_wrap(VFSNode *base) {
vfs_incref(base);
VFSNode *wrapper = vfs_alloc();
wrapper->funcs = &vfs_funcs_decomp;
wrapper->data1 = base;
return wrapper;
return &VFS_ALLOC(VFSDecompNode, {
.wrapped = base,
})->as_generic;
}
bool vfs_make_decompress_view(const char *path) {
@ -252,7 +250,7 @@ bool vfs_make_decompress_view(const char *path) {
return false;
}
if(node->funcs == &vfs_funcs_decomp) {
if(VFS_NODE_TRY_CAST(VFSDecompNode, node)) {
vfs_decref(node);
vfs_decref(parent);
return true;

View file

@ -9,7 +9,6 @@ vfs_src = files(
'readonly_wrapper.c',
'syspath_public.c',
'union.c',
'union_public.c',
'vdir.c',
'zipfile_public.c',
)
@ -18,6 +17,7 @@ if dep_zip.found()
vfs_src += files(
'zipfile.c',
'zippath.c',
'zippath_rwops.c',
)
else
vfs_src += files(

View file

@ -18,7 +18,7 @@ VFSInfo vfs_node_query(VFSNode *node) {
return node->funcs->query(node);
}
VFSNode* vfs_node_locate(VFSNode *root, const char *path) {
VFSNode *vfs_node_locate(VFSNode *root, const char *path) {
assert(root->funcs != NULL);
#ifndef NDEBUG
@ -36,7 +36,7 @@ VFSNode* vfs_node_locate(VFSNode *root, const char *path) {
return root->funcs->locate(root, path);
}
const char* vfs_node_iter(VFSNode *node, void **opaque) {
const char *vfs_node_iter(VFSNode *node, void **opaque) {
assert(node->funcs != NULL);
if(node->funcs->iter != NULL) {
@ -56,7 +56,7 @@ void vfs_node_iter_stop(VFSNode *node, void **opaque) {
}
}
char* vfs_node_syspath(VFSNode *node) {
char *vfs_node_syspath(VFSNode *node) {
assert(node->funcs != NULL);
if(node->funcs->syspath == NULL) {
@ -67,7 +67,7 @@ char* vfs_node_syspath(VFSNode *node) {
return node->funcs->syspath(node);
}
char* vfs_node_repr(VFSNode *node, bool try_syspath) {
char *(vfs_node_repr)(VFSNode *node, bool try_syspath) {
assert(node->funcs != NULL);
assert(node->funcs->repr != NULL);
char *r;
@ -122,7 +122,7 @@ bool vfs_node_mkdir(VFSNode *parent, const char *subdir) {
return parent->funcs->mkdir(parent, subdir);
}
SDL_RWops* vfs_node_open(VFSNode *filenode, VFSOpenMode mode) {
SDL_RWops *vfs_node_open(VFSNode *filenode, VFSOpenMode mode) {
assert(filenode->funcs != NULL);
if(filenode->funcs->open == NULL) {

View file

@ -55,9 +55,7 @@ static vfs_tls_t* vfs_tls_get(void) {
}
void vfs_init(void) {
vfs_root = vfs_alloc();
vfs_vdir_init(vfs_root);
vfs_root = vfs_vdir_create();
vfs_tls_id = SDL_TLSCreate();
if(vfs_tls_id) {
@ -95,12 +93,6 @@ void vfs_hook_on_shutdown(VFSShutdownHandler func, void *arg) {
}));
}
VFSNode* vfs_alloc(void) {
auto node = ALLOC(VFSNode);
vfs_incref(node);
return node;
}
static void vfs_free(VFSNode *node) {
if(!node) {
return;
@ -113,11 +105,11 @@ static void vfs_free(VFSNode *node) {
mem_free(node);
}
void vfs_incref(VFSNode *node) {
void (vfs_incref)(VFSNode *node) {
SDL_AtomicIncRef(&node->refcount);
}
bool vfs_decref(VFSNode *node) {
bool (vfs_decref)(VFSNode *node) {
if(!node) {
return true;
}
@ -130,7 +122,7 @@ bool vfs_decref(VFSNode *node) {
return false;
}
VFSNode* vfs_locate(VFSNode *root, const char *path) {
VFSNode *(vfs_locate)(VFSNode *root, const char *path) {
if(!*path) {
vfs_incref(root);
return root;

View file

@ -38,34 +38,98 @@ struct VFSNodeFuncs {
};
struct VFSNode {
VFSNodeFuncs *funcs;
char as_generic[0];
const VFSNodeFuncs *funcs;
SDL_atomic_t refcount;
void *data1;
void *data2;
};
#define VFS_NODE_TYPE_FUNCS_NAME(_typename) _vfs_funcs_##_typename
#define VFS_NODE_TYPE(_typename, ...) \
typedef struct _typename { \
VFSNode as_generic; \
struct __VA_ARGS__; \
} _typename; \
extern const VFSNodeFuncs VFS_NODE_TYPE_FUNCS_NAME(_typename)
#define VFS_NODE_FUNCS(_typename, ...) \
const VFSNodeFuncs VFS_NODE_TYPE_FUNCS_NAME(_typename) = __VA_ARGS__
#define IS_OF_VFS_NODE_BASE_TYPE(_node) \
__builtin_types_compatible_p(VFSNode, typeof(*(_node)))
#define IS_OF_VFS_NODE_DERIVED_TYPE(_node) \
(__builtin_types_compatible_p(VFSNode, typeof((_node)->as_generic)) \
&& offsetof(typeof(*(_node)), as_generic) == 0)
#define IS_OF_VFS_NODE_TYPE(_node) \
IS_OF_VFS_NODE_BASE_TYPE(_node) || IS_OF_VFS_NODE_DERIVED_TYPE(_node)
#define VFS_NODE_ASSERT_VALID(_node) \
static_assert(IS_OF_VFS_NODE_TYPE(_node), "Expected a VFSNode or derived type")
#define VFS_NODE_AS_BASE(_node) ({ \
VFS_NODE_ASSERT_VALID(_node); \
__builtin_choose_expr(IS_OF_VFS_NODE_BASE_TYPE(_node), (_node), &(_node)->as_generic); \
})
#define VFS_NODE_TRY_CAST(_typename, _node) ({ \
VFSNode *_base_node = _node; \
static_assert(IS_OF_VFS_NODE_DERIVED_TYPE( (&(_typename){}) ), \
"Typename must be of a VFSNode-derived type"); \
_typename* _n = UNION_CAST(VFSNode*, _typename*, NOT_NULL(_base_node)); \
LIKELY(_n->as_generic.funcs == &VFS_NODE_TYPE_FUNCS_NAME(_typename))? _n : NULL; \
})
#define VFS_NODE_CAST(_typename, _node) \
NOT_NULL(VFS_NODE_TRY_CAST(_typename, _node))
#define VFS_ALLOC(_typename, ...) ({ \
auto _node = ALLOC(_typename, ##__VA_ARGS__); \
static_assert(IS_OF_VFS_NODE_DERIVED_TYPE(_node), \
"Typename must be of a VFSNode-derived type"); \
_node->as_generic.funcs = &VFS_NODE_TYPE_FUNCS_NAME(_typename); \
vfs_incref(_node); \
_node; \
})
extern VFSNode *vfs_root;
VFSNode* vfs_alloc(void);
void vfs_incref(VFSNode *node) attr_nonnull(1);
bool vfs_decref(VFSNode *node);
bool vfs_mount(VFSNode *root, const char *mountpoint, VFSNode *subtree) attr_nonnull(1, 3) attr_nodiscard;
bool vfs_mount_or_decref(VFSNode *root, const char *mountpoint, VFSNode *subtree) attr_nonnull(1, 3) attr_nodiscard;
VFSNode* vfs_locate(VFSNode *root, const char *path) attr_nonnull(1, 2) attr_nodiscard;
VFSNode *vfs_locate(VFSNode *root, const char *path) attr_nonnull(1, 2) attr_nodiscard;
// Light wrappers around the virtual functions, safe to call even on nodes that
// don't implement the corresponding method. "free" is not included, there should
// be no reason to call it. It wouldn't do what you'd expect anyway; use vfs_decref.
char* vfs_node_repr(VFSNode *node, bool try_syspath) attr_returns_allocated attr_nonnull(1);
char *vfs_node_repr(VFSNode *node, bool try_syspath) attr_returns_allocated attr_nonnull(1);
VFSInfo vfs_node_query(VFSNode *node) attr_nonnull(1) attr_nodiscard attr_nonnull(1);
char* vfs_node_syspath(VFSNode *node) attr_nonnull(1) attr_returns_max_aligned attr_nodiscard attr_nonnull(1);
char *vfs_node_syspath(VFSNode *node) attr_nonnull(1) attr_returns_max_aligned attr_nodiscard attr_nonnull(1);
bool vfs_node_mount(VFSNode *mountroot, const char *subname, VFSNode *mountee) attr_nonnull(1, 3);
bool vfs_node_unmount(VFSNode *mountroot, const char *subname) attr_nonnull(1);
VFSNode* vfs_node_locate(VFSNode *root, const char *path) attr_nonnull(1, 2) attr_nodiscard;
const char* vfs_node_iter(VFSNode *node, void **opaque) attr_nonnull(1);
VFSNode *vfs_node_locate(VFSNode *root, const char *path) attr_nonnull(1, 2) attr_nodiscard;
const char *vfs_node_iter(VFSNode *node, void **opaque) attr_nonnull(1);
void vfs_node_iter_stop(VFSNode *node, void **opaque) attr_nonnull(1);
bool vfs_node_mkdir(VFSNode *parent, const char *subdir) attr_nonnull(1);
SDL_RWops* vfs_node_open(VFSNode *filenode, VFSOpenMode mode) attr_nonnull(1) attr_nodiscard;
SDL_RWops *vfs_node_open(VFSNode *filenode, VFSOpenMode mode) attr_nonnull(1) attr_nodiscard;
// NOTE: convenience wrappers added on demand
#define vfs_incref(node) \
vfs_incref(VFS_NODE_AS_BASE(node))
#define vfs_decref(node) \
vfs_decref(VFS_NODE_AS_BASE(node))
#define vfs_locate(root, path) \
vfs_locate(VFS_NODE_AS_BASE(root), path)
#define vfs_node_repr(node, try_syspath) \
vfs_node_repr(VFS_NODE_AS_BASE(node), try_syspath)
void vfs_hook_on_shutdown(VFSShutdownHandler, void *arg);
void vfs_print_tree_recurse(SDL_RWops *dest, VFSNode *root, char *prefix, const char *name) attr_nonnull(1, 2, 3, 4);

View file

@ -12,9 +12,13 @@
#include "private.h"
#include "rwops/rwops_ro.h"
#define WRAPPED(n) ((VFSNode*)(n)->data1)
VFS_NODE_TYPE(VFSReadOnlyNode, {
VFSNode *wrapped;
});
static char* vfs_ro_repr(VFSNode *node) {
#define WRAPPED(n) VFS_NODE_CAST(VFSReadOnlyNode, n)->wrapped
static char *vfs_ro_repr(VFSNode *node) {
char *wrapped_repr = vfs_node_repr(WRAPPED(node), false);
char *repr = strjoin("read-only view of ", wrapped_repr, NULL);
mem_free(wrapped_repr);
@ -80,7 +84,7 @@ static SDL_RWops* vfs_ro_open(VFSNode *filenode, VFSOpenMode mode) {
return SDL_RWWrapReadOnly(vfs_node_open(WRAPPED(filenode), mode), true);
}
static VFSNodeFuncs vfs_funcs_ro = {
VFS_NODE_FUNCS(VFSReadOnlyNode, {
.repr = vfs_ro_repr,
.query = vfs_ro_query,
.free = vfs_ro_free,
@ -92,9 +96,9 @@ static VFSNodeFuncs vfs_funcs_ro = {
.open = vfs_ro_open,
.mount = vfs_ro_mount,
.unmount = vfs_ro_unmount,
};
});
VFSNode* vfs_ro_wrap(VFSNode *base) {
VFSNode *vfs_ro_wrap(VFSNode *base) {
if(base == NULL) {
return NULL;
}
@ -106,10 +110,9 @@ VFSNode* vfs_ro_wrap(VFSNode *base) {
return base;
}
VFSNode *wrapper = vfs_alloc();
wrapper->funcs = &vfs_funcs_ro;
wrapper->data1 = base;
return wrapper;
return &VFS_ALLOC(VFSReadOnlyNode, {
.wrapped = base,
})->as_generic;
}
bool vfs_make_readonly(const char *path) {

View file

@ -13,4 +13,4 @@
#include "syspath_public.h"
extern char *vfs_syspath_separators;
bool vfs_syspath_init(VFSNode *node, const char *path);
VFSNode *vfs_syspath_create(const char *path);

View file

@ -16,21 +16,23 @@
#include "syspath.h"
#define _path_ data1
VFS_NODE_TYPE(VFSSysPathNode, {
char *path;
});
char *vfs_syspath_separators = "/";
static void vfs_syspath_init_internal(VFSNode *node, char *path);
static VFSNode *vfs_syspath_create_internal(char *path);
static void vfs_syspath_free(VFSNode *node) {
mem_free(node->_path_);
mem_free(VFS_NODE_CAST(VFSSysPathNode, node)->path);
}
static VFSInfo vfs_syspath_query(VFSNode *node) {
struct stat fstat;
VFSInfo i = {0};
if(stat(node->_path_, &fstat) >= 0) {
if(stat(VFS_NODE_CAST(VFSSysPathNode, node)->path, &fstat) >= 0) {
i.exists = true;
i.is_dir = S_ISDIR(fstat.st_mode);
}
@ -38,9 +40,10 @@ static VFSInfo vfs_syspath_query(VFSNode *node) {
return i;
}
static SDL_RWops* vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
static SDL_RWops *vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
mode &= VFS_MODE_RWMASK;
SDL_RWops *rwops = SDL_RWFromFile(node->_path_, mode == VFS_MODE_WRITE ? "w" : "r");
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
SDL_RWops *rwops = SDL_RWFromFile(pnode->path, mode == VFS_MODE_WRITE ? "w" : "r");
if(!rwops) {
vfs_set_error_from_sdl();
@ -49,18 +52,17 @@ static SDL_RWops* vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
return rwops;
}
static VFSNode* vfs_syspath_locate(VFSNode *node, const char *path) {
VFSNode *n = vfs_alloc();
vfs_syspath_init_internal(n, strfmt("%s%c%s", (char*)node->_path_, VFS_PATH_SEPARATOR, path));
return n;
static VFSNode *vfs_syspath_locate(VFSNode *node, const char *path) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
return vfs_syspath_create_internal(strjoin(pnode->path, "/", path, NULL));
}
static const char* vfs_syspath_iter(VFSNode *node, void **opaque) {
static const char *vfs_syspath_iter(VFSNode *node, void **opaque) {
DIR *dir;
struct dirent *e;
if(!*opaque) {
*opaque = opendir(node->_path_);
*opaque = opendir(VFS_NODE_CAST(VFSSysPathNode, node)->path);
}
if(!(dir = *opaque)) {
@ -87,12 +89,13 @@ static void vfs_syspath_iter_stop(VFSNode *node, void **opaque) {
}
}
static char* vfs_syspath_repr(VFSNode *node) {
return strfmt("filesystem path (posix): %s", (char*)node->_path_);
static char *vfs_syspath_repr(VFSNode *node) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
return strfmt("filesystem path (posix): %s", pnode->path);
}
static char* vfs_syspath_syspath(VFSNode *node) {
char *p = strdup(node->_path_);
static char *vfs_syspath_syspath(VFSNode *node) {
char *p = strdup(VFS_NODE_CAST(VFSSysPathNode, node)->path);
vfs_syspath_normalize_inplace(p);
return p;
}
@ -102,7 +105,8 @@ static bool vfs_syspath_mkdir(VFSNode *node, const char *subdir) {
subdir = "";
}
char *p = strfmt("%s%c%s", (char*)node->_path_, VFS_PATH_SEPARATOR, subdir);
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
char *p = strjoin(pnode->path, VFS_PATH_SEPARATOR_STR, subdir, NULL);
bool ok = !mkdir(p, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
if(!ok && errno == EEXIST) {
@ -125,7 +129,7 @@ static bool vfs_syspath_mkdir(VFSNode *node, const char *subdir) {
return ok;
}
static VFSNodeFuncs vfs_funcs_syspath = {
VFS_NODE_FUNCS(VFSSysPathNode, {
.repr = vfs_syspath_repr,
.query = vfs_syspath_query,
.free = vfs_syspath_free,
@ -135,7 +139,7 @@ static VFSNodeFuncs vfs_funcs_syspath = {
.iter_stop = vfs_syspath_iter_stop,
.mkdir = vfs_syspath_mkdir,
.open = vfs_syspath_open,
};
});
void vfs_syspath_normalize(char *buf, size_t bufsize, const char *path) {
char *bufptr = buf;
@ -174,13 +178,13 @@ void vfs_syspath_join(char *buf, size_t bufsize, const char *parent, const char
}
}
static void vfs_syspath_init_internal(VFSNode *node, char *path) {
static VFSNode *vfs_syspath_create_internal(char *path) {
vfs_syspath_normalize_inplace(path);
node->funcs = &vfs_funcs_syspath;
node->_path_ = path;
return &VFS_ALLOC(VFSSysPathNode, {
.path = path,
})->as_generic;
}
bool vfs_syspath_init(VFSNode *node, const char *path) {
vfs_syspath_init_internal(node, strdup(path));
return true;
VFSNode *vfs_syspath_create(const char *path) {
return vfs_syspath_create_internal(strdup(path));
}

View file

@ -41,9 +41,8 @@ static bool mkdir_with_parents(VFSNode *n, const char *fspath) {
return false;
}
VFSNode *pnode = vfs_alloc();
if(!vfs_syspath_init(pnode, p)) {
vfs_decref(pnode);
VFSNode *pnode;
if(!(pnode = vfs_syspath_create(p))) {
return false;
}
@ -53,9 +52,9 @@ static bool mkdir_with_parents(VFSNode *n, const char *fspath) {
}
bool vfs_mount_syspath(const char *mountpoint, const char *fspath, uint flags) {
VFSNode *rdir = vfs_alloc();
VFSNode *rdir;
if(!vfs_syspath_init(rdir, fspath)) {
if(!(rdir = vfs_syspath_create(fspath))) {
vfs_set_error("Can't initialize path: %s", vfs_get_error());
vfs_decref(rdir);
return false;

View file

@ -14,8 +14,10 @@
#include "syspath.h"
#define _path_ data1
#define _wpath_ data2
VFS_NODE_TYPE(VFSSysPathNode, {
char *path;
wchar_t *wpath;
});
char *vfs_syspath_separators = "\\/";
@ -39,11 +41,12 @@ static WCHAR *WIN_UTF8ToString(char *s) {
return buf;
}
static bool vfs_syspath_init_internal(VFSNode *node, char *path);
static VFSNode *vfs_syspath_create_internal(char *path);
static void vfs_syspath_free(VFSNode *node) {
mem_free(node->_path_);
mem_free(node->_wpath_);
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
mem_free(pnode->path);
mem_free(pnode->wpath);
}
static void _vfs_set_error_win32(const char *file, int line) {
@ -70,14 +73,15 @@ static void _vfs_set_error_win32(const char *file, int line) {
#define vfs_set_error_win32() _vfs_set_error_win32(_TAISEI_SRC_FILE, __LINE__)
static VFSInfo vfs_syspath_query(VFSNode *node) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
VFSInfo i = {0};
if(!PathFileExists(node->_wpath_)) {
if(!PathFileExists(pnode->wpath)) {
i.exists = false;
return i;
}
DWORD attrib = GetFileAttributes(node->_wpath_);
DWORD attrib = GetFileAttributes(pnode->wpath);
if(attrib == INVALID_FILE_ATTRIBUTES) {
vfs_set_error_win32();
@ -90,9 +94,10 @@ static VFSInfo vfs_syspath_query(VFSNode *node) {
return i;
}
static SDL_RWops* vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
static SDL_RWops *vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
mode &= VFS_MODE_RWMASK;
SDL_RWops *rwops = SDL_RWFromFile(node->_path_, mode == VFS_MODE_WRITE ? "w" : "r");
SDL_RWops *rwops = SDL_RWFromFile(pnode->path, mode == VFS_MODE_WRITE ? "w" : "r");
if(!rwops) {
vfs_set_error_from_sdl();
@ -101,18 +106,9 @@ static SDL_RWops* vfs_syspath_open(VFSNode *node, VFSOpenMode mode) {
return rwops;
}
static VFSNode* vfs_syspath_locate(VFSNode *node, const char *path) {
VFSNode *n = vfs_alloc();
char buf[strlen(path)+1], *base, *name;
strcpy(buf, path);
vfs_path_split_right(buf, &base, &name);
if(!vfs_syspath_init_internal(n, strfmt("%s%c%s", (char*)node->_path_, '\\', path))) {
vfs_decref(n);
return NULL;
}
return n;
static VFSNode *vfs_syspath_locate(VFSNode *node, const char *path) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
return vfs_syspath_create_internal(strjoin(pnode->path, "\\", path, NULL));
}
typedef struct VFSWin32IterData {
@ -121,12 +117,13 @@ typedef struct VFSWin32IterData {
} VFSWin32IterData;
static const char* vfs_syspath_iter(VFSNode *node, void **opaque) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
VFSWin32IterData *idata;
WIN32_FIND_DATA fdata;
HANDLE search_handle;
if(!*opaque) {
char *pattern = strjoin(node->_path_, "\\*.*", NULL);
char *pattern = strjoin(pnode->path, "\\*.*", NULL);
wchar_t *wpattern = WIN_UTF8ToString(pattern);
mem_free(pattern);
search_handle = FindFirstFile(wpattern, &fdata);
@ -185,21 +182,25 @@ static void vfs_syspath_iter_stop(VFSNode *node, void **opaque) {
}
static char* vfs_syspath_repr(VFSNode *node) {
return strfmt("filesystem path (win32): %s", (char*)node->_path_);
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
return strfmt("filesystem path (win32): %s", pnode->path);
}
static char* vfs_syspath_syspath(VFSNode *node) {
char *p = strdup(node->_path_);
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
char *p = strdup(pnode->path);
vfs_syspath_normalize_inplace(p);
return p;
}
static bool vfs_syspath_mkdir(VFSNode *node, const char *subdir) {
auto pnode = VFS_NODE_CAST(VFSSysPathNode, node);
if(!subdir) {
subdir = "";
}
char *p = strfmt("%s%c%s", (char*)node->_path_, '\\', subdir);
char *p = strjoin(pnode->path, "\\", subdir, NULL);
wchar_t *wp = WIN_UTF8ToString(p);
bool ok = CreateDirectory(wp, NULL);
DWORD err = GetLastError();
@ -225,7 +226,7 @@ static bool vfs_syspath_mkdir(VFSNode *node, const char *subdir) {
return ok;
}
static VFSNodeFuncs vfs_funcs_syspath = {
VFS_NODE_FUNCS(VFSSysPathNode, {
.repr = vfs_syspath_repr,
.query = vfs_syspath_query,
.free = vfs_syspath_free,
@ -235,7 +236,7 @@ static VFSNodeFuncs vfs_funcs_syspath = {
.iter_stop = vfs_syspath_iter_stop,
.mkdir = vfs_syspath_mkdir,
.open = vfs_syspath_open,
};
});
void vfs_syspath_normalize(char *buf, size_t bufsize, const char *path) {
// here we just remove redundant separators and convert forward slashes to back slashes.
@ -308,20 +309,19 @@ static bool vfs_syspath_validate(char *path) {
return true;
}
static bool vfs_syspath_init_internal(VFSNode *node, char *path) {
static VFSNode *vfs_syspath_create_internal(char *path) {
vfs_syspath_normalize_inplace(path);
if(!vfs_syspath_validate(path)) {
return false;
return NULL;
}
node->funcs = &vfs_funcs_syspath;
node->_path_ = path;
node->_wpath_ = WIN_UTF8ToString(path);
return true;
return &VFS_ALLOC(VFSSysPathNode, {
.path = path,
.wpath = WIN_UTF8ToString(path),
})->as_generic;
}
bool vfs_syspath_init(VFSNode *node, const char *path) {
return vfs_syspath_init_internal(node, strdup(path));
VFSNode *vfs_syspath_create(const char *path) {
return vfs_syspath_create_internal(strdup(path));
}

View file

@ -10,12 +10,16 @@
#include "union.h"
#define _members_ data1
#define _primary_member_ data2
// TODO rewrite this to use more sensible data structures
static bool vfs_union_mount_internal(VFSNode *unode, const char *mountpoint, VFSNode *mountee, VFSInfo info, bool seterror);
VFS_NODE_TYPE(VFSUnionNode, {
ListContainer *members;
VFSNode *primary_member;
});
static void* vfs_union_delete_callback(List **list, List *elem, void *arg) {
static bool vfs_union_mount_internal(VFSUnionNode *unode, const char *mountpoint, VFSNode *mountee, VFSInfo info, bool seterror);
static void *vfs_union_delete_callback(List **list, List *elem, void *arg) {
ListContainer *c = (ListContainer*)elem;
VFSNode *n = c->data;
vfs_decref(n);
@ -24,15 +28,16 @@ static void* vfs_union_delete_callback(List **list, List *elem, void *arg) {
}
static void vfs_union_free(VFSNode *node) {
list_foreach((ListContainer**)&node->_members_, vfs_union_delete_callback, NULL);
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
list_foreach(&unode->members, vfs_union_delete_callback, NULL);
}
static VFSNode* vfs_union_locate(VFSNode *node, const char *path) {
VFSNode *u = vfs_alloc();
vfs_union_init(u); // uniception!
static VFSNode *vfs_union_locate(VFSNode *node, const char *path) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
auto subunion = VFS_NODE_CAST(VFSUnionNode, vfs_union_create());
VFSInfo prim_info = VFSINFO_ERROR;
ListContainer *first = node->_members_;
ListContainer *first = unode->members;
ListContainer *last = first;
ListContainer *c;
@ -47,7 +52,7 @@ static VFSNode* vfs_union_locate(VFSNode *node, const char *path) {
if(o) {
VFSInfo i = vfs_node_query(o);
if(vfs_union_mount_internal(u, NULL, o, i, false)) {
if(vfs_union_mount_internal(subunion, NULL, o, i, false)) {
prim_info = i;
} else {
vfs_decref(o);
@ -55,28 +60,28 @@ static VFSNode* vfs_union_locate(VFSNode *node, const char *path) {
}
}
if(u->_primary_member_) {
if(!((List*)(u->_members_))->next || !prim_info.is_dir) {
if(subunion->primary_member) {
if(!subunion->members->next || !prim_info.is_dir) {
// the temporary union contains just one member, or doesn't represent a directory
// in those cases it's just a useless wrapper, so let's just return the primary member directly
VFSNode *n = u->_primary_member_;
VFSNode *n = subunion->primary_member;
// incref primary member to keep it alive
vfs_incref(n);
// destroy the wrapper, also decrefs n
vfs_decref(u);
vfs_decref(subunion);
// no need to incref n, vfs_locate did that for us earlier
return n;
}
} else {
// all in vain...
vfs_decref(u);
u = NULL;
vfs_decref(subunion);
return NULL;
}
return u;
return &subunion->as_generic;
}
typedef struct VFSUnionIterData {
@ -86,12 +91,13 @@ typedef struct VFSUnionIterData {
} VFSUnionIterData;
static const char* vfs_union_iter(VFSNode *node, void **opaque) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
VFSUnionIterData *i = *opaque;
const char *r = NULL;
if(!i) {
i = ALLOC(typeof(*i));
i->current = node->_members_;
i->current = unode->members;
i->opaque = NULL;
ht_create(&i->visited);
*opaque = i;
@ -131,8 +137,10 @@ static void vfs_union_iter_stop(VFSNode *node, void **opaque) {
}
static VFSInfo vfs_union_query(VFSNode *node) {
if(node->_primary_member_) {
VFSInfo i = vfs_node_query(node->_primary_member_);
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
if(unode->primary_member) {
VFSInfo i = vfs_node_query(unode->primary_member);
// can't trust the primary member here, others might be writable'
i.is_readonly = false;
return i;
@ -142,7 +150,7 @@ static VFSInfo vfs_union_query(VFSNode *node) {
return VFSINFO_ERROR;
}
static bool vfs_union_mount_internal(VFSNode *unode, const char *mountpoint, VFSNode *mountee, VFSInfo info, bool seterror) {
static bool vfs_union_mount_internal(VFSUnionNode *unode, const char *mountpoint, VFSNode *mountee, VFSInfo info, bool seterror) {
if(!info.exists) {
if(seterror) {
char *r = vfs_node_repr(mountee, true);
@ -160,13 +168,15 @@ static bool vfs_union_mount_internal(VFSNode *unode, const char *mountpoint, VFS
return false;
}
list_push((ListContainer**)&unode->_members_, list_wrap_container(mountee));
unode->_primary_member_ = mountee;
list_push(&unode->members, list_wrap_container(mountee));
unode->primary_member = mountee;
return true;
}
static bool vfs_union_mount(VFSNode *unode, const char *mountpoint, VFSNode *mountee) {
static bool vfs_union_mount(VFSNode *node, const char *mountpoint, VFSNode *mountee) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
if(mountpoint) {
vfs_set_error("Attempted to use a named mountpoint '%s' on a union", mountpoint);
return false;
@ -175,8 +185,9 @@ static bool vfs_union_mount(VFSNode *unode, const char *mountpoint, VFSNode *mou
return vfs_union_mount_internal(unode, NULL, mountee, vfs_node_query(mountee), true);
}
static SDL_RWops* vfs_union_open(VFSNode *unode, VFSOpenMode mode) {
VFSNode *n = unode->_primary_member_;
static SDL_RWops* vfs_union_open(VFSNode *node, VFSOpenMode mode) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
VFSNode *n = unode->primary_member;
if(n) {
return vfs_node_open(n, mode);
@ -187,10 +198,11 @@ static SDL_RWops* vfs_union_open(VFSNode *unode, VFSOpenMode mode) {
return NULL;
}
static char* vfs_union_repr(VFSNode *node) {
static char *vfs_union_repr(VFSNode *node) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
char *mlist = strdup("union: "), *r;
for(ListContainer *c = node->_members_; c; c = c->next) {
for(ListContainer *c = unode->members; c; c = c->next) {
VFSNode *n = c->data;
strappend(&mlist, r = vfs_node_repr(n, false));
@ -204,8 +216,9 @@ static char* vfs_union_repr(VFSNode *node) {
return mlist;
}
static char* vfs_union_syspath(VFSNode *node) {
VFSNode *n = node->_primary_member_;
static char *vfs_union_syspath(VFSNode *node) {
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
VFSNode *n = unode->primary_member;
if(n) {
return vfs_node_syspath(n);
@ -216,7 +229,8 @@ static char* vfs_union_syspath(VFSNode *node) {
}
static bool vfs_union_mkdir(VFSNode *node, const char *subdir) {
VFSNode *n = node->_primary_member_;
auto unode = VFS_NODE_CAST(VFSUnionNode, node);
VFSNode *n = unode->primary_member;
if(n) {
return vfs_node_mkdir(n, subdir);
@ -226,7 +240,7 @@ static bool vfs_union_mkdir(VFSNode *node, const char *subdir) {
return false;
}
static VFSNodeFuncs vfs_funcs_union = {
VFS_NODE_FUNCS(VFSUnionNode, {
.repr = vfs_union_repr,
.query = vfs_union_query,
.free = vfs_union_free,
@ -237,9 +251,12 @@ static VFSNodeFuncs vfs_funcs_union = {
.iter_stop = vfs_union_iter_stop,
.mkdir = vfs_union_mkdir,
.open = vfs_union_open,
};
});
void vfs_union_init(VFSNode *node) {
node->funcs = &vfs_funcs_union;
node->data1 = node->data2 = NULL;
VFSNode *vfs_union_create(void) {
return &VFS_ALLOC(VFSUnionNode)->as_generic;
}
bool vfs_create_union_mountpoint(const char *mountpoint) {
return vfs_mount_or_decref(vfs_root, mountpoint, vfs_union_create());
}

View file

@ -12,4 +12,4 @@
#include "private.h"
#include "union_public.h"
void vfs_union_init(VFSNode *node);
VFSNode *vfs_union_create(void);

View file

@ -1,17 +0,0 @@
/*
* This software is licensed under the terms of the MIT License.
* See COPYING for further information.
* ---
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
*/
#include "taisei.h"
#include "union.h"
bool vfs_create_union_mountpoint(const char *mountpoint) {
VFSNode *unode = vfs_alloc();
vfs_union_init(unode);
return vfs_mount_or_decref(vfs_root, mountpoint, unode);
}

View file

@ -10,39 +10,44 @@
#include "vdir.h"
#define VDIR_TABLE(vdir) ((ht_str2ptr_t*)((vdir)->data1))
VFS_NODE_TYPE(VFSVDirNode, {
ht_str2ptr_t table;
});
static void vfs_vdir_attach_node(VFSNode *vdir, const char *name, VFSNode *node) {
VFSNode *oldnode = ht_get(VDIR_TABLE(vdir), name, NULL);
static void vfs_vdir_attach_node(VFSNode *node, const char *name, VFSNode *subnode) {
auto vdir = VFS_NODE_CAST(VFSVDirNode, node);
VFSNode *oldnode = ht_get(&vdir->table, name, NULL);
if(oldnode) {
vfs_decref(oldnode);
}
ht_set(VDIR_TABLE(vdir), name, node);
ht_set(&vdir->table, name, subnode);
}
static VFSNode* vfs_vdir_locate(VFSNode *vdir, const char *path) {
VFSNode *node;
static VFSNode *vfs_vdir_locate(VFSNode *node, const char *path) {
auto vdir = VFS_NODE_CAST(VFSVDirNode, node);
VFSNode *subnode;
char mutpath[strlen(path)+1];
char *primpath, *subpath;
strcpy(mutpath, path);
vfs_path_split_left(mutpath, &primpath, &subpath);
if((node = ht_get(VDIR_TABLE(vdir), mutpath, NULL))) {
return vfs_locate(node, subpath);
if((subnode = ht_get(&vdir->table, primpath, NULL))) {
return vfs_locate(subnode, subpath);
}
return NULL;
}
static const char* vfs_vdir_iter(VFSNode *vdir, void **opaque) {
static const char *vfs_vdir_iter(VFSNode *node, void **opaque) {
auto vdir = VFS_NODE_CAST(VFSVDirNode, node);
ht_str2ptr_iter_t *iter = *opaque;
if(!iter) {
iter = *opaque = ALLOC(typeof(*iter));
ht_iter_begin(VDIR_TABLE(vdir), iter);
ht_iter_begin(&vdir->table, iter);
} else {
ht_iter_next(iter);
}
@ -50,7 +55,7 @@ static const char* vfs_vdir_iter(VFSNode *vdir, void **opaque) {
return iter->has_data ? iter->key : NULL;
}
static void vfs_vdir_iter_stop(VFSNode *vdir, void **opaque) {
static void vfs_vdir_iter_stop(VFSNode *node, void **opaque) {
if(*opaque) {
ht_iter_end((ht_str2ptr_iter_t*)*opaque);
mem_free(*opaque);
@ -58,26 +63,26 @@ static void vfs_vdir_iter_stop(VFSNode *vdir, void **opaque) {
}
}
static VFSInfo vfs_vdir_query(VFSNode *vdir) {
static VFSInfo vfs_vdir_query(VFSNode *node) {
return (VFSInfo) {
.exists = true,
.is_dir = true,
};
}
static void vfs_vdir_free(VFSNode *vdir) {
ht_str2ptr_t *ht = VDIR_TABLE(vdir);
static void vfs_vdir_free(VFSNode *node) {
auto vdir = VFS_NODE_CAST(VFSVDirNode, node);
ht_str2ptr_t *ht = &vdir->table;
ht_str2ptr_iter_t iter;
ht_iter_begin(ht, &iter);
for(; iter.has_data; ht_iter_next(&iter)) {
vfs_decref(iter.value);
vfs_decref((VFSNode*)iter.value);
}
ht_iter_end(&iter);
ht_destroy(ht);
mem_free(ht);
}
static bool vfs_vdir_mount(VFSNode *vdir, const char *mountpoint, VFSNode *subtree) {
@ -90,37 +95,29 @@ static bool vfs_vdir_mount(VFSNode *vdir, const char *mountpoint, VFSNode *subtr
return true;
}
static bool vfs_vdir_unmount(VFSNode *vdir, const char *mountpoint) {
static bool vfs_vdir_unmount(VFSNode *node, const char *mountpoint) {
auto vdir = VFS_NODE_CAST(VFSVDirNode, node);
VFSNode *mountee;
if(!(mountee = ht_get(VDIR_TABLE(vdir), mountpoint, NULL))) {
if(!(mountee = ht_get(&vdir->table, mountpoint, NULL))) {
vfs_set_error("Mountpoint '%s' doesn't exist", mountpoint);
return false;
}
ht_unset(VDIR_TABLE(vdir), mountpoint);
ht_unset(&vdir->table, mountpoint);
vfs_decref(mountee);
return true;
}
static bool vfs_vdir_mkdir(VFSNode *node, const char *subdir) {
if(!subdir) {
vfs_set_error("Virtual directory trying to create itself? How did you even get here?");
return false;
}
VFSNode *subnode = vfs_alloc();
vfs_vdir_init(subnode);
vfs_vdir_mount(node, subdir, subnode);
return true;
return vfs_vdir_mount(node, NOT_NULL(subdir), vfs_vdir_create());
}
static char* vfs_vdir_repr(VFSNode *node) {
static char *vfs_vdir_repr(VFSNode *node) {
return strdup("virtual directory");
}
static VFSNodeFuncs vfs_funcs_vdir = {
VFS_NODE_FUNCS(VFSVDirNode, {
.repr = vfs_vdir_repr,
.query = vfs_vdir_query,
.free = vfs_vdir_free,
@ -130,9 +127,10 @@ static VFSNodeFuncs vfs_funcs_vdir = {
.iter = vfs_vdir_iter,
.iter_stop = vfs_vdir_iter_stop,
.mkdir = vfs_vdir_mkdir,
};
});
void vfs_vdir_init(VFSNode *node) {
node->funcs = &vfs_funcs_vdir;
node->data1 = ht_str2ptr_new();
VFSNode *vfs_vdir_create(void) {
auto vdir = VFS_ALLOC(VFSVDirNode);
ht_create(&vdir->table);
return &vdir->as_generic;
}

View file

@ -11,4 +11,5 @@
#include "private.h"
void vfs_vdir_init(VFSNode *node);
VFSNode *vfs_vdir_create(void)
attr_returns_allocated;

View file

@ -14,10 +14,9 @@
#define LOG_SDL_ERROR log_debug("SDL error: %s", SDL_GetError())
static zip_int64_t vfs_zipfile_srcfunc(void *userdata, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
VFSNode *zipnode = userdata;
VFSZipFileData *zdata = zipnode->data1;
VFSZipNode *zipnode = userdata;
VFSZipFileTLS *tls = vfs_zipfile_get_tls(zipnode, false);
VFSNode *source = zdata->source;
VFSNode *source = zipnode->source;
zip_int64_t ret = -1;
if(!tls) {
@ -134,24 +133,19 @@ static void vfs_zipfile_free_tls(VFSZipFileTLS *tls) {
static void vfs_zipfile_free(VFSNode *node) {
if(node) {
VFSZipFileData *zdata = node->data1;
auto znode = VFS_NODE_CAST(VFSZipNode, node);
VFSZipFileTLS *tls = SDL_TLSGet(znode->tls_id);
if(zdata) {
SDL_TLSID tls_id = ((VFSZipFileData*)node->data1)->tls_id;
VFSZipFileTLS *tls = SDL_TLSGet(tls_id);
if(tls) {
vfs_zipfile_free_tls(tls);
SDL_TLSSet(tls_id, NULL, NULL);
}
if(zdata->source) {
vfs_decref(zdata->source);
}
ht_destroy(&zdata->pathmap);
mem_free(zdata);
if(tls) {
vfs_zipfile_free_tls(tls);
SDL_TLSSet(znode->tls_id, NULL, NULL);
}
if(znode->source) {
vfs_decref(znode->source);
}
ht_destroy(&znode->pathmap);
}
}
@ -163,25 +157,24 @@ static VFSInfo vfs_zipfile_query(VFSNode *node) {
};
}
static char* vfs_zipfile_syspath(VFSNode *node) {
VFSZipFileData *zdata = node->data1;
return vfs_node_syspath(zdata->source);
static char *vfs_zipfile_syspath(VFSNode *node) {
return vfs_node_syspath(VFS_NODE_CAST(VFSZipNode, node)->source);
}
static char* vfs_zipfile_repr(VFSNode *node) {
VFSZipFileData *zdata = node->data1;
char *srcrepr = vfs_node_repr(zdata->source, false);
char *ziprepr = strfmt("zip archive %s", srcrepr);
static char *vfs_zipfile_repr(VFSNode *node) {
auto znode = VFS_NODE_CAST(VFSZipNode, node);
char *srcrepr = vfs_node_repr(znode->source, false);
char *ziprepr = strjoin("zip archive ", srcrepr, NULL);
mem_free(srcrepr);
return ziprepr;
}
static VFSNode* vfs_zipfile_locate(VFSNode *node, const char *path) {
VFSZipFileTLS *tls = vfs_zipfile_get_tls(node, true);