From b65aaf3d20d9ad8d1ed575345cb99953999c66fe Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Sat, 10 Oct 2026 17:40:52 -0500 Subject: [PATCH 01/13] fix memory leak --- src/lib.rs | 3 ++ src/server.rs | 8 ++-- src/ua.rs | 4 +- src/ua/array.rs | 48 ++++++++++++------- src/ua/data_types/application_description.rs | 8 ++-- src/ua/data_types/browse_next_request.rs | 19 ++++---- src/ua/data_types/content_filter_element.rs | 13 +++-- .../delete_monitored_items_request.rs | 19 ++++---- .../delete_subscriptions_request.rs | 13 +++-- .../node_attributes/variable_attributes.rs | 8 ++-- 10 files changed, 79 insertions(+), 64 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fb88ee90..48e1dbad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,6 +267,9 @@ use self::{ monitored_item::create_monitored_items_callback, value::{ArrayValue, NonScalarValue}, }; + +pub(crate) use ua::UaIterExt; + #[cfg(feature = "mbedtls")] pub use self::{ ssl::{Certificate, Password, PrivateKey, create_certificate}, diff --git a/src/server.rs b/src/server.rs index d204274f..400644b1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -30,7 +30,7 @@ use parking_lot::{Condvar, Mutex, MutexGuard}; use crate::{ Attribute, Attributes, BrowseResult, DEFAULT_PORT_NUMBER, DataType, DataValue, Error, Result, - ua, + UaIterExt as _, ua, }; pub(crate) use self::node_context::NodeContext; @@ -184,10 +184,10 @@ impl ServerBuilder { #[must_use] pub fn server_urls(mut self, server_urls: &[&str]) -> Self { let config = self.config_mut(); - let server_urls = server_urls + server_urls .iter() - .map(|server_url| ua::String::new(server_url).unwrap()); - ua::Array::from_iter(server_urls) + .map(|server_url| ua::String::new(server_url).unwrap()) + .collect_ua_array() .move_into_raw(&mut config.serverUrlsSize, &mut config.serverUrls); self } diff --git a/src/ua.rs b/src/ua.rs index 4449772c..8ac526df 100644 --- a/src/ua.rs +++ b/src/ua.rs @@ -73,4 +73,6 @@ pub use self::{ unit_id::UnitId, user_identity_token::UserIdentityToken, }; -pub(crate) use self::{client_config::ClientConfig, logger::Logger, server_config::ServerConfig}; +pub(crate) use self::{ + array::UaIterExt, client_config::ClientConfig, logger::Logger, server_config::ServerConfig, +}; diff --git a/src/ua/array.rs b/src/ua/array.rs index dcd8edff..9aeab38d 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -83,22 +83,6 @@ impl Array { Self(State::NonEmpty { ptr: array, size }) } - /// Creates new array from existing elements. - /// - /// This takes ownership of the elements from the given iterator. - /// - /// # Panics - /// - /// Enough memory must be available to allocate array. - pub(crate) fn from_iter>(iter: I) -> Self { - // This creates a temporary copy by first collecting all elements into a `Vec` and then once - // more by copying elements from the `Vec` into the new array from `UA_Array_new()`. - // - // TODO: Avoid temporary copy. How to deal with unknown size of iterator when initializing a - // new array to hold its elements? - Self::from_slice(&iter.collect::>()) - } - /// Creates new array from existing elements. /// /// This copies over the elements from the given slice. The array will own the copies, and clean @@ -401,8 +385,11 @@ impl Array { /// /// After this, it is the responsibility of `dst` to eventually clean up the data. pub(crate) fn move_into_raw(self, dst_size: &mut usize, dst: &mut *mut T::Inner) { - // Make sure to clean up any previous value in target. - let _unused = Self::from_raw_parts(*dst_size, *dst); + // Make sure to clean up any previous value in target. This must move out of (not copy) the + // existing array, otherwise its allocation would be leaked. + // + // SAFETY: `dst` is the sole owner of the previous array, which we take over and drop here. + let _unused = unsafe { Self::move_from_raw_parts(dst_size, dst) }; let (size, ptr) = self.into_raw_parts(); *dst_size = size; @@ -456,6 +443,31 @@ impl Array { } } +/// Internal-only helper for collection into [`Array`]s. +/// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized +pub(crate) trait UaIterExt +where + Self: Iterator + Sized, + T: DataType, +{ + fn collect_ua_array(self) -> Array { + // This creates a temporary copy by first collecting all elements into a `Vec` and then once + // more by copying elements from the `Vec` into the new array from `UA_Array_new()`. + // + // TODO: Avoid temporary copy. How to deal with unknown size of iterator when initializing a + // new array to hold its elements? (possible optimization opportunity with a specialized + // implementation for `ExactSizeIterator`) + Array::from_slice(&self.collect::>()) + } +} + +impl UaIterExt for I +where + I: Iterator, + T: DataType, +{ +} + // SAFETY: Arrays in `open62541` can be sent across thread boundaries: they may be dropped in other // threads as long as their items are `Send`. unsafe impl Send for Array {} diff --git a/src/ua/data_types/application_description.rs b/src/ua/data_types/application_description.rs index 10a8e316..27a28787 100644 --- a/src/ua/data_types/application_description.rs +++ b/src/ua/data_types/application_description.rs @@ -1,4 +1,4 @@ -use crate::{DataType as _, ua}; +use crate::{DataType as _, UaIterExt as _, ua}; crate::data_type!(ApplicationDescription); @@ -86,10 +86,10 @@ impl ApplicationDescription { /// The strings must not contain any NUL bytes. #[must_use] pub fn with_discovery_urls(mut self, discovery_urls: &[&str]) -> Self { - let discovery_urls = discovery_urls + discovery_urls .iter() - .map(|discovery_url| ua::String::new(discovery_url).unwrap()); - ua::Array::from_iter(discovery_urls) + .map(|discovery_url| ua::String::new(discovery_url).unwrap()) + .collect_ua_array() .move_into_raw(&mut self.0.discoveryUrlsSize, &mut self.0.discoveryUrls); self } diff --git a/src/ua/data_types/browse_next_request.rs b/src/ua/data_types/browse_next_request.rs index c2e89457..bfe7a09c 100644 --- a/src/ua/data_types/browse_next_request.rs +++ b/src/ua/data_types/browse_next_request.rs @@ -1,4 +1,4 @@ -use crate::{DataType as _, ServiceRequest, ua}; +use crate::{DataType as _, ServiceRequest, UaIterExt as _, ua}; crate::data_type!(BrowseNextRequest); @@ -8,15 +8,14 @@ impl BrowseNextRequest { mut self, continuation_points: &[ua::ContinuationPoint], ) -> Self { - let array = ua::Array::from_iter( - continuation_points - .iter() - .map(ua::ContinuationPoint::to_byte_string), - ); - array.move_into_raw( - &mut self.0.continuationPointsSize, - &mut self.0.continuationPoints, - ); + continuation_points + .iter() + .map(ua::ContinuationPoint::to_byte_string) + .collect_ua_array() + .move_into_raw( + &mut self.0.continuationPointsSize, + &mut self.0.continuationPoints, + ); self } diff --git a/src/ua/data_types/content_filter_element.rs b/src/ua/data_types/content_filter_element.rs index bae4d419..dbf5db28 100644 --- a/src/ua/data_types/content_filter_element.rs +++ b/src/ua/data_types/content_filter_element.rs @@ -1,4 +1,4 @@ -use crate::{DataType, FilterOperand, ua}; +use crate::{DataType, FilterOperand, UaIterExt as _, ua}; crate::data_type!(ContentFilterElement); @@ -11,12 +11,11 @@ impl ContentFilterElement { #[must_use] pub fn with_filter_operands(mut self, filter_operands: &[impl FilterOperand]) -> Self { - let array = ua::Array::from_iter( - filter_operands - .iter() - .map(FilterOperand::to_extension_object), - ); - array.move_into_raw(&mut self.0.filterOperandsSize, &mut self.0.filterOperands); + filter_operands + .iter() + .map(FilterOperand::to_extension_object) + .collect_ua_array() + .move_into_raw(&mut self.0.filterOperandsSize, &mut self.0.filterOperands); self } } diff --git a/src/ua/data_types/delete_monitored_items_request.rs b/src/ua/data_types/delete_monitored_items_request.rs index e94c74fc..56988417 100644 --- a/src/ua/data_types/delete_monitored_items_request.rs +++ b/src/ua/data_types/delete_monitored_items_request.rs @@ -1,4 +1,4 @@ -use crate::ua; +use crate::{UaIterExt as _, ua}; crate::data_type!(DeleteMonitoredItemsRequest); @@ -11,15 +11,14 @@ impl DeleteMonitoredItemsRequest { #[must_use] pub fn with_monitored_item_ids(mut self, monitored_item_ids: &[ua::MonitoredItemId]) -> Self { - let array = ua::Array::from_iter( - monitored_item_ids - .iter() - .map(|monitored_item_id| monitored_item_id.as_id().to_uint32()), - ); - array.move_into_raw( - &mut self.0.monitoredItemIdsSize, - &mut self.0.monitoredItemIds, - ); + monitored_item_ids + .iter() + .map(|monitored_item_id| monitored_item_id.as_id().to_uint32()) + .collect_ua_array() + .move_into_raw( + &mut self.0.monitoredItemIdsSize, + &mut self.0.monitoredItemIds, + ); self } } diff --git a/src/ua/data_types/delete_subscriptions_request.rs b/src/ua/data_types/delete_subscriptions_request.rs index 5fa46610..1a7466b6 100644 --- a/src/ua/data_types/delete_subscriptions_request.rs +++ b/src/ua/data_types/delete_subscriptions_request.rs @@ -1,16 +1,15 @@ -use crate::ua; +use crate::{UaIterExt as _, ua}; crate::data_type!(DeleteSubscriptionsRequest); impl DeleteSubscriptionsRequest { #[must_use] pub fn with_subscription_ids(mut self, subscription_ids: &[ua::SubscriptionId]) -> Self { - let array = ua::Array::from_iter( - subscription_ids - .iter() - .map(|subscription_id| subscription_id.as_id().to_uint32()), - ); - array.move_into_raw(&mut self.0.subscriptionIdsSize, &mut self.0.subscriptionIds); + subscription_ids + .iter() + .map(|subscription_id| subscription_id.as_id().to_uint32()) + .collect_ua_array() + .move_into_raw(&mut self.0.subscriptionIdsSize, &mut self.0.subscriptionIds); self } } diff --git a/src/ua/data_types/node_attributes/variable_attributes.rs b/src/ua/data_types/node_attributes/variable_attributes.rs index 392ae1f1..b4abeac9 100644 --- a/src/ua/data_types/node_attributes/variable_attributes.rs +++ b/src/ua/data_types/node_attributes/variable_attributes.rs @@ -1,4 +1,4 @@ -use crate::{DataType as _, ua}; +use crate::{DataType as _, UaIterExt as _, ua}; impl super::VariableAttributes { #[must_use] @@ -17,9 +17,11 @@ impl super::VariableAttributes { #[must_use] pub fn with_array_dimensions(mut self, array_dimensions: &[u32]) -> Self { - let array_dimensions = - ua::Array::from_iter(array_dimensions.iter().copied().map(ua::UInt32::new)); array_dimensions + .iter() + .copied() + .map(ua::UInt32::new) + .collect_ua_array() .move_into_raw(&mut self.0.arrayDimensionsSize, &mut self.0.arrayDimensions); self.0.specifiedAttributes |= ua::SpecifiedAttributes::ARRAYDIMENSIONS.as_u32(); self From 214e71150de18f0e32cef4cc9dbf5eec0e53b400 Mon Sep 17 00:00:00 2001 From: Uwe Klotz Date: Mon, 12 Oct 2026 23:07:48 +0200 Subject: [PATCH 02/13] Remove default implementation --- src/ua/array.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ua/array.rs b/src/ua/array.rs index 9aeab38d..1e81e45c 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -447,7 +447,14 @@ impl Array { /// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized pub(crate) trait UaIterExt where - Self: Iterator + Sized, + T: DataType, +{ + fn collect_ua_array(self) -> Array; +} + +impl UaIterExt for I +where + I: Iterator, T: DataType, { fn collect_ua_array(self) -> Array { @@ -461,13 +468,6 @@ where } } -impl UaIterExt for I -where - I: Iterator, - T: DataType, -{ -} - // SAFETY: Arrays in `open62541` can be sent across thread boundaries: they may be dropped in other // threads as long as their items are `Send`. unsafe impl Send for Array {} From e6c3a29d109d38075f0059d7e14e6067180fe098 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Tue, 13 Oct 2026 11:17:44 -0400 Subject: [PATCH 03/13] fix `UaIterExt` docs --- src/ua/array.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/ua/array.rs b/src/ua/array.rs index 1e81e45c..3ac6d129 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -443,8 +443,15 @@ impl Array { } } -/// Internal-only helper for collection into [`Array`]s. -/// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized +/// Internal-only helper for collection of iterators into [`Array`]s. +/// +/// This takes ownership of the elements from the given iterator. +/// +/// # Panics +/// +/// Enough memory must be available to allocate array. +/// +// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized pub(crate) trait UaIterExt where T: DataType, From 1449dee1e29fbfd9f89bd47504403baf77c759a7 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Tue, 13 Oct 2026 11:18:38 -0400 Subject: [PATCH 04/13] fix import --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 48e1dbad..6a1fc847 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -268,7 +268,7 @@ use self::{ value::{ArrayValue, NonScalarValue}, }; -pub(crate) use ua::UaIterExt; +pub(crate) use self::ua::UaIterExt; #[cfg(feature = "mbedtls")] pub use self::{ From 0309ce9a964d8a33889301db3519b47b5ec0f769 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Wed, 14 Oct 2026 08:26:39 -0400 Subject: [PATCH 05/13] add leak test --- src/ua/array.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/ua/array.rs b/src/ua/array.rs index 3ac6d129..dd1ac386 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -698,4 +698,20 @@ mod tests { .join() .expect("join thread"); } + + // simple probe to verify that the behavior of `Array::move_into_raw` doesn't leak. + // only valuable when run with `-Zsanitizer=address`, so it's being gated behind an explicit cfg + #[expect( + unexpected_cfgs, + reason = "`--cfg asan` passed to run this test when address sanitizer is used" + )] + #[cfg(asan)] + #[test] + fn move_into_raw_leak_probe() { + let array_src = ua::Array::::new(42); + let array_dst = ua::Array::::new(24); + let (mut size, ptr) = unsafe { array_dst.as_raw_parts() }; + // this would fail under ASAN with previous impl, now succeeds + array_src.move_into_raw(&mut size, &mut ptr.cast_mut()); + } } From 92ba74c456352065b2f0eba9d15ff1d4a6d86dec Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Wed, 14 Oct 2026 08:26:46 -0400 Subject: [PATCH 06/13] cleanup --- src/lib.rs | 4 +--- src/ua/array.rs | 14 ++++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6a1fc847..9ab0f464 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,6 +233,7 @@ mod value; #[cfg(feature = "tokio")] pub use self::async_monitored_item::AsyncMonitoredItem; +pub(crate) use self::ua::UaIterExt; pub use self::{ async_client::AsyncClient, async_subscription::{AsyncSubscription, SubscriptionBuilder}, @@ -267,9 +268,6 @@ use self::{ monitored_item::create_monitored_items_callback, value::{ArrayValue, NonScalarValue}, }; - -pub(crate) use self::ua::UaIterExt; - #[cfg(feature = "mbedtls")] pub use self::{ ssl::{Certificate, Password, PrivateKey, create_certificate}, diff --git a/src/ua/array.rs b/src/ua/array.rs index dd1ac386..3186133d 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -443,19 +443,17 @@ impl Array { } } -/// Internal-only helper for collection of iterators into [`Array`]s. -/// -/// This takes ownership of the elements from the given iterator. -/// -/// # Panics -/// -/// Enough memory must be available to allocate array. -/// // Might be good to make this public as an impl of [`FromIterator`] once performance is optimized +/// Internal-only helper for collection of iterators into [`Array`]s. pub(crate) trait UaIterExt where T: DataType, { + /// Takes ownership of the elements from the given iterator. + /// + /// # Panics + /// + /// Enough memory must be available to allocate array. fn collect_ua_array(self) -> Array; } From 5c40de24a0593eecb812b0f8d917bc5b02c9c3b7 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Wed, 14 Oct 2026 08:36:53 -0400 Subject: [PATCH 07/13] register asan cfg flag --- Cargo.toml | 1 + src/ua/array.rs | 4 ---- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 67972cef..a2d0a816 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ unsafe_code = "allow" unsafe_op_in_unsafe_fn = "warn" unused = { level = "warn", priority = -1 } warnings = "warn" +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(asan)'] } [lints.clippy] # We use absolute paths where we require some item only once. Most often, these diff --git a/src/ua/array.rs b/src/ua/array.rs index 3186133d..d2e9d8c9 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -699,10 +699,6 @@ mod tests { // simple probe to verify that the behavior of `Array::move_into_raw` doesn't leak. // only valuable when run with `-Zsanitizer=address`, so it's being gated behind an explicit cfg - #[expect( - unexpected_cfgs, - reason = "`--cfg asan` passed to run this test when address sanitizer is used" - )] #[cfg(asan)] #[test] fn move_into_raw_leak_probe() { From dccbf49a0bfd280859933c2a661c205cc4a82cdb Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Thu, 15 Oct 2026 15:05:22 -0400 Subject: [PATCH 08/13] consume destination raw parts in asan probe --- src/ua/array.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ua/array.rs b/src/ua/array.rs index d2e9d8c9..6d974f7a 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -703,8 +703,7 @@ mod tests { #[test] fn move_into_raw_leak_probe() { let array_src = ua::Array::::new(42); - let array_dst = ua::Array::::new(24); - let (mut size, ptr) = unsafe { array_dst.as_raw_parts() }; + let (mut size, ptr) = unsafe { ua::Array::::new(24).into_raw_parts() }; // this would fail under ASAN with previous impl, now succeeds array_src.move_into_raw(&mut size, &mut ptr.cast_mut()); } From 133f2198ad8b9d9b688d5f1d9a9e64d54370cd1b Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Mon, 19 Oct 2026 15:16:52 -0500 Subject: [PATCH 09/13] add changelog entry --- CHANGELOG.md | 6 ++++++ src/lib.rs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e53f1f65..901f0213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Add back `Server::create_event()` with reordered and additional arguments. This new method replaces both `Server::create_event()` and `Server::trigger_event()` that have been removed. +### Fixed + +- Fix a memory leak triggered when using `with_*` builder methods on data types for array members + (e.g. `ua::RelativePath::with_elements()` would leak the elements that were previously owned by + the instance) + ## [0.12.0] - 2026-07-14 ### Changed diff --git a/src/lib.rs b/src/lib.rs index 9ab0f464..05471de9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,7 @@ mod value; #[cfg(feature = "tokio")] pub use self::async_monitored_item::AsyncMonitoredItem; -pub(crate) use self::ua::UaIterExt; +use self::ua::UaIterExt; pub use self::{ async_client::AsyncClient, async_subscription::{AsyncSubscription, SubscriptionBuilder}, From ffa5febc03c99667f3f9f3a2984edd0c421541db Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Thu, 15 Oct 2026 15:36:30 -0400 Subject: [PATCH 10/13] retake ownership of destination array in asan probe --- src/ua/array.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ua/array.rs b/src/ua/array.rs index 6d974f7a..020da1d2 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -703,8 +703,9 @@ mod tests { #[test] fn move_into_raw_leak_probe() { let array_src = ua::Array::::new(42); - let (mut size, ptr) = unsafe { ua::Array::::new(24).into_raw_parts() }; + let (mut size, mut ptr) = ua::Array::::new(24).into_raw_parts(); // this would fail under ASAN with previous impl, now succeeds - array_src.move_into_raw(&mut size, &mut ptr.cast_mut()); + array_src.move_into_raw(&mut size, &mut ptr); + unsafe { ua::Array::::move_from_raw_parts(&mut size, &mut ptr) }.unwrap(); } } From 70314d00ff11ce4616bc9b6366bc7e0cb0051079 Mon Sep 17 00:00:00 2001 From: Carter Canedy Date: Tue, 20 Oct 2026 07:40:09 -0500 Subject: [PATCH 11/13] coalesce crate re-exports --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 05471de9..2d45e270 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -233,7 +233,6 @@ mod value; #[cfg(feature = "tokio")] pub use self::async_monitored_item::AsyncMonitoredItem; -use self::ua::UaIterExt; pub use self::{ async_client::AsyncClient, async_subscription::{AsyncSubscription, SubscriptionBuilder}, @@ -266,6 +265,7 @@ use self::{ client::ClientContext, data_type::{bitmask_ops, data_type, enum_variants}, monitored_item::create_monitored_items_callback, + ua::UaIterExt, value::{ArrayValue, NonScalarValue}, }; #[cfg(feature = "mbedtls")] From 776bae502867b774e52cfbbaaee652af99bb1525 Mon Sep 17 00:00:00 2001 From: Sebastian Goll <1277035+sgoll@users.noreply.github.com> Date: Thu, 22 Oct 2026 00:39:40 +0200 Subject: [PATCH 12/13] Minor text adjustments Co-authored-by: Sebastian Goll <1277035+sgoll@users.noreply.github.com> --- CHANGELOG.md | 2 +- Cargo.toml | 7 +++++++ src/ua/array.rs | 9 +++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 901f0213..844d0704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Fix a memory leak triggered when using `with_*` builder methods on data types for array members (e.g. `ua::RelativePath::with_elements()` would leak the elements that were previously owned by - the instance) + the instance). ## [0.12.0] - 2026-07-14 diff --git a/Cargo.toml b/Cargo.toml index a2d0a816..5622d0d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,7 +75,14 @@ unsafe_code = "allow" unsafe_op_in_unsafe_fn = "warn" unused = { level = "warn", priority = -1 } warnings = "warn" +trivial_numeric_casts = "warn" unexpected_cfgs = { level = "warn", check-cfg = ['cfg(asan)'] } +unreachable_pub = "warn" +# Writing unsafe code is a necessity for FFI wrappers. +unsafe_code = "allow" +unsafe_op_in_unsafe_fn = "warn" +unused = { level = "warn", priority = -1 } +warnings = "warn" [lints.clippy] # We use absolute paths where we require some item only once. Most often, these diff --git a/src/ua/array.rs b/src/ua/array.rs index 020da1d2..e0e3bc18 100644 --- a/src/ua/array.rs +++ b/src/ua/array.rs @@ -443,8 +443,9 @@ impl Array { } } -// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized /// Internal-only helper for collection of iterators into [`Array`]s. +// +// Might be good to make this public as an impl of [`FromIterator`] once performance is optimized. pub(crate) trait UaIterExt where T: DataType, @@ -697,14 +698,14 @@ mod tests { .expect("join thread"); } - // simple probe to verify that the behavior of `Array::move_into_raw` doesn't leak. - // only valuable when run with `-Zsanitizer=address`, so it's being gated behind an explicit cfg + // Simple probe to verify that the behavior of `Array::move_into_raw()` doesn't leak. + // Only valuable when run with `-Zsanitizer=address`, so it's being gated behind an explicit cfg. #[cfg(asan)] #[test] fn move_into_raw_leak_probe() { let array_src = ua::Array::::new(42); let (mut size, mut ptr) = ua::Array::::new(24).into_raw_parts(); - // this would fail under ASAN with previous impl, now succeeds + // This would fail under ASAN with previous impl, now succeeds. array_src.move_into_raw(&mut size, &mut ptr); unsafe { ua::Array::::move_from_raw_parts(&mut size, &mut ptr) }.unwrap(); } From 91248ad2757fcc4c316f0d552a81e2a0c15ecff2 Mon Sep 17 00:00:00 2001 From: Sebastian Goll Date: Thu, 22 Oct 2026 00:44:14 +0200 Subject: [PATCH 13/13] Fix unintended duplication with GitHub `` ```suggestion `` --- Cargo.toml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5622d0d9..847f13a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,13 +69,6 @@ rust_2024_compatibility = { level = "warn", priority = -1 } tail_expr_drop_order = { level = "allow" } trivial_casts = "warn" trivial_numeric_casts = "warn" -unreachable_pub = "warn" -# Writing unsafe code is a necessity for FFI wrappers. -unsafe_code = "allow" -unsafe_op_in_unsafe_fn = "warn" -unused = { level = "warn", priority = -1 } -warnings = "warn" -trivial_numeric_casts = "warn" unexpected_cfgs = { level = "warn", check-cfg = ['cfg(asan)'] } unreachable_pub = "warn" # Writing unsafe code is a necessity for FFI wrappers.