Ensure parsing always has a valid VFS.

Explicitly free allocated memory for local resources. (Upcoming
changes to `mju_closeResource` will not work for local resources.)

PiperOrigin-RevId: 854254013
Change-Id: I5cbb8264b590327767d5500f0c2a32ebe036bc4f
This commit is contained in:
Haroon Qureshi
2026-01-09 10:44:53 -08:00
committed by Copybara-Service
parent e37c2268a6
commit 9e9314a4a4
2 changed files with 32 additions and 5 deletions
+20 -5
View File
@@ -67,20 +67,36 @@ mjSpec* mj_copySpec(const mjSpec* s) {
// parse file into spec
mjSpec* mj_parse(const char* filename, const char* content_type,
const mjVFS* vfs, char* error, int error_sz) {
mjVFS local_vfs;
mujoco::user::Cleanup cleanup;
// early exit for existing XML workflow
auto filepath = mujoco::user::FilePath(filename);
if (filepath.Ext() == ".xml" || (content_type && std::strcmp(content_type, "text/xml") == 0)) {
return mj_parseXML(filename, vfs, error, error_sz);
}
// If no VFS is provided, we'll create our own temporary one for the duration
// of this function.
if (vfs == nullptr) {
mj_defaultVFS(&local_vfs);
cleanup += [&local_vfs](){ mj_deleteVFS(&local_vfs); };
vfs = &local_vfs;
}
mjResource* resource = mju_openResource("", filename, vfs, error, error_sz);
// If we are unable to open the resource, we will create our own resource with
// just the filename. This allows decoders that rely on other systems to fetch
// their content to function without a custom resource provider.
// For example, USD may use identifiers to assets that are strictly in memory
// or that are fetched on a need-be basis via URI.
if (!resource) {
if (resource) {
cleanup += [resource](){ mju_closeResource(resource); };
} else {
resource = (mjResource*) mju_malloc(sizeof(mjResource));
cleanup += [resource](){ if (resource) mju_free(resource); };
if (resource == nullptr) {
if (error) {
strncpy(error, "could not allocate memory", error_sz);
@@ -96,20 +112,19 @@ mjSpec* mj_parse(const char* filename, const char* content_type,
std::string fullname = filename;
std::size_t n = fullname.size();
resource->name = (char*) mju_malloc(sizeof(char) * (n + 1));
cleanup += [resource](){ if (resource) mju_free(resource->name); };
if (resource->name == nullptr) {
if (error) {
strncpy(error, "could not allocate memory", error_sz);
error[error_sz - 1] = '\0';
}
mju_closeResource(resource);
return nullptr;
}
memcpy(resource->name, fullname.c_str(), sizeof(char) * (n + 1));
}
mjSpec* spec = mju_decodeResource(resource, content_type, vfs);
mju_closeResource(resource);
return spec;
return mju_decodeResource(resource, content_type, vfs);
}
// compile model
+12
View File
@@ -18,9 +18,12 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
const double mjEPS = 1E-14; // minimum value in various calculations
@@ -235,6 +238,15 @@ class FilePath {
std::string path_;
};
// utility class for scoping resources to functions
struct Cleanup {
using Fn = std::function<void()>;
~Cleanup() { for (auto& f : cleanup) f(); }
void operator+=(Fn f) { cleanup.push_front(std::move(f)); }
std::deque<Fn> cleanup;
};
// read file into memory buffer
std::vector<uint8_t> FileToMemory(const char* filename);