Add checksums (used as a timestamp) to VFS.

Deprecate mj_makeEmptyFileVFS in favor of mj_addBufferVFS.

PiperOrigin-RevId: 608915388
Change-Id: Id9e9b042799bc1bf547f0795ae6f386447d53cdd
This commit is contained in:
Kyle Bayes
2024-02-21 02:24:35 -08:00
committed by Copybara-Service
parent 042760eba8
commit a4a621f6e3
15 changed files with 251 additions and 78 deletions
+14 -5
View File
@@ -1109,14 +1109,14 @@ mj_addFileVFS
Add file to VFS. The directory argument is optional and can be NULL or empty. Returns 0 on success, 1 when VFS is full,
2 on name collision, or -1 when an internal error occurs.
.. _mj_makeEmptyFileVFS:
.. _mj_addBufferVFS:
mj_makeEmptyFileVFS
~~~~~~~~~~~~~~~~~~~
mj_addBufferVFS
~~~~~~~~~~~~~~~
.. mujoco-include:: mj_makeEmptyFileVFS
.. mujoco-include:: mj_addBufferVFS
Make empty file in VFS, return 0: success, 1: full, 2: repeated name.
Add file to VFS from buffer, return 0: success, 1: full, 2: repeated name, -1: failed to load.
.. _mj_findFileVFS:
@@ -1145,6 +1145,15 @@ mj_deleteVFS
Delete all files from VFS.
.. _mj_makeEmptyFileVFS:
mj_makeEmptyFileVFS
~~~~~~~~~~~~~~~~~~~
.. mujoco-include:: mj_makeEmptyFileVFS
deprecated: use mj_copyBufferVFS.
.. _Initialization:
Initialization
+10 -5
View File
@@ -12,20 +12,25 @@ General
:at:`ctrlrange` or :at:`actrange` (respectively), according to the range of the transmission
target (joint or tendon). See :ref:`position/inheritrange<actuator-position-inheritrange>` for
details.
2. Deprecated :ref:`mj_makeEmptyFileVFS` in favor of :ref:`mj_addBufferVFS`. :ref:`mjVFS` now computes checksums of
its internal file buffers. :ref:`mj_addBufferVFS` allocates an empty buffer with a given name in an mjVFS and
copies the data buffer into it, combining and replacing the deprecated two-step process of calling
:ref:`mj_makeEmptyFileVFS` followed by a direct copy into the given mjVFS internal file buffer.
MJX
^^^
2. Improved performance of getting and putting device data.
3. Improved performance of getting and putting device data.
- Use ``tobytes()`` for numpy array serialization, which is orders of magnitude faster than converting to tuples.
- Avoid reallocating host ``mjData`` arrays when array shapes are unchanged.
- Speed up calculation of ``mjx.ncon`` for models with many geoms.
- Avoid calling ``mjx.ncon`` in ``mjx.get_data_into`` when ``nc`` can be derived from ``mjx.Data``.
3. Fixed a bug in ``mjx-viewer`` that prevented it from running. Updated ``mjx-viewer`` to use newer
4. Fixed a bug in ``mjx-viewer`` that prevented it from running. Updated ``mjx-viewer`` to use newer
``mjx.get_data_into`` function call.
4. Fixed a bug in ``mjx.euler`` that applied incorrect damping when using dense mass matrices.
5. Fixed a bug in ``mjx.solve`` that was causing slow convergence when using ``mjSOL_NEWTON`` in :ref:`mjtSolver`.
6. Added support for :ref:`mjOption.impratio<mjOption>` to ``mjx.Model``.
5. Fixed a bug in ``mjx.euler`` that applied incorrect damping when using dense mass matrices.
6. Fixed a bug in ``mjx.solve`` that was causing slow convergence when using ``mjSOL_NEWTON`` in :ref:`mjtSolver`.
7. Added support for :ref:`mjOption.impratio<mjOption>` to ``mjx.Model``.
Python bindings
^^^^^^^^^^^^^^^
+9 -6
View File
@@ -691,11 +691,12 @@ struct mjLROpt_ { // options for mj_setLengthRange()
mjtNum tolrange; // convergence tolerance (relative to range)
};
typedef struct mjLROpt_ mjLROpt;
struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data
struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data
uint64_t filestamp[mjMAXVFS]; // checksum of the file data
};
typedef struct mjVFS_ mjVFS;
struct mjOption_ { // physics options
@@ -1375,6 +1376,7 @@ typedef struct mjModel_ mjModel;
struct mjResource_ {
char* name; // name of resource (filename, etc)
void* data; // opaque data pointer
char timestamp[512]; // timestamp of the resource
const struct mjpResourceProvider* provider; // pointer to the provider
};
typedef struct mjResource_ mjResource;
@@ -2406,10 +2408,11 @@ typedef struct mjvSceneState_ mjvSceneState;
//----------------------------- MJAPI FUNCTIONS --------------------------------
void mj_defaultVFS(mjVFS* vfs);
int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer, int nbuffer);
int mj_findFileVFS(const mjVFS* vfs, const char* filename);
int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
void mj_deleteVFS(mjVFS* vfs);
int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
mjModel* mj_loadXML(const char* filename, const mjVFS* vfs, char* error, int error_sz);
int mj_saveLastXML(const char* filename, const mjModel* m, char* error, int error_sz);
void mj_freeLastXML(void);
+7 -5
View File
@@ -16,6 +16,7 @@
#define MUJOCO_MJMODEL_H_
#include <stddef.h>
#include <stdint.h>
#include <mujoco/mjtnum.h>
@@ -390,11 +391,12 @@ typedef struct mjLROpt_ mjLROpt;
//---------------------------------- mjVFS ---------------------------------------------------------
struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data
struct mjVFS_ { // virtual file system for loading from memory
int nfile; // number of files present
char filename[mjMAXVFS][mjMAXVFSNAME]; // file name without path
size_t filesize[mjMAXVFS]; // file size in bytes
void* filedata[mjMAXVFS]; // buffer with file data
uint64_t filestamp[mjMAXVFS]; // checksum of the file data
};
typedef struct mjVFS_ mjVFS;
+8 -5
View File
@@ -26,11 +26,12 @@
struct mjResource_ {
char* name; // name of resource (filename, etc)
void* data; // opaque data pointer
char timestamp[512]; // timestamp of the resource
const struct mjpResourceProvider* provider; // pointer to the provider
};
typedef struct mjResource_ mjResource;
// callback for opeing a resource, returns zero on failure
// callback for opening a resource, returns zero on failure
typedef int (*mjfOpenResource)(mjResource* resource);
// callback for reading a resource
@@ -44,10 +45,12 @@ typedef void (*mjfCloseResource)(mjResource* resource);
// sets dir to directory string with ndir being size of directory string
typedef void (*mjfGetResourceDir)(mjResource* resource, const char** dir, int* ndir);
// callback for checking if a resource was modified since last read
// returns > 0 if resource was modified since last open, 0 if resource was not
// modified, and < 0 if inconclusive
typedef int (*mjfResourceModified)(const mjResource* resource);
// callback for checking if the current resource was modified from the time
// specified by the timestamp
// returns 0 if the resource's timestamp matches the provided timestamp
// returns > 0 if the the resource is younger than the given timestamp
// returns < 0 if the resource is older than the given timestamp
typedef int (*mjfResourceModified)(const mjResource* resource, const char* timestamp);
// struct describing a single resource provider
struct mjpResourceProvider {
+4 -2
View File
@@ -82,8 +82,8 @@ MJAPI void mj_defaultVFS(mjVFS* vfs);
// Add file to VFS, return 0: success, 1: full, 2: repeated name, -1: failed to load.
MJAPI int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
// Make empty file in VFS, return 0: success, 1: full, 2: repeated name.
MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
// Add file to VFS from buffer, return 0: success, 1: full, 2: repeated name, -1: failed to load.
MJAPI int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer, int nbuffer);
// Return file index in VFS, or -1 if not found in VFS.
MJAPI int mj_findFileVFS(const mjVFS* vfs, const char* filename);
@@ -94,6 +94,8 @@ MJAPI int mj_deleteFileVFS(mjVFS* vfs, const char* filename);
// Delete all files from VFS.
MJAPI void mj_deleteVFS(mjVFS* vfs);
// deprecated: use mj_copyBufferVFS.
MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
//---------------------------------- Parse and compile ---------------------------------------------
+35 -5
View File
@@ -66,9 +66,9 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc='Add file to VFS, return 0: success, 1: full, 2: repeated name, -1: failed to load.', # pylint: disable=line-too-long
)),
('mj_makeEmptyFileVFS',
('mj_addBufferVFS',
FunctionDecl(
name='mj_makeEmptyFileVFS',
name='mj_addBufferVFS',
return_type=ValueType(name='int'),
parameters=(
FunctionParameterDecl(
@@ -78,17 +78,23 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
),
FunctionParameterDecl(
name='filename',
name='name',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='filesize',
name='buffer',
type=PointerType(
inner_type=ValueType(name='void', is_const=True),
),
),
FunctionParameterDecl(
name='nbuffer',
type=ValueType(name='int'),
),
),
doc='Make empty file in VFS, return 0: success, 1: full, 2: repeated name.', # pylint: disable=line-too-long
doc='Add file to VFS from buffer, return 0: success, 1: full, 2: repeated name, -1: failed to load.', # pylint: disable=line-too-long
)),
('mj_findFileVFS',
FunctionDecl(
@@ -144,6 +150,30 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc='Delete all files from VFS.',
)),
('mj_makeEmptyFileVFS',
FunctionDecl(
name='mj_makeEmptyFileVFS',
return_type=ValueType(name='int'),
parameters=(
FunctionParameterDecl(
name='vfs',
type=PointerType(
inner_type=ValueType(name='mjVFS'),
),
),
FunctionParameterDecl(
name='filename',
type=PointerType(
inner_type=ValueType(name='char', is_const=True),
),
),
FunctionParameterDecl(
name='filesize',
type=ValueType(name='int'),
),
),
doc='deprecated: use mj_copyBufferVFS.',
)),
('mj_loadXML',
FunctionDecl(
name='mj_loadXML',
+8
View File
@@ -121,6 +121,14 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
doc='buffer with file data',
),
StructFieldDecl(
name='filestamp',
type=ArrayType(
inner_type=ValueType(name='uint64_t'),
extents=(2000,),
),
doc='checksum of the file data',
),
),
)),
('mjOption',
+8 -10
View File
@@ -105,7 +105,9 @@ mjResource* mju_openResource(const char* name, char* error, size_t error_sz) {
memcpy(&fb->mtime, &file_stat.st_mtime, sizeof(time_t));
} else {
memset(&fb->mtime, 0, sizeof(time_t));
resource->timestamp[0] = '\0';
}
strftime(resource->timestamp, 512, "%Y-%m-%d-%H:%M:%S", localtime(&(fb->mtime)));
return resource;
}
@@ -185,24 +187,20 @@ static int mju_isModifiedFile(const char* name, const file_buffer* fb) {
if (stat(name, &file_stat) == 0) {
return difftime(fb->mtime, file_stat.st_mtime) < 0;
}
return -1;
}
return -2;
return 1; // modified (default)
}
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
// if inconclusive
int mju_isModifiedResource(const mjResource* resource) {
if (resource == NULL) {
return -2;
}
// return 0 if the resource's timestamp matches the provided timestamp
// return > 0 if the the resource is younger than the given timestamp
// return < 0 if the resource is older than the given timestamp
int mju_isModifiedResource(const mjResource* resource, const char* timestamp) {
// provider is not OS filesystem
if (resource->provider) {
if (resource->provider->modified) {
return resource->provider->modified(resource);
return resource->provider->modified(resource, timestamp);
}
return 1; // default (modified)
}
+5 -4
View File
@@ -35,12 +35,13 @@ MJAPI void mju_closeResource(mjResource* resource);
// return negative value if error
MJAPI int mju_readResource(mjResource* resource, const void** buffer);
// sets for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers
// set for a resource with a name partitioned as {dir}{filename}, the dir and ndir pointers
MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir);
// Returns > 0 if resource has been modified since last read, 0 if not, and < 0
// if inconclusive
MJAPI int mju_isModifiedResource(const mjResource* resource);
// return 0 if the resource's timestamp matches the provided timestamp
// return > 0 if the the resource is younger than the given timestamp
// return < 0 if the resource is older than the given timestamp
MJAPI int mju_isModifiedResource(const mjResource* resource, const char* timestamp);
// get the length of the dirname portion of a given path
int mju_dirnamelen(const char* path);
+90 -2
View File
@@ -15,7 +15,9 @@
#include "engine/engine_vfs.h"
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "engine/engine_array_safety.h"
#include "engine/engine_resource.h"
@@ -55,6 +57,38 @@ static void vfs_strippath(char* newname, const char* oldname) {
// copies data into a buffer and produces a hash of the data
static uint64_t vfs_memcpy(void* dest, const void* restrict src, size_t n) {
uint64_t hash = 0xcbf29ce484222325; // magic number
uint64_t prime = 0x100000001b3; // magic prime
const uint8_t* bytes = (uint8_t*) src;
uint8_t* buffer = (uint8_t*) dest;
for (size_t i = 0; i < n; i++) {
buffer[i] = bytes[i];
// do FNV-1 hash
hash |= bytes[i];
hash *= prime;
}
return hash;
}
// VFS hash function implemented using the FNV-1 hash
static uint64_t vfs_hash(const void* restrict buffer, size_t n) {
uint64_t hash = 0xcbf29ce484222325; // magic number
uint64_t prime = 0x100000001b3; // magic prime
const uint8_t* bytes = (uint8_t*) buffer;
for (size_t i = 0; i < n; i++) {
hash |= bytes[i];
hash *= prime;
}
return hash;
}
// initialize to empty (no deallocation)
void mj_defaultVFS(mjVFS* vfs) {
memset(vfs, 0, sizeof(mjVFS));
@@ -95,7 +129,9 @@ int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename) {
if (!vfs->filedata[vfs->nfile]) {
return -1;
}
// assign size and count
// assign size, count, and checksum
vfs->filestamp[vfs->nfile] = vfs_hash(vfs->filedata[vfs->nfile], filesize);
vfs->filesize[vfs->nfile] = filesize;
vfs->nfile++;
@@ -136,6 +172,7 @@ int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize) {
mjERROR("could not allocate memory");
}
memset(vfs->filedata[vfs->nfile], 0, filesize);
vfs->filestamp[vfs->nfile] = 0;
// assign size and count
vfs->filesize[vfs->nfile] = filesize;
@@ -146,6 +183,51 @@ int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize) {
// add file from buffer into VFS
int mj_addBufferVFS(mjVFS* vfs, const char* name, const void* buffer, int nbuffer) {
if (!vfs || !buffer || !name) {
mjERROR("null pointer");
}
if (vfs->nfile >= mjMAXVFS-1) {
return 1;
}
// check buffer size
if (nbuffer <= 0) {
mjERROR("expects positive buffer size");
}
// strip path
char newname[mjMAXVFSNAME];
vfs_strippath(newname, name);
// check for repeated name
for (int i=0; i < vfs->nfile; i++) {
if (strncmp(newname, vfs->filename[i], mjMAXVFSNAME) == 0) {
return 2;
}
}
// assign name
mjSTRNCPY(vfs->filename[vfs->nfile], newname);
// allocate and clear
vfs->filedata[vfs->nfile] = mju_malloc(nbuffer);
if (!vfs->filedata[vfs->nfile]) {
mjERROR("could not allocate memory");
}
vfs->filestamp[vfs->nfile] = vfs_memcpy(vfs->filedata[vfs->nfile], buffer, nbuffer);
// assign size and count
vfs->filesize[vfs->nfile] = nbuffer;
vfs->nfile++;
return 0;
}
// return file index in VFS, or -1 if not found in VFS
int mj_findFileVFS(const mjVFS* vfs, const char* filename) {
// strip path
@@ -216,7 +298,13 @@ static int vfs_open_callback(mjResource* resource) {
}
const mjVFS* vfs = (const mjVFS*) resource->data;
return mj_findFileVFS(vfs, resource->name) >= 0;
int i = mj_findFileVFS(vfs, resource->name);
resource->timestamp[0] = '\0';
if (i >= 0 && vfs->filestamp[i]) {
mju_encodeBase64(resource->timestamp, (uint8_t*) &vfs->filestamp[i],
sizeof(uint64_t));
}
return i >= 0;
}
+6 -1
View File
@@ -15,6 +15,8 @@
#ifndef MUJOCO_SRC_ENGINE_ENGINE_VFS_H_
#define MUJOCO_SRC_ENGINE_ENGINE_VFS_H_
#include <stddef.h>
#include <mujoco/mjexport.h>
#include <mujoco/mjmodel.h>
#include <mujoco/mjplugin.h>
@@ -29,9 +31,12 @@ MJAPI void mj_defaultVFS(mjVFS* vfs);
// add file to VFS, return 0: success, 1: full, 2: repeated name, -1: not found on disk
MJAPI int mj_addFileVFS(mjVFS* vfs, const char* directory, const char* filename);
// make empty file in VFS, return 0: success, 1: full, 2: repeated name
// deprecated: use mj_copyBufferVFS
MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize);
// add file from buffer into VFS, return 0: success, 1: full, 2: repeated name, -1: failed to load
MJAPI int mj_copyBufferVFS(mjVFS* vfs, const char* filename, const void* buffer, int nbuffer);
// return file index in VFS, or -1 if not found in VFS
MJAPI int mj_findFileVFS(const mjVFS* vfs, const char* filename);
-1
View File
@@ -247,7 +247,6 @@ TEST_F(Base64Test, mju_encodeBase64) {
EXPECT_THAT(buffer.data(), StrEq("D4a+//A="));
EXPECT_THAT(n, std::strlen(buffer.data()) + 1);
EXPECT_THAT(n, buffer.size());
}
TEST_F(Base64Test, mju_encodeBase64_align0) {
+42 -26
View File
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <array>
#include <cstdio>
#include <memory>
#include <string>
#include <gmock/gmock.h>
@@ -25,7 +28,7 @@ namespace {
using ::testing::NotNull;
using EngineVfsTest = MujocoTest;
TEST_F(EngineVfsTest, AddFileVFS) {
TEST_F(EngineVfsTest, AddFileTest) {
constexpr char path[] = "engine/testdata/actuation/";
const std::string dir = GetTestDataFilePath(path);
std::string file1 = "activation.xml";
@@ -44,38 +47,51 @@ TEST_F(EngineVfsTest, AddFileVFS) {
ASSERT_THAT(fp3, NotNull()) << "Input file3 missing.";
std::fclose(fp3);
auto mj_vfs = std::make_unique<mjVFS>();
mj_defaultVFS(mj_vfs.get());
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
EXPECT_THAT(mj_vfs->nfile, 0);
EXPECT_THAT(mj_addFileVFS(mj_vfs.get(), dir.c_str(), file1.c_str()), 0);
EXPECT_THAT(mj_vfs->nfile, 1);
EXPECT_THAT(mj_vfs->filename[0], file1);
EXPECT_THAT(vfs->nfile, 0);
EXPECT_THAT(mj_addFileVFS(vfs.get(), dir.c_str(), file1.c_str()), 0);
EXPECT_THAT(vfs->nfile, 1);
EXPECT_THAT(vfs->filename[0], file1);
EXPECT_THAT(mj_addFileVFS(mj_vfs.get(), dir.c_str(), file2.c_str()), 0);
EXPECT_THAT(mj_vfs->nfile, 2);
EXPECT_THAT(mj_vfs->filename[0], file1);
EXPECT_THAT(mj_vfs->filename[1], file2);
EXPECT_THAT(mj_addFileVFS(vfs.get(), dir.c_str(), file2.c_str()), 0);
EXPECT_THAT(vfs->nfile, 2);
EXPECT_THAT(vfs->filename[0], file1);
EXPECT_THAT(vfs->filename[1], file2);
EXPECT_THAT(mj_addFileVFS(mj_vfs.get(), dir.c_str(), file3.c_str()), 0);
EXPECT_THAT(mj_vfs->nfile, 3);
EXPECT_THAT(mj_vfs->filename[0], file1);
EXPECT_THAT(mj_vfs->filename[1], file2);
EXPECT_THAT(mj_vfs->filename[2], file3);
EXPECT_THAT(mj_addFileVFS(vfs.get(), dir.c_str(), file3.c_str()), 0);
EXPECT_THAT(vfs->nfile, 3);
EXPECT_THAT(vfs->filename[0], file1);
EXPECT_THAT(vfs->filename[1], file2);
EXPECT_THAT(vfs->filename[2], file3);
mj_deleteFileVFS(mj_vfs.get(), file1.c_str());
EXPECT_THAT(mj_vfs->nfile, 2);
EXPECT_THAT(mj_vfs->filename[0], file2);
EXPECT_THAT(mj_vfs->filename[1], file3);
mj_deleteFileVFS(vfs.get(), file1.c_str());
EXPECT_THAT(vfs->nfile, 2);
EXPECT_THAT(vfs->filename[0], file2);
EXPECT_THAT(vfs->filename[1], file3);
mj_deleteFileVFS(mj_vfs.get(), file3.c_str());
EXPECT_THAT(mj_vfs->nfile, 1);
EXPECT_THAT(mj_vfs->filename[0], file2);
mj_deleteFileVFS(vfs.get(), file3.c_str());
EXPECT_THAT(vfs->nfile, 1);
EXPECT_THAT(vfs->filename[0], file2);
mj_deleteFileVFS(mj_vfs.get(), file2.c_str());
EXPECT_THAT(mj_vfs->nfile, 0);
mj_deleteFileVFS(vfs.get(), file2.c_str());
EXPECT_THAT(vfs->nfile, 0);
mj_deleteVFS(mj_vfs.get());
mj_deleteVFS(vfs.get());
}
TEST_F(EngineVfsTest, AddBufferTest) {
auto vfs = std::make_unique<mjVFS>();
mj_defaultVFS(vfs.get());
std::string buffer = "<mujoco/>";
mj_addBufferVFS(vfs.get(), "model", static_cast<const void*>(buffer.c_str()),
buffer.size());
std::array<char, 1024> error;
mjModel* model = mj_loadXML("model", vfs.get(), error.data(), error.size());
EXPECT_THAT(model, NotNull());
mj_deleteModel(model);
mj_deleteVFS(vfs.get());
}
} // namespace
+5 -1
View File
@@ -4938,6 +4938,7 @@ public unsafe struct _mjVFS
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000 * 1000)] public char[] filename;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public UIntPtr[] filesize;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public IntPtr[] filedata;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2000)] public UInt64[] filestamp;
}
[StructLayout(LayoutKind.Sequential)]
@@ -6321,7 +6322,7 @@ public static unsafe extern void mj_defaultVFS(void* vfs);
public static unsafe extern int mj_addFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string directory, [MarshalAs(UnmanagedType.LPStr)]string filename);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_makeEmptyFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string filename, int filesize);
public static unsafe extern int mj_addBufferVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string name, void* buffer, int nbuffer);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_findFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string filename);
@@ -6332,6 +6333,9 @@ public static unsafe extern int mj_deleteFileVFS(void* vfs, [MarshalAs(Unmanaged
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mj_deleteVFS(void* vfs);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern int mj_makeEmptyFileVFS(void* vfs, [MarshalAs(UnmanagedType.LPStr)]string filename, int filesize);
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern mjModel_* mj_loadXML([MarshalAs(UnmanagedType.LPStr)]string filename, void* vfs, StringBuilder error, int error_sz);