Add mj_containsFileVFS and mj_containsBufferVFS functions.

PiperOrigin-RevId: 903785790
Change-Id: I013b37a177284f8440179c4ae5c6221e0f572b49
This commit is contained in:
Sam Haves
2026-04-22 05:26:33 -07:00
committed by Copybara-Service
parent c3fddf5dc1
commit 4cfebcc32b
10 changed files with 156 additions and 0 deletions
+18
View File
@@ -1519,6 +1519,24 @@ Add file to VFS from buffer; return 0: success, 2: repeated name, -1: failed to
Delete file from VFS; return 0: success, -1: not found in VFS.
.. _mj_containsBufferVFS:
`mj_containsBufferVFS <#mj_containsBufferVFS>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mj_containsBufferVFS
Check if buffer exists in VFS; return 1: exists, 0: not found.
.. _mj_containsFileVFS:
`mj_containsFileVFS <#mj_containsFileVFS>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mj_containsFileVFS
Check if file exists in VFS; return 1: exists, 0: not found.
.. _mj_deleteVFS:
`mj_deleteVFS <#mj_deleteVFS>`__
+1
View File
@@ -9,6 +9,7 @@ General
^^^^^^^
- Added new :ref:`mj_maxContact<mj_maxContact>` function to get the maximum number of possible contacts returned by
two geoms.
- Added ``mj_containsBufferVFS`` and ``mj_containsFileVFS`` to check for existence of buffers and files in VFS.
- Added :ref:`multi-cell support<body-flexcomp-cellnum>` for trilinear and quadratic flexes. Note that the implicit
integrator uses a dense solver for the flex degrees of freedom, which can be slow for multi-cell flexes.
- Refactored ``flexstrain`` equality constraints to be instantiated per cell instead of per flex object, reducing the
+2
View File
@@ -3161,6 +3161,8 @@ int mj_unmountVFS(mjVFS* vfs, const char* filename);
int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer, int nbuffer);
int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
int mj_containsBufferVFS(mjVFS* vfs, const char* name);
int mj_containsFileVFS(mjVFS* vfs, const char* directory, const char* filename);
void mj_deleteVFS(mjVFS* vfs);
size_t mj_getCacheSize(const mjCache* cache);
size_t mj_getCacheCapacity(const mjCache* cache);
+6
View File
@@ -94,6 +94,12 @@ MJAPI int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer, int
// Delete file from VFS; return 0: success, -1: not found in VFS.
MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
// Check if buffer exists in VFS; return 1: exists, 0: not found.
MJAPI int mj_containsBufferVFS(mjVFS* vfs, const char* name);
// Check if file exists in VFS; return 1: exists, 0: not found.
MJAPI int mj_containsFileVFS(mjVFS* vfs, const char* directory, const char* filename);
// Delete all files from VFS and deallocates VFS internal memory.
MJAPI void mj_deleteVFS(mjVFS* vfs);
+46
View File
@@ -162,6 +162,52 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc='Delete file from VFS; return 0: success, -1: not found in VFS.',
)),
('mj_containsBufferVFS',
FunctionDecl(
name='mj_containsBufferVFS',
return_type=ValueType(name='int'),
parameters=(
FunctionParameterDecl(
name='vfs',
type=PointerType(
inner_type=ValueType(name='mjVFS'),
),
),
FunctionParameterDecl(
name='name',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
),
doc='Check if buffer exists in VFS; return 1: exists, 0: not found.',
)),
('mj_containsFileVFS',
FunctionDecl(
name='mj_containsFileVFS',
return_type=ValueType(name='int'),
parameters=(
FunctionParameterDecl(
name='vfs',
type=PointerType(
inner_type=ValueType(name='mjVFS'),
),
),
FunctionParameterDecl(
name='directory',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='filename',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
),
doc='Check if file exists in VFS; return 1: exists, 0: not found.',
)),
('mj_deleteVFS',
FunctionDecl(
name='mj_deleteVFS',
+37
View File
@@ -236,6 +236,24 @@ VFS::Status VFS::Unmount(const FilePath& path) {
return kInvalidResourceProvider;
}
bool VFS::ContainsBuffer(const char* name) {
if (name == nullptr) {
return false;
}
std::lock_guard<std::mutex> lock(mutex_);
return mounts_.contains(name);
}
bool VFS::ContainsFile(const char* directory, const char* filename) {
if (filename == nullptr) {
return false;
}
mujoco::user::FilePath path(directory ? directory : "", filename);
std::string key = path.StripPath().Lower().Str();
std::lock_guard<std::mutex> lock(mutex_);
return mounts_.contains(key);
}
int VFS::Read(mjResource* resource, const void** buffer) {
if (resource && resource->provider && resource->provider->read) {
return resource->provider->read(resource, buffer);
@@ -498,3 +516,22 @@ int mj_deleteFileVFS(mjVFS* vfs, const char* filename) {
}
return mujoco::user::VFS::kSuccess;
}
int mj_containsBufferVFS(mjVFS* vfs, const char* name) {
mujoco::user::VFS* impl = mujoco::user::VFS::Upcast(vfs);
if (impl == nullptr) {
mju_error("mjVFS is null.");
return -1;
}
return impl->ContainsBuffer(name);
}
int mj_containsFileVFS(mjVFS* vfs, const char* directory, const char* filename) {
mujoco::user::VFS* impl = mujoco::user::VFS::Upcast(vfs);
if (impl == nullptr) {
mju_error("mjVFS is null.");
return -1;
}
return impl->ContainsFile(directory, filename);
}
+6
View File
@@ -90,6 +90,12 @@ class VFS {
// Unmounts the ResourceProvider from the given path.
Status Unmount(const FilePath& path);
// Returns true if the VFS contains a buffer with the given name.
bool ContainsBuffer(const char* name);
// Returns true if the VFS contains a file with the given name.
bool ContainsFile(const char* directory, const char* filename);
// Sets a destructor to be called when the VFS has no more open resources.
// Assumes that `destructor` will delete `this`.
//
+32
View File
@@ -224,6 +224,38 @@ TEST_F(UserVfsTest, DeleteFileRepeat) {
mj_deleteVFS(&vfs);
}
TEST_F(UserVfsTest, ContainsBuffer) {
mjVFS vfs;
mj_defaultVFS(&vfs);
std::string buffer = "<mujoco/>";
const void* ptr = static_cast<const void*>(buffer.c_str());
mj_addBufferVFS(&vfs, "model", ptr, buffer.size());
EXPECT_TRUE(mj_containsBufferVFS(&vfs, "model"));
EXPECT_FALSE(mj_containsBufferVFS(&vfs, "nonexistent"));
EXPECT_FALSE(mj_containsBufferVFS(&vfs, "Model"));
mj_deleteVFS(&vfs);
}
TEST_F(UserVfsTest, ContainsFile) {
mjVFS vfs;
mj_defaultVFS(&vfs);
constexpr char path[] = "engine/testdata/actuation/";
const std::string dir = GetTestDataFilePath(path);
std::string file = "activation.xml";
mj_addFileVFS(&vfs, dir.c_str(), file.c_str());
EXPECT_TRUE(mj_containsFileVFS(&vfs, dir.c_str(), file.c_str()));
EXPECT_TRUE(mj_containsFileVFS(&vfs, nullptr, (dir + file).c_str()));
EXPECT_TRUE(mj_containsFileVFS(&vfs, nullptr, "Activation.xml"));
EXPECT_TRUE(mj_containsFileVFS(&vfs, "some/dir/", "activation.xml"));
EXPECT_FALSE(mj_containsFileVFS(&vfs, nullptr, "nonexistent.xml"));
mj_deleteVFS(&vfs);
}
TEST_F(UserVfsTest, AddBuffer) {
mjVFS vfs;
+6
View File
@@ -6684,6 +6684,12 @@ public static unsafe extern int mj_addBufferVFS(void* vfs, [MarshalAs(UnmanagedT
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_deleteFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string filename);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_containsBufferVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string name);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_containsFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string directory, [MarshalAs(UnmanagedType.LPStr)]string filename);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mj_deleteVFS(void* vfs);
+2
View File
@@ -124,6 +124,8 @@ _SKIPPED_ASSET_CACHE_FUNCTIONS: tuple[str, ...] = (
_SKIPPED_VFS_FUNCTIONS: tuple[str, ...] = (
# go/keep-sorted start
"mj_addFileVFS",
"mj_containsBufferVFS",
"mj_containsFileVFS",
"mj_mountVFS",
"mj_unmountVFS",
# go/keep-sorted end