This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new cff68da8a feat(rust): support shared reference tracking for arc/rc<dny
T> (#2707)
cff68da8a is described below
commit cff68da8a8932d94e00c079e62a3ae6eeb41b681
Author: Shawn Yang <[email protected]>
AuthorDate: Mon Oct 6 18:51:47 2025 +0800
feat(rust): support shared reference tracking for arc/rc<dny T> (#2707)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
This PR supports shared ref for arc/rc<dny T> by forward
serialization/deserialization to `arc/rc<dny T>` using `upcast/downcast`
## Related issues
Closes #2705
#2691
#2704
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
Delete section if not applicable.
-->
---
rust/fory-core/src/serializer/trait_object.rs | 118 +++++++++++++++++---------
rust/tests/tests/test_rc_arc_trait_object.rs | 64 ++++++++++++++
2 files changed, 140 insertions(+), 42 deletions(-)
diff --git a/rust/fory-core/src/serializer/trait_object.rs
b/rust/fory-core/src/serializer/trait_object.rs
index b94d2f31a..7269c8769 100644
--- a/rust/fory-core/src/serializer/trait_object.rs
+++ b/rust/fory-core/src/serializer/trait_object.rs
@@ -167,9 +167,7 @@ macro_rules! register_trait_type {
// 3. Auto-generate Arc wrapper type and conversions
$crate::generate_smart_pointer_wrapper!(Arc, $trait_name,
$($impl_type),+);
- // 4. Skip registration helper function for wrapper types - wrappers
are not registered
-
- // 5. Serializer implementation for Box<dyn Trait> (existing
functionality)
+ // 4. Serializer implementation for Box<dyn Trait> (existing
functionality)
impl $crate::serializer::Serializer for Box<dyn $trait_name> {
fn fory_write(&self, context: &mut
$crate::resolver::context::WriteContext, is_field: bool) {
let any_ref = <dyn $trait_name as
$crate::serializer::Serializer>::as_any(&**self);
@@ -336,7 +334,16 @@ macro_rules! generate_smart_pointer_wrapper {
}
}
- $crate::impl_smart_pointer_serializer!([<$trait_name Rc>],
std::rc::Rc::new, std::rc::Rc<dyn $trait_name>, $trait_name, $($impl_type),+);
+ $crate::impl_smart_pointer_serializer!(
+ [<$trait_name Rc>],
+ std::rc::Rc<dyn $trait_name>,
+ std::rc::Rc::new,
+ $trait_name,
+ try_write_rc_ref,
+ get_rc_ref,
+ store_rc_ref,
+ $($impl_type),+
+ );
}
};
@@ -399,7 +406,16 @@ macro_rules! generate_smart_pointer_wrapper {
}
}
- $crate::impl_smart_pointer_serializer!([<$trait_name Arc>],
std::sync::Arc::new, std::sync::Arc<dyn $trait_name>, $trait_name,
$($impl_type),+);
+ $crate::impl_smart_pointer_serializer!(
+ [<$trait_name Arc>],
+ std::sync::Arc<dyn $trait_name>,
+ std::sync::Arc::new,
+ $trait_name,
+ try_write_arc_ref,
+ get_arc_ref,
+ store_arc_ref,
+ $($impl_type),+
+ );
}
};
}
@@ -407,17 +423,15 @@ macro_rules! generate_smart_pointer_wrapper {
/// Shared serializer implementation for smart pointer wrappers
#[macro_export]
macro_rules! impl_smart_pointer_serializer {
- ($wrapper_name:ident, $constructor_expr:expr, $pointer_type:ty,
$trait_name:ident, $($impl_type:ty),+) => {
+ ($wrapper_name:ident, $pointer_type:ty, $constructor_expr:expr,
$trait_name:ident, $try_write_ref:ident, $get_ref:ident, $store_ref:ident,
$($impl_type:ty),+) => {
impl $crate::serializer::Serializer for $wrapper_name {
fn fory_write(&self, context: &mut
$crate::resolver::context::WriteContext, is_field: bool) {
- let any_ref = <dyn $trait_name as
$crate::serializer::Serializer>::as_any(&*self.0);
- let concrete_type_id = any_ref.type_id();
-
- if let Some(fory_type_id) =
context.get_fory().get_type_resolver().get_fory_type_id(concrete_type_id) {
-
$crate::serializer::trait_object::write_trait_object_headers(context,
fory_type_id, concrete_type_id);
- $crate::downcast_and_serialize!(any_ref, context,
is_field, $trait_name, $($impl_type),+);
- } else {
- panic!("Type {:?} not registered for {} serialization",
concrete_type_id, stringify!($wrapper_name));
+ if !context.ref_writer.$try_write_ref(context.writer, &self.0)
{
+ let any_obj = <dyn $trait_name as
$crate::serializer::Serializer>::as_any(&*self.0);
+ let concrete_type_id = any_obj.type_id();
+ let harness = context.write_any_typeinfo(concrete_type_id);
+ let serializer_fn = harness.get_serializer_no_ref();
+ serializer_fn(any_obj, context, is_field);
}
}
@@ -427,37 +441,60 @@ macro_rules! impl_smart_pointer_serializer {
}
fn fory_read(context: &mut $crate::resolver::context::ReadContext,
is_field: bool) -> Result<Self, $crate::error::Error> {
- let fory_type_id =
$crate::serializer::trait_object::read_trait_object_headers(context)?;
+ use $crate::types::RefFlag;
+
+ let ref_flag = context.ref_reader.read_ref_flag(&mut
context.reader);
+
+ match ref_flag {
+ RefFlag::Null => Err($crate::error::AnyhowError::msg(
+ format!("{}<dyn {}> cannot be null",
stringify!($pointer_type), stringify!($trait_name))
+ ).into()),
+ RefFlag::Ref => {
+ let ref_id = context.ref_reader.read_ref_id(&mut
context.reader);
+ context.ref_reader.$get_ref::<dyn $trait_name>(ref_id)
+ .map(|ptr| Self::from(ptr))
+ .ok_or_else(|| $crate::error::AnyhowError::msg(
+ format!("{}<dyn {}> reference {} not found",
stringify!($pointer_type), stringify!($trait_name), ref_id)
+ ).into())
+ }
+ RefFlag::NotNullValue => {
+ let harness = context.read_any_typeinfo();
+ let deserializer_fn =
harness.get_deserializer_no_ref();
+ let boxed_any = deserializer_fn(context, is_field)?;
- // Use type resolver to deserialize any registered type
- let type_resolver = context.get_fory().get_type_resolver();
- if let Some(harness) = type_resolver.get_harness(fory_type_id)
{
- let deserializer_fn = harness.get_deserializer();
- if let Ok(any_obj) = deserializer_fn(context, is_field,
true) {
$(
- if any_obj.is::<$impl_type>() {
- match any_obj.downcast::<$impl_type>() {
- Ok(boxed) => {
- let pointer =
$constructor_expr(*boxed) as $pointer_type;
- return Ok(Self::from(pointer));
- }
- Err(recovered) => {
- return
Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
- format!("Failed to downcast type
for trait {}", stringify!($trait_name))
- )));
- }
- }
+ if boxed_any.is::<$impl_type>() {
+ let concrete =
boxed_any.downcast::<$impl_type>()
+ .map_err(|_|
$crate::error::AnyhowError::msg("Downcast failed"))?;
+ let ptr = $constructor_expr(*concrete) as
$pointer_type;
+ return Ok(Self::from(ptr));
}
)*
- return
Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
- format!("Type ID {} is registered but doesn't
implement trait {}", fory_type_id, stringify!($trait_name))
- )));
+
+ Err($crate::error::AnyhowError::msg(
+ format!("Deserialized type does not implement
trait {}", stringify!($trait_name))
+ ).into())
}
- }
+ RefFlag::RefValue => {
+ let harness = context.read_any_typeinfo();
+ let deserializer_fn =
harness.get_deserializer_no_ref();
+ let boxed_any = deserializer_fn(context, is_field)?;
-
Err($crate::error::Error::Other($crate::error::AnyhowError::msg(
- format!("Type ID {} not registered in Fory", fory_type_id)
- )))
+ $(
+ if boxed_any.is::<$impl_type>() {
+ let concrete =
boxed_any.downcast::<$impl_type>()
+ .map_err(|_|
$crate::error::AnyhowError::msg("Downcast failed"))?;
+ let ptr = $constructor_expr(*concrete) as
$pointer_type;
+ context.ref_reader.$store_ref(ptr.clone());
+ return Ok(Self::from(ptr));
+ }
+ )*
+
+ Err($crate::error::AnyhowError::msg(
+ format!("Deserialized type does not implement
trait {}", stringify!($trait_name))
+ ).into())
+ }
+ }
}
fn fory_read_data(context: &mut
$crate::resolver::context::ReadContext, is_field: bool) -> Result<Self,
$crate::error::Error> {
@@ -477,11 +514,9 @@ macro_rules! impl_smart_pointer_serializer {
}
fn fory_write_type_info(_context: &mut
$crate::resolver::context::WriteContext, _is_field: bool) {
- // Wrapper types are polymorphic - type info is written per
element
}
fn fory_read_type_info(_context: &mut
$crate::resolver::context::ReadContext, _is_field: bool) {
- // Wrapper types are polymorphic - type info is read per
element
}
fn fory_is_polymorphic() -> bool {
@@ -504,7 +539,6 @@ macro_rules! impl_smart_pointer_serializer {
<dyn $trait_name as
$crate::serializer::Serializer>::as_any(&*self.0)
}
}
-
};
}
diff --git a/rust/tests/tests/test_rc_arc_trait_object.rs
b/rust/tests/tests/test_rc_arc_trait_object.rs
index 7b0b538e5..42dc90744 100644
--- a/rust/tests/tests/test_rc_arc_trait_object.rs
+++ b/rust/tests/tests/test_rc_arc_trait_object.rs
@@ -401,3 +401,67 @@ fn test_collections_of_wrappers() {
"Meow!"
);
}
+
+#[test]
+fn test_rc_shared_ref_tracking() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(200);
+ fory.register::<Cat>(201);
+
+ let dog = Rc::new(Dog {
+ name: "Rex".to_string(),
+ breed: "Golden Retriever".to_string(),
+ }) as Rc<dyn Animal>;
+
+ let shared_animals: Vec<AnimalRc> = vec![
+ AnimalRc::from(dog.clone()),
+ AnimalRc::from(dog.clone()),
+ AnimalRc::from(dog.clone()),
+ ];
+
+ let serialized = fory.serialize(&shared_animals);
+ let deserialized: Vec<AnimalRc> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+ assert_eq!(deserialized[0].as_ref().name(), "Rex");
+ assert_eq!(deserialized[1].as_ref().name(), "Rex");
+ assert_eq!(deserialized[2].as_ref().name(), "Rex");
+
+ let rc0 = Rc::<dyn Animal>::from(deserialized[0].clone());
+ let rc1 = Rc::<dyn Animal>::from(deserialized[1].clone());
+ let rc2 = Rc::<dyn Animal>::from(deserialized[2].clone());
+ assert!(Rc::ptr_eq(&rc0, &rc1));
+ assert!(Rc::ptr_eq(&rc1, &rc2));
+}
+
+#[test]
+fn test_arc_shared_ref_tracking() {
+ let mut fory = fory_compatible();
+ fory.register::<Dog>(200);
+ fory.register::<Cat>(201);
+
+ let cat = Arc::new(Cat {
+ name: "Whiskers".to_string(),
+ color: "Orange".to_string(),
+ }) as Arc<dyn Animal>;
+
+ let shared_animals: Vec<AnimalArc> = vec![
+ AnimalArc::from(cat.clone()),
+ AnimalArc::from(cat.clone()),
+ AnimalArc::from(cat.clone()),
+ ];
+
+ let serialized = fory.serialize(&shared_animals);
+ let deserialized: Vec<AnimalArc> = fory.deserialize(&serialized).unwrap();
+
+ assert_eq!(deserialized.len(), 3);
+ assert_eq!(deserialized[0].as_ref().name(), "Whiskers");
+ assert_eq!(deserialized[1].as_ref().name(), "Whiskers");
+ assert_eq!(deserialized[2].as_ref().name(), "Whiskers");
+
+ let arc0 = Arc::<dyn Animal>::from(deserialized[0].clone());
+ let arc1 = Arc::<dyn Animal>::from(deserialized[1].clone());
+ let arc2 = Arc::<dyn Animal>::from(deserialized[2].clone());
+ assert!(Arc::ptr_eq(&arc0, &arc1));
+ assert!(Arc::ptr_eq(&arc1, &arc2));
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]