Don't read from file until mju_readResource is called.

PiperOrigin-RevId: 615434757
Change-Id: I96a1e77f74203e91bc62dc24ac9225810fcacb20
This commit is contained in:
Kyle Bayes
2024-03-13 08:54:46 -07:00
committed by Copybara-Service
parent 40baa51072
commit 0d16587135
+46 -21
View File
@@ -35,12 +35,17 @@
#include "engine/engine_plugin.h" #include "engine/engine_plugin.h"
#include "engine/engine_util_errmem.h" #include "engine/engine_util_errmem.h"
// internal helper for mju_fileToMemory (closes fp automatically)
static void* _fileToMemory(FILE* fp, const char* filename, size_t* filesize);
// file buffer used internally for the OS filesystem // file buffer used internally for the OS filesystem
typedef struct { typedef struct {
FILE* fp; // file handle
int is_read; // set to nonzero if buffer was read into
uint8_t* buffer; // raw bytes from file uint8_t* buffer; // raw bytes from file
size_t nbuffer; // size of buffer in bytes size_t nbuffer; // size of buffer in bytes
time_t mtime; // last modified time time_t mtime; // last modified time
} file_buffer; } file_spec;
// open the given resource; if the name doesn't have a prefix matching with a // open the given resource; if the name doesn't have a prefix matching with a
// resource provider, then the OS filesystem is used // resource provider, then the OS filesystem is used
@@ -89,10 +94,13 @@ mjResource* mju_openResource(const char* name, char* error, size_t error_sz) {
// lastly fallback to OS filesystem // lastly fallback to OS filesystem
resource->provider = NULL; resource->provider = NULL;
resource->data = mju_malloc(sizeof(file_buffer)); resource->data = mju_malloc(sizeof(file_spec));
file_buffer* fb = (file_buffer*) resource->data; file_spec* spec = (file_spec*) resource->data;
fb->buffer = mju_fileToMemory(name, &(fb->nbuffer)); spec->is_read = 0;
if (fb->buffer == NULL) { spec->buffer = NULL;
spec->nbuffer = 0;
spec->fp = fopen(name, "rb");
if (!spec->fp) {
if (error) { if (error) {
snprintf(error, error_sz, snprintf(error, error_sz,
"resource not found via provider or OS filesystem: '%s'", name); "resource not found via provider or OS filesystem: '%s'", name);
@@ -102,12 +110,13 @@ mjResource* mju_openResource(const char* name, char* error, size_t error_sz) {
} }
struct stat file_stat; struct stat file_stat;
if (stat(name, &file_stat) == 0) { if (stat(name, &file_stat) == 0) {
memcpy(&fb->mtime, &file_stat.st_mtime, sizeof(time_t)); memcpy(&spec->mtime, &file_stat.st_mtime, sizeof(time_t));
} else { } else {
memset(&fb->mtime, 0, sizeof(time_t)); memset(&spec->mtime, 0, sizeof(time_t));
resource->timestamp[0] = '\0'; resource->timestamp[0] = '\0';
} }
strftime(resource->timestamp, 512, "%Y-%m-%d-%H:%M:%S", localtime(&(fb->mtime))); strftime(resource->timestamp, 512, "%Y-%m-%d-%H:%M:%S",
localtime(&(spec->mtime)));
return resource; return resource;
} }
@@ -124,10 +133,10 @@ void mju_closeResource(mjResource* resource) {
resource->provider->close(resource); resource->provider->close(resource);
} else { } else {
// clear OS filesystem if present // clear OS filesystem if present
file_buffer* fb = (file_buffer*) resource->data; file_spec* spec = (file_spec*) resource->data;
if (fb) { if (spec) {
if (fb->buffer) mju_free(fb->buffer); if (spec->buffer) mju_free(spec->buffer);
mju_free(fb); mju_free(spec);
} }
} }
@@ -151,9 +160,19 @@ int mju_readResource(mjResource* resource, const void** buffer) {
// if provider is NULL, then OS filesystem is used // if provider is NULL, then OS filesystem is used
const file_buffer* fb = (file_buffer*) resource->data; file_spec* spec = (file_spec*) resource->data;
*buffer = fb->buffer; if (!spec->fp && !spec->is_read) {
return fb->nbuffer; mjERROR("internal error FILE pointer undefined"); // should not occur
}
// only read once from file
if (!spec->is_read) {
spec->buffer = _fileToMemory(spec->fp, resource->name, &(spec->nbuffer));
spec->fp = NULL; // closed by _fileToMemory
spec->is_read = 1;
}
*buffer = spec->buffer;
return spec->nbuffer;
} }
@@ -181,11 +200,11 @@ void mju_getResourceDir(mjResource* resource, const char** dir, int* ndir) {
// modified callback for OS filesystem // modified callback for OS filesystem
static int mju_isModifiedFile(const char* name, const file_buffer* fb) { static int mju_isModifiedFile(const char* name, const file_spec* spec) {
if (fb != NULL) { if (spec != NULL) {
struct stat file_stat; struct stat file_stat;
if (stat(name, &file_stat) == 0) { if (stat(name, &file_stat) == 0) {
return difftime(fb->mtime, file_stat.st_mtime) < 0; return difftime(spec->mtime, file_stat.st_mtime) < 0;
} }
} }
return 1; // modified (default) return 1; // modified (default)
@@ -205,7 +224,7 @@ int mju_isModifiedResource(const mjResource* resource, const char* timestamp) {
return 1; // default (modified) return 1; // default (modified)
} }
return mju_isModifiedFile(resource->name, (file_buffer*) resource->data); return mju_isModifiedFile(resource->name, (file_spec*) resource->data);
} }
@@ -230,13 +249,19 @@ int mju_dirnamelen(const char* path) {
// read file into memory buffer (allocated here with mju_malloc) // read file into memory buffer (allocated here with mju_malloc)
void* mju_fileToMemory(const char* filename, size_t* filesize) { void* mju_fileToMemory(const char* filename, size_t* filesize) {
// open file
*filesize = 0;
FILE* fp = fopen(filename, "rb"); FILE* fp = fopen(filename, "rb");
if (!fp) { if (!fp) {
return NULL; return NULL;
} }
return _fileToMemory(fp, filename, filesize);
}
// internal helper for mju_fileToMemory (closes fp automatically)
static void* _fileToMemory(FILE* fp, const char* filename, size_t* filesize) {
// open file
*filesize = 0;
// find size // find size
if (fseek(fp, 0, SEEK_END) != 0) { if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp); fclose(fp);