Add various improvements for resource providers.
PiperOrigin-RevId: 529706437 Change-Id: I20385e031446674584349c301982dbe13b812477
This commit is contained in:
committed by
Copybara-Service
parent
b798f89212
commit
67f0f5154f
@@ -621,7 +621,7 @@ void mj_saveModel(const mjModel* m, const char* filename, void* buffer, int buff
|
||||
|
||||
|
||||
// load model from binary MJB resource
|
||||
static mjModel* _mj_loadModel(const char* filename, int default_provider) {
|
||||
static mjModel* _mj_loadModel(const char* filename, int vfs_provider) {
|
||||
int header[4] = {0};
|
||||
int expected_header[4] = {ID, sizeof(mjtNum), getnint(), getnptr()};
|
||||
int info[2000];
|
||||
@@ -629,7 +629,7 @@ static mjModel* _mj_loadModel(const char* filename, int default_provider) {
|
||||
mjModel *m = 0;
|
||||
mjResource* r = NULL;
|
||||
|
||||
if((r = mju_openResource(filename, default_provider)) == NULL) {
|
||||
if((r = mju_openResource(filename, vfs_provider)) == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
+66
-20
@@ -21,6 +21,7 @@
|
||||
#include "engine/engine_plugin.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cctype>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -266,14 +267,53 @@ bool PluginsAreIdentical(const mjpPlugin& plugin1, const mjpPlugin& plugin2) {
|
||||
return !std::memcmp(ptr1, ptr2, remaining_size);
|
||||
}
|
||||
|
||||
// does case insensitive comparison
|
||||
bool PrefixesAreIdentical(const char* p1, const char* p2) {
|
||||
int i = 0;
|
||||
for (; p1[i] != '\0'; i++) {
|
||||
if (std::tolower(p1[i]) != std::tolower(p2[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return p2[i] == '\0';
|
||||
}
|
||||
|
||||
// check if two resource providers are identical
|
||||
bool ResourceProvidersAreIdentical(const mjpResourceProvider* p1, const mjpResourceProvider* p2) {
|
||||
return (!std::strcmp(p1->prefix, p2->prefix) &&
|
||||
return (PrefixesAreIdentical(p1->prefix, p2->prefix) &&
|
||||
p1->open == p2->open &&
|
||||
p1->read == p2->read &&
|
||||
p1->close == p2->close &&
|
||||
p1->data == p2->data);
|
||||
}
|
||||
|
||||
// check if prefix is a valid URI scheme format
|
||||
bool IsValidURISchemeFormat(const char* prefix) {
|
||||
int len;
|
||||
|
||||
// prefix is NULL or empty
|
||||
if (prefix == nullptr || !(len = std::strlen(prefix))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// first character must be a letter
|
||||
if (!std::isalpha(prefix[0])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; i < len; i++) {
|
||||
// each following character must be a letter, digit, '+', '.', or '-'
|
||||
if (!std::isalnum(prefix[i]) &&
|
||||
(prefix[i] != '+') &&
|
||||
(prefix[i] != '.') &&
|
||||
(prefix[i] != '-')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// globally register a plugin (thread-safe), return new slot id
|
||||
@@ -508,12 +548,7 @@ void mjp_defaultResourceProvider(mjpResourceProvider* provider) {
|
||||
// globally register a resource provider (thread-safe), return new slot id
|
||||
int mjp_registerResourceProvider(const mjpResourceProvider* provider) {
|
||||
// check against reserved prefixes
|
||||
int n = std::strlen(provider->prefix),
|
||||
m = std::strlen(kVfsPrefix);
|
||||
|
||||
// one of the prefixes is a subprefix of the other
|
||||
if (!std::strncmp(kVfsPrefix, provider->prefix, n) ||
|
||||
!std::strncmp(kVfsPrefix, provider->prefix, m)) {
|
||||
if (PrefixesAreIdentical(kVfsPrefix, provider->prefix)) {
|
||||
mju_warning("provider->prefix is '%s' which is reserved", provider->prefix);
|
||||
return -1;
|
||||
}
|
||||
@@ -523,8 +558,10 @@ int mjp_registerResourceProvider(const mjpResourceProvider* provider) {
|
||||
|
||||
// internal version of mjp_registerResourceProvider without prechecks on reserved prefixes
|
||||
int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
||||
if (!provider->prefix || provider->prefix[0] == '\0') {
|
||||
mju_warning("provider->prefix is an empty string");
|
||||
// check if prefix is valid URI scheme format
|
||||
if (!IsValidURISchemeFormat(provider->prefix)) {
|
||||
mju_warning("provider->prefix is '%s' which is not a valid URI scheme format",
|
||||
provider->prefix);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -544,7 +581,7 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
||||
std::unique_ptr<char[]> prefix;
|
||||
|
||||
// check if this is a VFS provider
|
||||
if (!std::strcmp(mjVFS_PREFIX, provider->prefix)) {
|
||||
if (PrefixesAreIdentical(mjVFS_PREFIX, provider->prefix)) {
|
||||
vfs_provider = true;
|
||||
}
|
||||
|
||||
@@ -589,13 +626,8 @@ int mjp_registerResourceProviderInternal(const mjpResourceProvider* provider) {
|
||||
}
|
||||
|
||||
if (!vfs_provider && existing.prefix != nullptr) {
|
||||
int n = std::strlen(provider->prefix);
|
||||
int m = std::strlen(existing.prefix);
|
||||
|
||||
// one of the prefixes is a subprefix of the other
|
||||
if (!std::strncmp(existing.prefix, provider->prefix, n) ||
|
||||
!std::strncmp(existing.prefix, provider->prefix, m)) {
|
||||
// if identical then return slot number
|
||||
// if identical then return slot number
|
||||
if (PrefixesAreIdentical(provider->prefix, existing.prefix)) {
|
||||
if (ResourceProvidersAreIdentical(provider, &existing)) {
|
||||
return i;
|
||||
} else {
|
||||
@@ -691,20 +723,34 @@ int mjp_resourceProviderCount() {
|
||||
return GetGlobal<mjpResourceProvider>().count().load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
// look up a resource provider that matches its prefix against the given resource name
|
||||
// look up a resource provider that matches its prefix against the given resource scheme
|
||||
const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
|
||||
const int count = mjp_resourceProviderCount();
|
||||
if (!resource_name || !resource_name[0]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char* ch = std::strchr(resource_name, ':');
|
||||
if (ch == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int n = ch - resource_name;
|
||||
std::string file_prefix = std::string(resource_name, n);
|
||||
|
||||
// return NULL if file_prefix doesn't have a valid URI scheme syntax
|
||||
if (!IsValidURISchemeFormat(file_prefix.c_str())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// since multiple VFS resource providers can be registered with the same
|
||||
// prefix, it doesn't make sense to try to match against them
|
||||
if (!std::strncmp(kVfsPrefix, resource_name, std::strlen(kVfsPrefix))) {
|
||||
if (PrefixesAreIdentical(kVfsPrefix, file_prefix.c_str())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Global<mjpResourceProvider>& global = GetGlobal<mjpResourceProvider>();
|
||||
auto lock = global.lock_mutex_exclusively();
|
||||
PluginTable<mjpResourceProvider>* table = &global.table();
|
||||
int found_slot = 0;
|
||||
|
||||
@@ -717,7 +763,7 @@ const mjpResourceProvider* mjp_getResourceProvider(const char* resource_name) {
|
||||
const char *prefix = provider.prefix;
|
||||
|
||||
if (prefix != nullptr &&
|
||||
!std::strncmp(prefix, resource_name, std::strlen(prefix))) {
|
||||
PrefixesAreIdentical(prefix, file_prefix.c_str())) {
|
||||
return &provider;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,21 @@ typedef struct {
|
||||
int nbuffer;
|
||||
} file_buffer;
|
||||
|
||||
// helper function to fill data from resource provider into provider
|
||||
static void fillResource(const mjpResourceProvider* provider, mjResource* resource) {
|
||||
if (provider == NULL) {
|
||||
resource->read = NULL;
|
||||
resource->close = NULL;
|
||||
resource->getdir = NULL;
|
||||
resource->provider_data = NULL;
|
||||
} else {
|
||||
resource->read = provider->read;
|
||||
resource->close = provider->close;
|
||||
resource->getdir = provider->getdir;
|
||||
resource->provider_data = provider->data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// open the given resource; if the name doesn't have a prefix matching with a
|
||||
// resource provider, then the default_provider is used
|
||||
@@ -54,9 +69,7 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
// find provider based off prefix of name
|
||||
provider = mjp_getResourceProvider(name);
|
||||
if (provider != NULL) {
|
||||
resource->read = provider->read;
|
||||
resource->close = provider->close;
|
||||
resource->provider_data = provider->data;
|
||||
fillResource(provider, resource);
|
||||
if (provider->open(resource)) {
|
||||
return resource;
|
||||
}
|
||||
@@ -79,9 +92,8 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
mju_free(resource);
|
||||
return NULL;
|
||||
}
|
||||
resource->read = provider->read;
|
||||
resource->close = provider->close;
|
||||
resource->provider_data = provider->data;
|
||||
|
||||
fillResource(provider, resource);
|
||||
if (provider->open(resource)) {
|
||||
return resource;
|
||||
}
|
||||
@@ -96,9 +108,7 @@ mjResource* mju_openResource(const char* name, int default_provider) {
|
||||
|
||||
// lastly fallback to OS filesystem
|
||||
else {
|
||||
resource->read = NULL;
|
||||
resource->close = NULL;
|
||||
resource->provider_data = NULL;
|
||||
fillResource(NULL, resource);
|
||||
resource->data = mju_malloc(sizeof(file_buffer));
|
||||
file_buffer* fb = (file_buffer*) resource->data;
|
||||
fb->buffer = mju_fileToMemory(name, &(fb->nbuffer));
|
||||
@@ -152,7 +162,7 @@ int mju_readResource(mjResource* resource, const void** buffer) {
|
||||
}
|
||||
|
||||
|
||||
// if provider is NULL, then OS filesystem is used
|
||||
// if provider read callback is NULL, then OS filesystem is used
|
||||
const file_buffer* fb = (file_buffer*) resource->data;
|
||||
*buffer = fb->buffer;
|
||||
return fb->nbuffer;
|
||||
@@ -160,6 +170,41 @@ int mju_readResource(mjResource* resource, const void** buffer) {
|
||||
|
||||
|
||||
|
||||
// get directory path of resource
|
||||
void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
|
||||
*dir = NULL;
|
||||
*ndir = 0;
|
||||
|
||||
if (resource == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
// provider is not OS filesystem
|
||||
if (resource->read) {
|
||||
if (resource->getdir) {
|
||||
resource->getdir(resource, dir, ndir);
|
||||
}
|
||||
} else {
|
||||
mju_getfiledir(resource->name, dir, ndir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// get directory path of file in OS filesystem
|
||||
void mju_getfiledir(const char* filename, const char** dir, int* ndir) {
|
||||
*dir = NULL;
|
||||
*ndir = 0;
|
||||
char* ch;
|
||||
|
||||
if ((filename != NULL) &&
|
||||
((ch = strrchr(filename, '/')) ||
|
||||
(ch = strrchr(filename, '\\')))) {
|
||||
*dir = filename;
|
||||
*ndir = (int)(ch - filename) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// read file into memory buffer (allocated here with mju_malloc)
|
||||
void* mju_fileToMemory(const char* filename, int* filesize) {
|
||||
// open file
|
||||
|
||||
@@ -34,6 +34,12 @@ 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
|
||||
MJAPI void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir);
|
||||
|
||||
// get directory of a file in the OS filesystem
|
||||
void mju_getfiledir(const char* filename, const char** dir, int* ndir);
|
||||
|
||||
// read file into memory buffer (allocated here with mju_malloc)
|
||||
void* mju_fileToMemory(const char* filename, int* filesize);
|
||||
|
||||
|
||||
@@ -251,6 +251,18 @@ static void vfs_close_callback(mjResource* resource) {
|
||||
|
||||
|
||||
|
||||
// getdir callback for the VFS resource provider
|
||||
static void vfs_getdir_callback(mjResource* resource, const char** dir, int* ndir) {
|
||||
*dir = NULL;
|
||||
*ndir = 0;
|
||||
|
||||
if (resource) {
|
||||
mju_getfiledir(resource->name, dir, ndir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// registers a VFS resource provider; returns the index of the provider
|
||||
int mj_registerVfsProvider(const mjVFS* vfs) {
|
||||
mjpResourceProvider provider = {
|
||||
@@ -258,6 +270,7 @@ int mj_registerVfsProvider(const mjVFS* vfs) {
|
||||
.open = &vfs_open_callback,
|
||||
.read = &vfs_read_callback,
|
||||
.close = &vfs_close_callback,
|
||||
.getdir = &vfs_getdir_callback,
|
||||
.data = (void*) vfs
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user