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:
committed by
Copybara-Service
parent
042760eba8
commit
a4a621f6e3
@@ -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
@@ -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
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user