Partial roll back VFS refactor due internal breakages

PiperOrigin-RevId: 862210191
Change-Id: Ia15a345a267bcef4b593d507d54f1668da0c0611
This commit is contained in:
Haroon Qureshi
2026-01-28 06:39:00 -08:00
committed by Copybara-Service
parent 19ff06155a
commit 2fd9b5e92f
6 changed files with 437 additions and 726 deletions
+27 -97
View File
@@ -17,115 +17,45 @@
#ifndef MUJOCO_SRC_USER_USER_VFS_H_
#define MUJOCO_SRC_USER_USER_VFS_H_
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <stddef.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mujoco.h>
#include "user/user_util.h"
#include <mujoco/mjplugin.h>
namespace mujoco::user {
#ifdef __cplusplus
extern "C" {
#endif
// Underlying Virtual File System implementation for opaque mjVFS struct.
//
// This class owns and manages all the mjResource instances that are created
// using the mju_openResource. Its main job is to find the correct
// mjpResourceProvider to handle the mju open/read/close operations. It does
// this by "mounting" mjpResourceProviders at specific paths such that any
// operation within that path will be handled by the mjpResourceProvider.
//
// Mounting can be done explicitly (using mj_mountVFS) or implicitly (using
// mjp_registerResourceProvider). If no provider is found for a given path,
// then a "default" provider is used that uses normal C file operations to
// open/read/close files.
//
// To support legacy use-cases (where the VFS is an optional argument), a
// "self-destruct" mode can be configured so that a temporary VFS instance can
// be created with a lifetime tied to the opened mjResource instance.
//
// This class itself is thread-safe, but it makes no guarantees about the
// thread-safety of the underlying mjResourceProviders.
class VFS {
public:
explicit VFS(mjVFS* vfs);
~VFS();
// Initialize an empty VFS, mj_deleteVFS must be called to deallocate the VFS
MJAPI void mj_defaultVFS(mjVFS* vfs);
VFS(const VFS&) = delete;
VFS& operator=(const VFS&) = delete;
// add file to VFS, return 0: success, 2: repeated name, -1: not found on disk
MJAPI int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
// Status codes for VFS operations. Values are based on the mj_VFS APIs.
enum Status {
kSuccess = 0,
kFailedToLoad = -1,
kFailedToRead = -1,
kNotFound = -1,
kRepeatedName = 2,
kInvalidVfs = -1,
kInvalidResource = -1,
kInvalidResourceProvider = -1,
};
// add file from buffer into VFS, return 0: success, 2: repeated name, -1: failed to load
MJAPI int mj_addBufferVFS(mjVFS* vfs, const char* filename, const void* buffer, int nbuffer);
// Opens a mjResource for the given path, or nullptr on error. If successful,
// will invoke the 'open' callback for the mjpResourceProvider associated with
// the path/dir.
mjResource* Open(const char* dir, const char* name);
// mount a ResourceProvider to handle file operations under the given path; return 0: success,
// 2: repeated name, -1: invalid resource provider
MJAPI int mj_mountVFS(mjVFS* vfs, const char* filepath, const mjpResourceProvider* provider);
// Sets `buffer` to the contents of the resource and returns the number of
// bytes of the content. This is done by invoking the 'read' callback for the
// mjpResourceProvider associated with the resource. Returns -1 on error.
int Read(mjResource* resource, const void** buffer);
// unmount a previously mounted ResourceProvider; return 0: success, -1: not found in VFS
MJAPI int mj_unmountVFS(mjVFS* vfs, const char* filename);
// Closes the resource by invoking the 'close' callback for the
// mjpResourceProvider associated with the resource.
Status Close(mjResource* resource);
// return file index in VFS, or -1 if not found in VFS
MJAPI int mj_findFileVFS(const mjVFS* vfs, const char* filename);
// Mounts a ResourceProvider at the given path. All subsequent operations
// under `path` will be delegated to the `provider` until it is unmounted.
Status Mount(const FilePath& path, const mjpResourceProvider* provider);
// delete file from VFS, return 0: success, -1: not found in VFS
MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
// Unmounts the ResourceProvider from the given path.
Status Unmount(const FilePath& path);
// delete all files from VFS
MJAPI void mj_deleteVFS(mjVFS* vfs);
// Sets a destructor to be called when the VFS has no more open resources.
// Assumes that `destructor` will delete `this`.
//
// This is useful for when you want to create a temporary VFS instance with
// a lifetime tied to a single mjResource to be opened. The `destructor`
// should be set to `delete this` and any other cleanup that needs to happen.
void SetToSelfDestruct(std::function<void(mjVFS*)> destructor);
#ifdef __cplusplus
}
#endif
// Converts the public C-API pointer to the internal C++ class.
static VFS* Upcast(mjVFS* vfs);
static const VFS* Upcast(const mjVFS* vfs);
private:
using ResourcePtr = std::unique_ptr<mjResource, void (*)(mjResource*)>;
ResourcePtr CreateResource(std::string_view name,
const mjpResourceProvider* provider);
// Returns a mounted mjResource* that matches the given path. If no explicitly
// mounted mjResource* is found, returns a "default" mounting that uses the
// C file system.
mjResource* FindMount(const std::string& fullpath);
// Invokes the `destructor_, but only if it has been set previously. This
// should be only called when resources_ is empty and callers should assume
// that `this` will be invalidated after this call.
void MaybeSelfDestruct();
mjVFS* self_;
std::mutex mutex_; // Protects open_resources_ and mounts_.
std::unordered_map<mjResource*, ResourcePtr> open_resources_;
std::unordered_map<std::string, ResourcePtr> mounts_;
mjResource default_mount_;
mjpResourceProvider default_provider_;
std::function<void(mjVFS*)> destructor_;
};
} // namespace mujoco::user
const mjpResourceProvider* GetVfsResourceProvider();
#endif // MUJOCO_SRC_USER_USER_VFS_H_