diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index 15c82ff9..49c48a4b 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -2053,8 +2053,8 @@ mjfEncode .. code-block:: C - typedef int (*mjfEncode)(const mjSpec* s, const mjModel* m, const mjVFS* vfs, - mjResource* resource); + typedef mjtSize (*mjfEncode)(const mjSpec* s, const mjModel* m, const mjVFS* vfs, + mjResource* resource); This callback populates the :ref:`mjResource` `data` member with bytes representing the diff --git a/doc/changelog.rst b/doc/changelog.rst index bb6af630..0a9041f0 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -10,6 +10,11 @@ General - Added Nesterov momentum extrapolation with adaptive gradient restart (O'Donoghue-Candès) to the PGS solver, significantly improving convergence. Overall PGS now requires ~2x fewer iterations. +.. admonition:: Breaking API changes + :class: attention + + - Return type of :ref:`mj_encode` and the :ref:`mjfEncode` callback changed from ``int`` to ``mjtSize`` (64-bit). + Version 3.10.0 (June 22, 2026) ------------------------------ diff --git a/doc/includes/references.h b/doc/includes/references.h index 0887af0b..c37e2495 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -3231,9 +3231,9 @@ mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, int err mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz); mjSpec* mj_parse(const char* filename, const char* content_type, const mjVFS* vfs, char* error, int error_sz); -int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, - const char* content_type, const mjVFS* vfs, char* error, - int error_sz); +mjtSize mj_encode(const mjSpec* s, const mjModel* m, const char* filename, + const char* content_type, const mjVFS* vfs, char* error, + int error_sz); mjModel* mj_compile(mjSpec* s, const mjVFS* vfs); int mj_copyBack(mjSpec* s, const mjModel* m); int mj_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d); diff --git a/include/mujoco/mjplugin.h b/include/mujoco/mjplugin.h index ae6bfb11..4901833a 100644 --- a/include/mujoco/mjplugin.h +++ b/include/mujoco/mjplugin.h @@ -91,8 +91,8 @@ typedef struct mjpDecoder { //---------------------------------- Encoder ------------------------------------------------------- -typedef int (*mjfEncode)(const mjSpec* s, const mjModel* m, const mjVFS* vfs, - mjResource* resource); +typedef mjtSize (*mjfEncode)(const mjSpec* s, const mjModel* m, const mjVFS* vfs, + mjResource* resource); typedef struct mjpEncoder { const char* content_type; diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index 35b2e58d..62f476bc 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -147,9 +147,9 @@ MJAPI mjSpec* mj_parse(const char* filename, const char* content_type, // Encode spec/model to a file using a registered encoder. // Returns the number of bytes written on success, -1 on failure. // Nullable: m, vfs, error -MJAPI int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, - const char* content_type, const mjVFS* vfs, char* error, - int error_sz); +MJAPI mjtSize mj_encode(const mjSpec* s, const mjModel* m, const char* filename, + const char* content_type, const mjVFS* vfs, char* error, + int error_sz); // Compile spec to model. // Nullable: vfs diff --git a/python/mujoco/introspect/functions.py b/python/mujoco/introspect/functions.py index fc817cf9..9325d3d4 100644 --- a/python/mujoco/introspect/functions.py +++ b/python/mujoco/introspect/functions.py @@ -436,7 +436,7 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ('mj_encode', FunctionDecl( name='mj_encode', - return_type=ValueType(name='int'), + return_type=ValueType(name='mjtSize'), parameters=( FunctionParameterDecl( name='s', diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc index 8bc0d00d..a11fd950 100644 --- a/python/mujoco/specs.cc +++ b/python/mujoco/specs.cc @@ -545,7 +545,7 @@ PYBIND11_MODULE(_specs, m) { "encode", [](MjSpec& self, std::string filename, std::optional model, - std::optional content_type) -> int { + std::optional content_type) -> mjtSize { raw::MjModel* m = nullptr; if (model.has_value() && !model->is_none()) { auto& wrapper = @@ -572,8 +572,8 @@ PYBIND11_MODULE(_specs, m) { err[0] = '\0'; const char* ct = content_type.has_value() ? content_type->c_str() : nullptr; - int nbytes = mj_encode(self.ptr, m, filename.c_str(), ct, - vfs_ptr, err.data(), err.size()); + mjtSize nbytes = mj_encode(self.ptr, m, filename.c_str(), ct, + vfs_ptr, err.data(), err.size()); if (vfs_ptr) { mj_deleteVFS(vfs_ptr); diff --git a/src/user/user_api.cc b/src/user/user_api.cc index d5e61f7b..dd49873c 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -146,9 +146,9 @@ mjSpec* mj_parse(const char* filename, const char* content_type, } // encode spec/model to file -int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, - const char* content_type, const mjVFS* vfs, char* error, - int error_sz) { +mjtSize mj_encode(const mjSpec* s, const mjModel* m, const char* filename, + const char* content_type, const mjVFS* vfs, char* error, + int error_sz) { // TODO(shaves) Move MJCF and URDF to encoders/decoders. auto filepath = mujoco::user::FilePath(filename); if (filepath.Ext() == ".xml" || @@ -158,7 +158,7 @@ int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, return -1; } - return std::filesystem::file_size(filename); + return static_cast(std::filesystem::file_size(filename)); } const mjpEncoder* encoder = mjp_findEncoder(filename, content_type); @@ -174,7 +174,7 @@ int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, memset(&resource, 0, sizeof(resource)); resource.name = const_cast(filename); - const int nbytes = encoder->encode(s, m, vfs, &resource); + const mjtSize nbytes = encoder->encode(s, m, vfs, &resource); if (nbytes < 0 || !resource.data) { if (error) { strncpy(error, "encoder failed", error_sz); @@ -197,7 +197,7 @@ int mj_encode(const mjSpec* s, const mjModel* m, const char* filename, fclose(fp); encoder->close_resource(&resource); - if (static_cast(written) != nbytes) { + if (static_cast(written) != nbytes) { if (error) { strncpy(error, "failed to write all bytes to file", error_sz); error[error_sz - 1] = '\0'; diff --git a/src/xml/mjz/mjz_encoder.cc b/src/xml/mjz/mjz_encoder.cc index b209fb04..75c8fa73 100644 --- a/src/xml/mjz/mjz_encoder.cc +++ b/src/xml/mjz/mjz_encoder.cc @@ -306,8 +306,8 @@ std::unordered_map CollectAssets( return archive_entries; } -int MjzEncode(const mjSpec* spec, const mjModel* model, const mjVFS* vfs, - mjResource* resource) { +mjtSize MjzEncode(const mjSpec* spec, const mjModel* model, const mjVFS* vfs, + mjResource* resource) { if (!spec || !resource) { return -1; } @@ -399,7 +399,7 @@ int MjzEncode(const mjSpec* spec, const mjModel* model, const mjVFS* vfs, resource->data = archive_buf; - return static_cast(archive_size); + return static_cast(archive_size); } void MjzCloseResource(mjResource* resource) { diff --git a/test/plugin/encoder/encoder_test.cc b/test/plugin/encoder/encoder_test.cc index ceb43f63..1451c14f 100644 --- a/test/plugin/encoder/encoder_test.cc +++ b/test/plugin/encoder/encoder_test.cc @@ -34,8 +34,8 @@ struct FakeEncoderOutput { char resource_name[512]; }; -int FakeEncode(const mjSpec* s, const mjModel* m, const mjVFS* vfs, - mjResource* resource) { +mjtSize FakeEncode(const mjSpec* s, const mjModel* m, const mjVFS* vfs, + mjResource* resource) { auto* output = new FakeEncoderOutput; output->nbody = m->nbody; output->ngeom = m->ngeom; @@ -43,7 +43,7 @@ int FakeEncode(const mjSpec* s, const mjModel* m, const mjVFS* vfs, std::snprintf(output->resource_name, sizeof(output->resource_name), "%s", resource->name); resource->data = output; - return sizeof(FakeEncoderOutput); + return static_cast(sizeof(FakeEncoderOutput)); } void CloseResource(mjResource* resource) { diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index 2cf104ec..e3f10d13 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -6690,7 +6690,7 @@ public static unsafe extern void mj_clearCache(mjCache_* cache); public static unsafe extern mjModel_* mj_loadXML([MarshalAs(UnmanagedType.LPStr)]string filename, void* vfs, StringBuilder error, int error_sz); [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] -public static unsafe extern int mj_encode(void* s, mjModel_* m, [MarshalAs(UnmanagedType.LPStr)]string filename, [MarshalAs(UnmanagedType.LPStr)]string content_type, void* vfs, StringBuilder error, int error_sz); +public static unsafe extern UInt64 mj_encode(void* s, mjModel_* m, [MarshalAs(UnmanagedType.LPStr)]string filename, [MarshalAs(UnmanagedType.LPStr)]string content_type, void* vfs, StringBuilder error, int error_sz); [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern int mj_saveLastXML([MarshalAs(UnmanagedType.LPStr)]string filename, mjModel_* m, StringBuilder error, int error_sz);