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]

Reply via email to