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 724242469 perf(Rust): remove clone()/to_owned() on 
MetaString/MetaStringBytes in MetaStringResolver to improve performance && fix 
xlang test (#2791)
724242469 is described below

commit 7242424697221353ef37eb2e17214500f4a037d9
Author: urlyy <[email protected]>
AuthorDate: Tue Oct 21 10:49:57 2025 +0800

    perf(Rust): remove clone()/to_owned() on MetaString/MetaStringBytes in 
MetaStringResolver to improve performance && fix xlang test (#2791)
    
    ## Why?
    `clone()/to_owned()` on `MetaString/MetaStringBytes` is expensive.
    
    ## What does this PR do?
    1. use `Rc` in  `MetaStringWriterResolver `.
    2. aligned `metastring_resolver` logic with Java
    
    ## Related issues
    close #2762
    
    ---------
    
    Co-authored-by: Shawn Yang <[email protected]>
---
 .../test/java/org/apache/fory/RustXlangTest.java   |  36 +-
 rust/fory-core/src/fory.rs                         |   6 +-
 rust/fory-core/src/meta/meta_string.rs             |  14 +
 rust/fory-core/src/meta/type_meta.rs               |  29 +-
 rust/fory-core/src/resolver/context.rs             |  27 +-
 rust/fory-core/src/resolver/meta_resolver.rs       |  27 +-
 rust/fory-core/src/resolver/metastring_resolver.rs | 412 +++++++++------------
 rust/fory-core/src/resolver/type_resolver.rs       |  40 +-
 rust/fory-core/src/serializer/enum_.rs             |  12 +-
 rust/fory-core/src/serializer/skip.rs              |   2 +-
 rust/fory-core/src/serializer/struct_.rs           |  12 +-
 rust/tests/tests/test_cross_language.rs            |  81 ++--
 rust/tests/tests/test_metastring_resolver.rs       | 122 ++++++
 13 files changed, 415 insertions(+), 405 deletions(-)

diff --git a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java 
b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
index eecd52eb0..6fa5310b1 100644
--- a/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/RustXlangTest.java
@@ -395,15 +395,6 @@ public class RustXlangTest extends ForyTestBase {
     fory.serialize(buffer, strSet);
     fory.serialize(buffer, strMap);
     fory.serialize(buffer, color);
-    //    Map<Object, Object> map = new HashMap<>();
-    //    for (int i = 0; i < list.size(); i++) {
-    //        map.put("k" + i, list.get(i));
-    //        map.put(list.get(i), list.get(i));
-    //    }
-    //    fory.serialize(buffer, map);
-
-    //    Set<Object> set = new HashSet<>(list);
-    //    fory.serialize(buffer, set);
 
     BiConsumer<MemoryBuffer, Boolean> function =
         (MemoryBuffer buf, Boolean useToString) -> {
@@ -433,9 +424,6 @@ public class RustXlangTest extends ForyTestBase {
           assertStringEquals(fory.deserialize(buf), strSet, useToString);
           assertStringEquals(fory.deserialize(buf), strMap, useToString);
           assertStringEquals(fory.deserialize(buf), color, useToString);
-          //            assertStringEquals(fory.deserialize(buf), list, 
useToString);
-          //            assertStringEquals(fory.deserialize(buf), map, 
useToString);
-          //            assertStringEquals(fory.deserialize(buf), set, 
useToString);
         };
     function.accept(buffer, false);
     Path dataFile = Files.createTempFile("test_cross_language_serializer", 
"data");
@@ -817,25 +805,25 @@ public class RustXlangTest extends ForyTestBase {
     MyStruct myStruct = new MyStruct(42);
     MyExt myExt = new MyExt(43);
     MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(32);
-    fory.serialize(buffer, Color.White);
-    fory.serialize(buffer, Color.White);
-    fory.serialize(buffer, Color.White);
+    for (int i = 0; i < 3; i++) {
+      fory.serialize(buffer, Color.White);
+    }
     // todo: checkVersion
     //        fory.serialize(buffer, myStruct);
-    fory.serialize(buffer, myExt);
-    fory.serialize(buffer, myExt);
-    fory.serialize(buffer, myExt);
+    for (int i = 0; i < 3; i++) {
+      fory.serialize(buffer, myExt);
+    }
     byte[] bytes = buffer.getBytes(0, buffer.writerIndex());
     Path dataFile = Files.createTempFile("test_consistent_named", "data");
     Pair<Map<String, String>, File> env_workdir = setFilePath(language, 
command, dataFile, bytes);
     Assert.assertTrue(executeCommand(command, 30, env_workdir.getLeft(), 
env_workdir.getRight()));
     MemoryBuffer buffer2 = MemoryUtils.wrap(Files.readAllBytes(dataFile));
-    Assert.assertEquals(fory.deserialize(buffer2), Color.White);
-    Assert.assertEquals(fory.deserialize(buffer2), Color.White);
-    Assert.assertEquals(fory.deserialize(buffer2), Color.White);
-    Assert.assertEquals(fory.deserialize(buffer2), myExt);
-    Assert.assertEquals(fory.deserialize(buffer2), myExt);
-    Assert.assertEquals(fory.deserialize(buffer2), myExt);
+    for (int i = 0; i < 3; i++) {
+      Assert.assertEquals(fory.deserialize(buffer2), Color.White);
+    }
+    for (int i = 0; i < 3; i++) {
+      Assert.assertEquals(fory.deserialize(buffer2), myExt);
+    }
   }
 
   /**
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index ca3f50096..ec34cbd57 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -403,7 +403,7 @@ impl Fory {
         if result.is_ok() {
             assert_eq!(context.reader.slice_after_cursor().len(), 0);
         }
-        context.reset();
+        context.reader.reset();
         pool.put(context);
         result
     }
@@ -428,6 +428,7 @@ impl Fory {
             context.reader.skip(bytes_to_skip)?;
         }
         context.ref_reader.resolve_callbacks();
+        context.reset();
         result
     }
 
@@ -481,7 +482,7 @@ impl Fory {
         });
         let mut context = pool.get();
         let result = self.serialize_with_context(record, &mut context)?;
-        context.reset();
+        context.writer.reset();
         pool.put(context);
         Ok(result)
     }
@@ -503,6 +504,7 @@ impl Fory {
                 context.write_meta(meta_start_offset);
             }
         }
+        context.reset();
         Ok(context.writer.dump())
     }
 
diff --git a/rust/fory-core/src/meta/meta_string.rs 
b/rust/fory-core/src/meta/meta_string.rs
index 9391b94e4..23b42f5c8 100644
--- a/rust/fory-core/src/meta/meta_string.rs
+++ b/rust/fory-core/src/meta/meta_string.rs
@@ -18,6 +18,7 @@
 use crate::ensure;
 use crate::error::Error;
 use crate::meta::string_util;
+use std::sync::OnceLock;
 
 // equal to "std::i16::MAX"
 const SHORT_MAX_VALUE: usize = 32767;
@@ -66,6 +67,8 @@ impl std::hash::Hash for MetaString {
     }
 }
 
+static EMPTY: OnceLock<MetaString> = OnceLock::new();
+
 impl MetaString {
     pub fn new(
         original: String,
@@ -95,6 +98,17 @@ impl MetaString {
         writer.write_varuint32(self.bytes.len() as u32);
         writer.write_bytes(&self.bytes);
     }
+
+    pub fn get_empty() -> &'static MetaString {
+        EMPTY.get_or_init(|| MetaString {
+            original: "".to_string(),
+            encoding: Encoding::default(),
+            bytes: vec![],
+            strip_last_char: false,
+            special_char1: '\0',
+            special_char2: '\0',
+        })
+    }
 }
 
 #[derive(Clone)]
diff --git a/rust/fory-core/src/meta/type_meta.rs 
b/rust/fory-core/src/meta/type_meta.rs
index a52f94d4a..d7fb33864 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -26,6 +26,7 @@ use crate::types::{TypeId, PRIMITIVE_TYPES};
 use std::clone::Clone;
 use std::cmp::min;
 use std::collections::HashMap;
+use std::rc::Rc;
 
 const SMALL_NUM_FIELDS_THRESHOLD: usize = 0b11111;
 const REGISTER_BY_NAME_FLAG: u8 = 0b100000;
@@ -236,8 +237,8 @@ impl PartialEq for FieldType {
 #[derive(Debug)]
 pub struct TypeMetaLayer {
     type_id: u32,
-    namespace: MetaString,
-    type_name: MetaString,
+    namespace: Rc<MetaString>,
+    type_name: Rc<MetaString>,
     register_by_name: bool,
     field_infos: Vec<FieldInfo>,
 }
@@ -252,8 +253,8 @@ impl TypeMetaLayer {
     ) -> TypeMetaLayer {
         TypeMetaLayer {
             type_id,
-            namespace,
-            type_name,
+            namespace: Rc::from(namespace),
+            type_name: Rc::from(type_name),
             register_by_name,
             field_infos,
         }
@@ -262,8 +263,8 @@ impl TypeMetaLayer {
     pub fn empty() -> TypeMetaLayer {
         TypeMetaLayer {
             type_id: 0,
-            namespace: MetaString::default(),
-            type_name: MetaString::default(),
+            namespace: Rc::from(MetaString::get_empty().clone()),
+            type_name: Rc::from(MetaString::get_empty().clone()),
             register_by_name: false,
             field_infos: vec![],
         }
@@ -273,12 +274,12 @@ impl TypeMetaLayer {
         self.type_id
     }
 
-    pub fn get_type_name(&self) -> &MetaString {
-        &self.type_name
+    pub fn get_type_name(&self) -> Rc<MetaString> {
+        self.type_name.clone()
     }
 
-    pub fn get_namespace(&self) -> &MetaString {
-        &self.namespace
+    pub fn get_namespace(&self) -> Rc<MetaString> {
+        self.namespace.clone()
     }
 
     pub fn get_field_infos(&self) -> &Vec<FieldInfo> {
@@ -556,12 +557,12 @@ impl TypeMeta {
         self.hash
     }
 
-    pub fn get_type_name(&self) -> MetaString {
-        self.layer.get_type_name().clone()
+    pub fn get_type_name(&self) -> Rc<MetaString> {
+        self.layer.get_type_name()
     }
 
-    pub fn get_namespace(&self) -> MetaString {
-        self.layer.get_namespace().clone()
+    pub fn get_namespace(&self) -> Rc<MetaString> {
+        self.layer.get_namespace()
     }
 
     pub fn empty() -> TypeMeta {
diff --git a/rust/fory-core/src/resolver/context.rs 
b/rust/fory-core/src/resolver/context.rs
index a347d54a9..f3f4025ad 100644
--- a/rust/fory-core/src/resolver/context.rs
+++ b/rust/fory-core/src/resolver/context.rs
@@ -21,9 +21,7 @@ use crate::error::Error;
 use crate::fory::Fory;
 use crate::meta::MetaString;
 use crate::resolver::meta_resolver::{MetaReaderResolver, MetaWriterResolver};
-use crate::resolver::metastring_resolver::{
-    MetaStringBytes, MetaStringReaderResolver, MetaStringWriterResolver,
-};
+use crate::resolver::metastring_resolver::{MetaStringReaderResolver, 
MetaStringWriterResolver};
 use crate::resolver::ref_resolver::{RefReader, RefWriter};
 use crate::resolver::type_resolver::{TypeInfo, TypeResolver};
 use crate::types;
@@ -170,8 +168,8 @@ impl WriteContext {
                         as u32;
                     self.writer.write_varuint32(meta_index);
                 } else {
-                    namespace.write_to(&mut self.writer);
-                    type_name.write_to(&mut self.writer);
+                    self.write_meta_string_bytes(namespace)?;
+                    self.write_meta_string_bytes(type_name)?;
                 }
             }
             _ => {
@@ -182,7 +180,7 @@ impl WriteContext {
     }
 
     #[inline(always)]
-    pub fn write_meta_string_bytes(&mut self, ms: &MetaString) -> Result<(), 
Error> {
+    pub fn write_meta_string_bytes(&mut self, ms: Rc<MetaString>) -> 
Result<(), Error> {
         self.meta_string_resolver
             .write_meta_string_bytes(&mut self.writer, ms)
     }
@@ -190,8 +188,8 @@ impl WriteContext {
     #[inline(always)]
     pub fn reset(&mut self) {
         self.meta_resolver.reset();
+        self.meta_string_resolver.reset();
         self.ref_writer.reset();
-        self.writer.reset();
     }
 }
 
@@ -332,10 +330,12 @@ impl ReadContext {
                     let type_info = 
self.get_type_info_by_index(meta_index)?.clone();
                     Ok(type_info)
                 } else {
-                    let namespace = self.meta_resolver.read_metastring(&mut 
self.reader)?;
-                    let type_name = self.meta_resolver.read_metastring(&mut 
self.reader)?;
+                    let namespace = self.read_meta_string()?.to_owned();
+                    let type_name = self.read_meta_string()?.to_owned();
+                    let rc_namespace = Rc::from(namespace);
+                    let rc_type_name = Rc::from(type_name);
                     self.type_resolver
-                        .get_type_info_by_msname(&namespace, &type_name)
+                        .get_type_info_by_msname(rc_namespace, rc_type_name)
                         .ok_or_else(|| Error::type_error("Name harness not 
found"))
                 }
             }
@@ -351,9 +351,8 @@ impl ReadContext {
         self.type_resolver.get_type_info(type_id)
     }
 
-    pub fn read_meta_string_bytes(&mut self) -> Result<MetaStringBytes, Error> 
{
-        self.meta_string_resolver
-            .read_meta_string_bytes(&mut self.reader)
+    pub fn read_meta_string(&mut self) -> Result<&MetaString, Error> {
+        self.meta_string_resolver.read_meta_string(&mut self.reader)
     }
 
     #[inline(always)]
@@ -378,8 +377,8 @@ impl ReadContext {
 
     #[inline(always)]
     pub fn reset(&mut self) {
-        self.reader.reset();
         self.meta_resolver.reset();
+        self.meta_string_resolver.reset();
         self.ref_reader.reset();
     }
 }
diff --git a/rust/fory-core/src/resolver/meta_resolver.rs 
b/rust/fory-core/src/resolver/meta_resolver.rs
index dbd4db05c..9504c6388 100644
--- a/rust/fory-core/src/resolver/meta_resolver.rs
+++ b/rust/fory-core/src/resolver/meta_resolver.rs
@@ -17,7 +17,7 @@
 
 use crate::buffer::{Reader, Writer};
 use crate::error::Error;
-use crate::meta::{Encoding, MetaString, TypeMeta, NAMESPACE_DECODER};
+use crate::meta::TypeMeta;
 use crate::resolver::type_resolver::TypeInfo;
 use crate::TypeResolver;
 use std::collections::HashMap;
@@ -140,31 +140,6 @@ impl MetaReaderResolver {
         Ok(reader.get_cursor())
     }
 
-    pub fn read_metastring(&self, reader: &mut Reader) -> Result<MetaString, 
Error> {
-        let len = reader.read_varuint32()? as usize;
-        if len == 0 {
-            return Ok(MetaString {
-                bytes: vec![],
-                encoding: Encoding::Utf8,
-                original: String::new(),
-                strip_last_char: false,
-                special_char1: '\0',
-                special_char2: '\0',
-            });
-        }
-        let bytes = reader.read_bytes(len)?;
-        let encoding_byte = bytes[0] & 0x07;
-        let encoding = match encoding_byte {
-            0x00 => Encoding::Utf8,
-            0x01 => Encoding::LowerSpecial,
-            0x02 => Encoding::LowerUpperDigitSpecial,
-            0x03 => Encoding::FirstToLowerSpecial,
-            0x04 => Encoding::AllToLowerSpecial,
-            _ => Encoding::Utf8,
-        };
-        NAMESPACE_DECODER.decode(bytes, encoding)
-    }
-
     pub fn reset(&mut self) {
         self.reading_type_infos.clear();
     }
diff --git a/rust/fory-core/src/resolver/metastring_resolver.rs 
b/rust/fory-core/src/resolver/metastring_resolver.rs
index 975d27e67..64ca15ad2 100644
--- a/rust/fory-core/src/resolver/metastring_resolver.rs
+++ b/rust/fory-core/src/resolver/metastring_resolver.rs
@@ -15,14 +15,17 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use std::collections::hash_map::Entry;
 use std::collections::HashMap;
 use std::convert::TryInto;
+use std::rc::Rc;
+use std::sync::OnceLock;
 
 use crate::buffer::Writer;
 use crate::error::Error;
-use crate::meta::murmurhash3_x64_128;
+use crate::meta::{murmurhash3_x64_128, NAMESPACE_DECODER};
 use crate::meta::{Encoding, MetaString};
-use crate::Reader;
+use crate::{ensure, Reader};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct MetaStringBytes {
@@ -31,7 +34,6 @@ pub struct MetaStringBytes {
     pub encoding: Encoding,
     pub first8: u64,
     pub second8: u64,
-    pub dynamic_write_id: i16,
 }
 
 const HEADER_MASK: i64 = 0xff;
@@ -47,240 +49,181 @@ fn byte_to_encoding(byte: u8) -> Encoding {
     }
 }
 
+static EMPTY: OnceLock<MetaStringBytes> = OnceLock::new();
+
 impl MetaStringBytes {
     pub const DEFAULT_DYNAMIC_WRITE_STRING_ID: i16 = -1;
-    pub const EMPTY: MetaStringBytes = MetaStringBytes {
-        bytes: Vec::new(),
-        hash_code: 0,
-        encoding: Encoding::Utf8,
-        first8: 0,
-        second8: 0,
-        dynamic_write_id: MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID,
-    };
-
-    pub fn new(
-        bytes: Vec<u8>,
-        hash_code: i64,
-        encoding: Encoding,
-        first8: u64,
-        second8: u64,
-    ) -> Self {
+
+    pub fn new(bytes: Vec<u8>, hash_code: i64) -> Self {
+        let header = (hash_code & HEADER_MASK) as u8;
+        let encoding = byte_to_encoding(header);
+        let mut data = bytes.clone();
+        if bytes.len() < 16 {
+            data.resize(16, 0);
+        }
+        let first8 = u64::from_le_bytes(data[0..8].try_into().unwrap());
+        let second8 = u64::from_le_bytes(data[8..16].try_into().unwrap());
         MetaStringBytes {
             bytes,
             hash_code,
             encoding,
             first8,
             second8,
-            dynamic_write_id: MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID,
         }
     }
 
-    pub fn decode_lossy(&self) -> String {
-        String::from_utf8_lossy(&self.bytes).into_owned()
+    pub fn to_metastring(&self) -> Result<MetaString, Error> {
+        let ms = NAMESPACE_DECODER.decode(&self.bytes, self.encoding)?;
+        Ok(ms)
     }
 
     pub(crate) fn from_metastring(meta_string: &MetaString) -> Result<Self, 
Error> {
-        let mut bytes = meta_string.bytes.to_vec();
-        let len = bytes.len();
-        // follow python/java implementation: small strings use quick v1/v2 
based hash,
-        // large strings use murmurhash, then mask lower 8 bits for encoding
-        let encoding_val = meta_string.encoding as i64 & HEADER_MASK;
-        let hash_code: i64;
-        if len <= MetaStringReaderResolver::SMALL_STRING_THRESHOLD {
-            // compute v1 and v2 from bytes little-endian
-            let mut v1: u64 = 0;
-            let mut v2: u64 = 0;
-            for (i, b) in bytes.iter().take(8).enumerate() {
-                v1 |= (*b as u64) << (8 * i);
-            }
-            if bytes.len() > 8 {
-                for j in 0..usize::min(8, bytes.len() - 8) {
-                    v2 |= (bytes[8 + j] as u64) << (8 * j);
-                }
-            }
-            // ((v1 * 31 + v2) >> 8 << 8) | encoding
-            let tmp: u128 = (v1 as u128).wrapping_mul(31u128).wrapping_add(v2 
as u128);
-            let masked = ((tmp >> 8) << 8) as u64;
-            hash_code = masked as i64 | encoding_val;
-        } else {
-            let mut hc = murmurhash3_x64_128(&bytes, 47).0 as i64;
-            hc = hc.abs();
-            if hc == 0 {
-                hc += 256;
-            }
-            hc = (hc as u64 & 0xffffffffffffff00) as i64;
-            hash_code = hc | encoding_val;
-        }
-
-        // ensure we have 16 bytes to extract first8/second8
-        if bytes.len() < 16 {
-            bytes.resize(16, 0);
+        let bytes = meta_string.bytes.to_vec();
+        let mut hash_code = murmurhash3_x64_128(&bytes, 47).0 as i64;
+        hash_code = hash_code.abs();
+        if hash_code == 0 {
+            hash_code += 256;
         }
+        hash_code = (hash_code as u64 & 0xffffffffffffff00) as i64;
+        let encoding = meta_string.encoding;
+        let header = encoding as i64 & HEADER_MASK;
+        hash_code |= header;
+        Ok(Self::new(bytes, hash_code))
+    }
 
-        let first8: [u8; 8] = bytes[0..8].try_into().map_err(|_| {
-            Error::invalid_data(format!("expected at least 8 bytes, got {}", 
bytes.len()))
-        })?;
-        let first8 = u64::from_le_bytes(first8);
-
-        let second8: [u8; 8] = bytes[8..16].try_into().map_err(|_| {
-            Error::invalid_data(format!("expected at least 16 bytes, got {}", 
bytes.len()))
-        })?;
-        let second8 = u64::from_le_bytes(second8);
-
-        Ok(Self::new(
-            bytes,
-            hash_code,
-            byte_to_encoding((hash_code & HEADER_MASK) as u8),
-            first8,
-            second8,
-        ))
+    pub fn get_empty() -> &'static MetaStringBytes {
+        EMPTY.get_or_init(|| 
MetaStringBytes::from_metastring(MetaString::get_empty()).unwrap())
     }
 }
 
-#[derive(Default)]
 pub struct MetaStringWriterResolver {
-    meta_string_to_bytes: HashMap<MetaString, MetaStringBytes>,
-    dynamic_written: Vec<Option<MetaStringBytes>>,
+    meta_string_to_bytes: HashMap<Rc<MetaString>, MetaStringBytes>,
+    dynamic_written: Vec<*const MetaStringBytes>,
     dynamic_write_id: usize,
+    bytes_id_map: HashMap<*const MetaStringBytes, i16>,
 }
 
-impl MetaStringWriterResolver {
-    const INITIAL_CAPACITY: usize = 8;
-    const SMALL_STRING_THRESHOLD: usize = 16;
-
-    pub fn new() -> Self {
+impl Default for MetaStringWriterResolver {
+    fn default() -> Self {
         Self {
             meta_string_to_bytes: 
HashMap::with_capacity(Self::INITIAL_CAPACITY),
-            dynamic_written: vec![None; 32],
+            dynamic_written: vec![std::ptr::null(); 32],
             dynamic_write_id: 0,
+            bytes_id_map: HashMap::with_capacity(Self::INITIAL_CAPACITY),
         }
     }
+}
 
-    pub fn get_or_create_meta_string_bytes(&mut self, m: &MetaString) -> 
MetaStringBytes {
-        if let Some(b) = self.meta_string_to_bytes.get(m) {
-            return b.clone();
-        }
-        // create via from_metastring to keep same hash/encoding rules
-        let msb = 
MetaStringBytes::from_metastring(m).expect("from_metastring");
-        self.meta_string_to_bytes.insert(m.clone(), msb.clone());
-        msb
-    }
-
-    pub fn write_meta_string_bytes_with_flag(&mut self, w: &mut Writer, mut 
mb: MetaStringBytes) {
-        let id = mb.dynamic_write_id;
-        if id == MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID {
-            let id_usize = self.dynamic_write_id;
-            self.dynamic_write_id += 1;
-            mb.dynamic_write_id = id_usize as i16;
-            if id_usize >= self.dynamic_written.len() {
-                self.dynamic_written.resize(id_usize * 2, None);
-            }
-            self.dynamic_written[id_usize] = Some(mb.clone());
-
-            let len = mb.bytes.len();
-            let header = ((len as u32) << 2) | 0b1;
-            w.write_varuint32(header);
-            if len > Self::SMALL_STRING_THRESHOLD {
-                w.write_i64(mb.hash_code);
-            } else {
-                w.write_u8(mb.encoding as i16 as u8);
-            }
-            w.write_bytes(&mb.bytes);
-        } else {
-            let header = ((id as u32 + 1) << 2) | 0b11;
-            w.write_varuint32(header);
-        }
-    }
+impl MetaStringWriterResolver {
+    const INITIAL_CAPACITY: usize = 8;
+    const SMALL_STRING_THRESHOLD: usize = 16;
 
     pub fn write_meta_string_bytes(
         &mut self,
         writer: &mut Writer,
-        ms: &MetaString,
+        ms: Rc<MetaString>,
     ) -> Result<(), Error> {
-        let mut mb = MetaStringBytes::from_metastring(ms)?;
-        let id = mb.dynamic_write_id;
-        if id == MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID {
-            let id_usize = self.dynamic_write_id;
-            self.dynamic_write_id += 1;
-            mb.dynamic_write_id = id_usize as i16;
-            if id_usize >= self.dynamic_written.len() {
-                self.dynamic_written.resize(id_usize * 2 + 1, None);
+        // get_or_create_meta_string_bytes
+        let mb_ref = {
+            let entry = self.meta_string_to_bytes.entry(ms.clone());
+            match entry {
+                Entry::Occupied(o) => o.into_mut(),
+                Entry::Vacant(v) => 
v.insert(MetaStringBytes::from_metastring(&ms)?),
             }
-            self.dynamic_written[id_usize] = Some(mb.clone());
+        };
 
-            let len = mb.bytes.len();
-            writer.write_varuint32((len as u32) << 1);
-            if len > Self::SMALL_STRING_THRESHOLD {
-                writer.write_i64(mb.hash_code);
-            } else {
-                writer.write_u8(mb.encoding as i16 as u8);
+        let mb_ptr: *const MetaStringBytes = mb_ref as *const _;
+        let id = if let Some(exist_id) = self.bytes_id_map.get_mut(&mb_ptr) {
+            if *exist_id != MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID {
+                writer.write_varuint32(((*exist_id as u32 + 1) << 1) | 1);
+                return Ok(());
             }
-            writer.write_bytes(&mb.bytes);
+            let id = self.dynamic_write_id;
+            *exist_id = id as i16;
+            id
         } else {
-            let header = ((id as u32 + 1) << 1) | 1;
-            writer.write_varuint32(header);
+            let id = self.dynamic_write_id;
+            self.bytes_id_map.insert(mb_ptr, id as i16);
+            id
+        };
+        // // update dynamic_write
+        self.dynamic_write_id += 1;
+        if id >= self.dynamic_written.len() {
+            self.dynamic_written.resize(id * 2, std::ptr::null());
         }
+        self.dynamic_written[id] = mb_ptr;
+
+        let len = mb_ref.bytes.len();
+        writer.write_varuint32((len as u32) << 1);
+        if len > Self::SMALL_STRING_THRESHOLD {
+            writer.write_i64(mb_ref.hash_code);
+        } else {
+            writer.write_u8(mb_ref.encoding as i16 as u8);
+        }
+        writer.write_bytes(&mb_ref.bytes);
         Ok(())
     }
 
-    pub fn reset_write(&mut self) {
+    pub fn reset(&mut self) {
         if self.dynamic_write_id != 0 {
             for i in 0..self.dynamic_write_id {
-                if let Some(ref mut mb) = self.dynamic_written[i] {
-                    mb.dynamic_write_id = 
MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID;
+                let key = self.dynamic_written[i];
+                if !key.is_null() {
+                    if let Some(v) = self.bytes_id_map.get_mut(&key) {
+                        *v = MetaStringBytes::DEFAULT_DYNAMIC_WRITE_STRING_ID;
+                    }
+                    self.dynamic_written[i] = std::ptr::null();
                 }
-                self.dynamic_written[i] = None;
             }
             self.dynamic_write_id = 0;
         }
     }
 }
 
-#[derive(Default)]
 pub struct MetaStringReaderResolver {
-    meta_string_bytes_to_string: HashMap<MetaStringBytes, String>,
-    hash_to_meta: HashMap<i64, MetaStringBytes>,
-    small_map: HashMap<(u64, u64, u8), MetaStringBytes>,
-    dynamic_read: Vec<Option<MetaStringBytes>>,
+    meta_string_bytes_to_string: HashMap<*const MetaStringBytes, MetaString>,
+    hash_to_meta_string_bytes: HashMap<i64, MetaStringBytes>,
+    long_long_byte_map: HashMap<(u64, u64, u8), MetaStringBytes>,
+    dynamic_read: Vec<Option<*const MetaStringBytes>>,
     dynamic_read_id: usize,
 }
 
-impl MetaStringReaderResolver {
-    const INITIAL_CAPACITY: usize = 8;
-    const SMALL_STRING_THRESHOLD: usize = 16;
-
-    pub fn new() -> Self {
+impl Default for MetaStringReaderResolver {
+    fn default() -> Self {
         Self {
             meta_string_bytes_to_string: 
HashMap::with_capacity(Self::INITIAL_CAPACITY),
-            hash_to_meta: HashMap::with_capacity(Self::INITIAL_CAPACITY),
-            small_map: HashMap::with_capacity(Self::INITIAL_CAPACITY),
+            hash_to_meta_string_bytes: 
HashMap::with_capacity(Self::INITIAL_CAPACITY),
+            long_long_byte_map: HashMap::with_capacity(Self::INITIAL_CAPACITY),
             dynamic_read: vec![None; 32],
             dynamic_read_id: 0,
         }
     }
+}
+
+impl MetaStringReaderResolver {
+    const INITIAL_CAPACITY: usize = 8;
+    const SMALL_STRING_THRESHOLD: usize = 16;
 
     pub fn read_meta_string_bytes_with_flag(
         &mut self,
         reader: &mut Reader,
         header: u32,
-    ) -> Result<MetaStringBytes, Error> {
+    ) -> Result<&MetaStringBytes, Error> {
         let len = (header >> 2) as usize;
+
         if (header & 0b10) == 0 {
             if len <= Self::SMALL_STRING_THRESHOLD {
-                let mb = self.read_small_meta_string_bytes(reader, len)?;
-                self.update_dynamic_string(mb.clone());
-                Ok(mb)
+                self.read_small_meta_string_bytes_and_update(reader, len)
             } else {
                 let hash_code = reader.read_i64()?;
-                let mb = self.read_big_meta_string_bytes(reader, len, 
hash_code)?;
-                self.update_dynamic_string(mb.clone());
-                Ok(mb)
+                self.read_big_meta_string_bytes_and_update(reader, len, 
hash_code)
             }
         } else {
             let idx = len - 1;
             self.dynamic_read
                 .get(idx)
-                .and_then(|opt| opt.clone())
+                .and_then(|opt| opt.as_ref())
+                .map(|ptr| unsafe { &**ptr })
                 .ok_or_else(|| Error::invalid_data("dynamic id not found"))
         }
     }
@@ -288,66 +231,72 @@ impl MetaStringReaderResolver {
     pub fn read_meta_string_bytes(
         &mut self,
         reader: &mut Reader,
-    ) -> Result<MetaStringBytes, Error> {
+    ) -> Result<&MetaStringBytes, Error> {
         let header = reader.read_varuint32()?;
         let len = (header >> 1) as usize;
+
         if (header & 0b1) == 0 {
             if len > Self::SMALL_STRING_THRESHOLD {
                 let hash_code = reader.read_i64()?;
-                let mb = self.read_big_meta_string_bytes(reader, len, 
hash_code)?;
-                self.update_dynamic_string(mb.clone());
-                Ok(mb)
+                self.read_big_meta_string_bytes_and_update(reader, len, 
hash_code)
             } else {
-                let mb = self.read_small_meta_string_bytes(reader, len)?;
-                self.update_dynamic_string(mb.clone());
-                Ok(mb)
+                self.read_small_meta_string_bytes_and_update(reader, len)
             }
         } else {
             let idx = len - 1;
             self.dynamic_read
                 .get(idx)
-                .and_then(|opt| opt.clone())
+                .and_then(|opt| opt.as_ref())
+                .map(|ptr| unsafe { &**ptr })
                 .ok_or_else(|| Error::invalid_data("dynamic id not found"))
         }
     }
 
-    fn read_big_meta_string_bytes(
+    fn read_big_meta_string_bytes_and_update(
         &mut self,
         reader: &mut Reader,
         len: usize,
         hash_code: i64,
-    ) -> Result<MetaStringBytes, Error> {
-        if let Some(existing) = self.hash_to_meta.get(&hash_code) {
-            reader.skip(len)?;
-            Ok(existing.clone())
-        } else {
-            let bytes = reader.read_bytes(len)?.to_vec();
-            let mut first8 = 0;
-            let mut second8 = 0;
-            for (i, b) in bytes.iter().enumerate().take(8) {
-                first8 |= (*b as u64) << (8 * i);
+    ) -> Result<&MetaStringBytes, Error> {
+        let mb_ref: &mut MetaStringBytes = match 
self.hash_to_meta_string_bytes.entry(hash_code) {
+            Entry::Occupied(entry) => {
+                reader.skip(len)?;
+                entry.into_mut()
             }
-            if bytes.len() > 8 {
-                for j in 0..usize::min(8, bytes.len() - 8) {
-                    second8 |= (bytes[8 + j] as u64) << (8 * j);
-                }
+            Entry::Vacant(entry) => {
+                let bytes = reader.read_bytes(len)?.to_vec();
+                let mb = MetaStringBytes::new(bytes, hash_code);
+                entry.insert(mb)
             }
-            let mb = MetaStringBytes::new(bytes, hash_code, Encoding::Utf8, 
first8, second8);
-            self.hash_to_meta.insert(hash_code, mb.clone());
-            Ok(mb)
+        };
+
+        // update dynamic_read
+        let id = self.dynamic_read_id;
+        self.dynamic_read_id += 1;
+        if id >= self.dynamic_read.len() {
+            self.dynamic_read.resize(id * 2 + 1, None);
         }
+        let ptr = mb_ref as *const MetaStringBytes;
+        self.dynamic_read[id] = Some(ptr);
+        Ok(mb_ref)
     }
 
-    fn read_small_meta_string_bytes(
+    fn read_small_meta_string_bytes_and_update(
         &mut self,
         reader: &mut Reader,
         len: usize,
-    ) -> Result<MetaStringBytes, Error> {
+    ) -> Result<&MetaStringBytes, Error> {
         let encoding_val = reader.read_u8()?;
         if len == 0 {
-            debug_assert_eq!(encoding_val, Encoding::Utf8 as i16 as u8);
-            return Ok(MetaStringBytes::EMPTY.clone());
+            ensure!(
+                encoding_val == Encoding::Utf8 as u8,
+                Error::EncodingError(format!("wrong encoding value: {}", 
encoding_val).into())
+            );
+            let empty = MetaStringBytes::get_empty();
+            // empty must be a static or globally unique instance
+            return Ok(empty);
         }
+
         let (v1, v2) = if len <= 8 {
             let v1 = Self::read_bytes_as_u64(reader, len)?;
             (v1, 0)
@@ -357,32 +306,31 @@ impl MetaStringReaderResolver {
             (v1, v2)
         };
         let key = (v1, v2, encoding_val);
-        if let Some(existing) = self.small_map.get(&key) {
-            Ok(existing.clone())
-        } else {
-            let mut data = Vec::with_capacity(len);
-            for i in 0..usize::min(8, len) {
-                data.push(((v1 >> (8 * i)) & 0xFF) as u8);
-            }
-            if len > 8 {
-                for j in 0..(len - 8) {
-                    data.push(((v2 >> (8 * j)) & 0xFF) as u8);
-                }
+
+        let mb_ref = match self.long_long_byte_map.entry(key) {
+            Entry::Occupied(entry) => entry.into_mut(),
+            Entry::Vacant(entry) => {
+                let mut data = vec![0u8; 16];
+                data[0..8].copy_from_slice(&v1.to_le_bytes());
+                data[8..16].copy_from_slice(&v2.to_le_bytes());
+                data.truncate(len);
+
+                let hash_code = (murmurhash3_x64_128(&data, 47).0 as 
i64).abs();
+                let hash_code =
+                    (hash_code as u64 & 0xffffffffffffff00_u64) as i64 | 
(encoding_val as i64);
+                let mb = MetaStringBytes::new(data, hash_code);
+                entry.insert(mb)
             }
-            data.truncate(len);
-            let hash_code = (murmurhash3_x64_128(&data, 47).0 as i64).abs();
-            let hash_code =
-                (hash_code as u64 & 0xffffffffffffff00_u64) as i64 | 
(encoding_val as i64);
-            let mb = MetaStringBytes::new(
-                data.clone(),
-                hash_code,
-                byte_to_encoding(encoding_val),
-                v1,
-                v2,
-            );
-            self.small_map.insert(key, mb.clone());
-            Ok(mb)
+        };
+        // update dynamic_read
+        let ptr = mb_ref as *const MetaStringBytes;
+        let id = self.dynamic_read_id;
+        self.dynamic_read_id += 1;
+        if id >= self.dynamic_read.len() {
+            self.dynamic_read.resize(id * 2, None);
         }
+        self.dynamic_read[id] = Some(ptr);
+        Ok(mb_ref)
     }
 
     fn read_bytes_as_u64(reader: &mut Reader, len: usize) -> Result<u64, 
Error> {
@@ -394,16 +342,7 @@ impl MetaStringReaderResolver {
         Ok(v)
     }
 
-    fn update_dynamic_string(&mut self, mb: MetaStringBytes) {
-        let id = self.dynamic_read_id;
-        self.dynamic_read_id += 1;
-        if id >= self.dynamic_read.len() {
-            self.dynamic_read.resize(id * 2 + 1, None);
-        }
-        self.dynamic_read[id] = Some(mb);
-    }
-
-    pub fn reset_read(&mut self) {
+    pub fn reset(&mut self) {
         if self.dynamic_read_id != 0 {
             for i in 0..self.dynamic_read_id {
                 self.dynamic_read[i] = None;
@@ -412,14 +351,19 @@ impl MetaStringReaderResolver {
         }
     }
 
-    pub fn read_meta_string(&mut self, reader: &mut Reader) -> Result<String, 
Error> {
-        let mb = self.read_meta_string_bytes(reader)?;
-        Ok(if let Some(s) = self.meta_string_bytes_to_string.get(&mb) {
-            s.clone()
-        } else {
-            let s = mb.decode_lossy();
-            self.meta_string_bytes_to_string.insert(mb, s.clone());
-            s
-        })
+    pub fn read_meta_string(&mut self, reader: &mut Reader) -> 
Result<&MetaString, Error> {
+        let ptr = {
+            let mb_ref = self.read_meta_string_bytes(reader)?;
+            mb_ref as *const MetaStringBytes
+        };
+        let ms_ref = self
+            .meta_string_bytes_to_string
+            .entry(ptr)
+            .or_insert_with(|| {
+                let mb_ref = unsafe { &*ptr };
+                mb_ref.to_metastring().unwrap()
+            });
+
+        Ok(ms_ref)
     }
 }
diff --git a/rust/fory-core/src/resolver/type_resolver.rs 
b/rust/fory-core/src/resolver/type_resolver.rs
index 7da594172..3ee153418 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -95,8 +95,8 @@ pub struct TypeInfo {
     type_def: Rc<Vec<u8>>,
     type_meta: Rc<TypeMeta>,
     type_id: u32,
-    namespace: MetaString,
-    type_name: MetaString,
+    namespace: Rc<MetaString>,
+    type_name: Rc<MetaString>,
     register_by_name: bool,
     harness: Harness,
 }
@@ -147,8 +147,8 @@ impl TypeInfo {
             type_def: Rc::from(type_def_bytes),
             type_meta,
             type_id,
-            namespace: namespace_metastring,
-            type_name: type_name_metastring,
+            namespace: Rc::from(namespace_metastring),
+            type_name: Rc::from(type_name_metastring),
             register_by_name,
             harness,
         })
@@ -179,8 +179,8 @@ impl TypeInfo {
             type_def: Rc::from(type_def),
             type_meta: Rc::new(meta),
             type_id,
-            namespace: namespace_metastring,
-            type_name: type_name_metastring,
+            namespace: Rc::from(namespace_metastring),
+            type_name: Rc::from(type_name_metastring),
             register_by_name,
             harness,
         })
@@ -190,12 +190,12 @@ impl TypeInfo {
         self.type_id
     }
 
-    pub fn get_namespace(&self) -> &MetaString {
-        &self.namespace
+    pub fn get_namespace(&self) -> Rc<MetaString> {
+        self.namespace.clone()
     }
 
-    pub fn get_type_name(&self) -> &MetaString {
-        &self.type_name
+    pub fn get_type_name(&self) -> Rc<MetaString> {
+        self.type_name.clone()
     }
 
     pub fn get_type_def(&self) -> Rc<Vec<u8>> {
@@ -294,7 +294,7 @@ pub struct TypeResolver {
     type_info_map_by_id: HashMap<u32, Rc<TypeInfo>>,
     type_info_map: HashMap<std::any::TypeId, Rc<TypeInfo>>,
     type_info_map_by_name: HashMap<(String, String), Rc<TypeInfo>>,
-    type_info_map_by_ms_name: HashMap<(MetaString, MetaString), Rc<TypeInfo>>,
+    type_info_map_by_ms_name: HashMap<(Rc<MetaString>, Rc<MetaString>), 
Rc<TypeInfo>>,
     // Fast lookup by numeric ID for common types
     type_id_index: Vec<u32>,
     compatible: bool,
@@ -347,11 +347,11 @@ impl TypeResolver {
 
     pub fn get_type_info_by_msname(
         &self,
-        namespace: &MetaString,
-        type_name: &MetaString,
+        namespace: Rc<MetaString>,
+        type_name: Rc<MetaString>,
     ) -> Option<Rc<TypeInfo>> {
         self.type_info_map_by_ms_name
-            .get(&(namespace.clone(), type_name.clone()))
+            .get(&(namespace, type_name))
             .cloned()
     }
 
@@ -378,10 +378,10 @@ impl TypeResolver {
 
     pub fn get_name_harness(
         &self,
-        namespace: &MetaString,
-        type_name: &MetaString,
+        namespace: Rc<MetaString>,
+        type_name: Rc<MetaString>,
     ) -> Option<Rc<Harness>> {
-        let key = (namespace.clone(), type_name.clone());
+        let key = (namespace, type_name);
         self.type_info_map_by_ms_name
             .get(&key)
             .map(|info| Rc::new(info.get_harness().clone()))
@@ -396,10 +396,10 @@ impl TypeResolver {
 
     pub fn get_ext_name_harness(
         &self,
-        namespace: &MetaString,
-        type_name: &MetaString,
+        namespace: Rc<MetaString>,
+        type_name: Rc<MetaString>,
     ) -> Result<Rc<Harness>, Error> {
-        let key = (namespace.clone(), type_name.clone());
+        let key = (namespace, type_name);
         self.type_info_map_by_ms_name
             .get(&key)
             .map(|info| Rc::new(info.get_harness().clone()))
diff --git a/rust/fory-core/src/serializer/enum_.rs 
b/rust/fory-core/src/serializer/enum_.rs
index 2d07ec109..46755f210 100644
--- a/rust/fory-core/src/serializer/enum_.rs
+++ b/rust/fory-core/src/serializer/enum_.rs
@@ -60,10 +60,10 @@ pub fn write_type_info<T: Serializer>(context: &mut 
WriteContext) -> Result<(),
         context.writer.write_varuint32(meta_index);
     } else {
         let type_info = 
context.get_type_resolver().get_type_info(&rs_type_id)?;
-        let namespace = type_info.get_namespace().to_owned();
-        let type_name = type_info.get_type_name().to_owned();
-        context.write_meta_string_bytes(&namespace)?;
-        context.write_meta_string_bytes(&type_name)?;
+        let namespace = type_info.get_namespace();
+        let type_name = type_info.get_type_name();
+        context.write_meta_string_bytes(namespace)?;
+        context.write_meta_string_bytes(type_name)?;
     }
     Ok(())
 }
@@ -111,8 +111,8 @@ pub fn read_type_info<T: Serializer>(context: &mut 
ReadContext) -> Result<(), Er
     if context.is_share_meta() {
         let _meta_index = context.reader.read_varuint32()?;
     } else {
-        let _namespace_msb = context.read_meta_string_bytes()?;
-        let _type_name_msb = context.read_meta_string_bytes()?;
+        let _namespace_msb = context.read_meta_string()?;
+        let _type_name_msb = context.read_meta_string()?;
     }
     Ok(())
 }
diff --git a/rust/fory-core/src/serializer/skip.rs 
b/rust/fory-core/src/serializer/skip.rs
index 8cb0d421a..524d0ab26 100644
--- a/rust/fory-core/src/serializer/skip.rs
+++ b/rust/fory-core/src/serializer/skip.rs
@@ -181,7 +181,7 @@ pub fn skip_value(
                 let type_resolver = context.get_type_resolver();
                 let type_meta = type_info.get_type_meta();
                 type_resolver
-                    .get_ext_name_harness(&type_meta.get_namespace(), 
&type_meta.get_type_name())?
+                    .get_ext_name_harness(type_meta.get_namespace(), 
type_meta.get_type_name())?
                     .get_read_data_fn()(context)?;
                 Ok(())
             } else {
diff --git a/rust/fory-core/src/serializer/struct_.rs 
b/rust/fory-core/src/serializer/struct_.rs
index 98597e60b..4f821bd9c 100644
--- a/rust/fory-core/src/serializer/struct_.rs
+++ b/rust/fory-core/src/serializer/struct_.rs
@@ -50,10 +50,10 @@ pub fn write_type_info<T: Serializer>(context: &mut 
WriteContext) -> Result<(),
             context.writer.write_varuint32(meta_index);
         } else {
             let type_info = 
context.get_type_resolver().get_type_info(&rs_type_id)?;
-            let namespace = type_info.get_namespace().to_owned();
-            let type_name = type_info.get_type_name().to_owned();
-            context.write_meta_string_bytes(&namespace)?;
-            context.write_meta_string_bytes(&type_name)?;
+            let namespace = type_info.get_namespace();
+            let type_name = type_info.get_type_name();
+            context.write_meta_string_bytes(namespace)?;
+            context.write_meta_string_bytes(type_name)?;
         }
     } else if type_id & 0xff == TypeId::NAMED_COMPATIBLE_STRUCT as u32
         || type_id & 0xff == TypeId::COMPATIBLE_STRUCT as u32
@@ -77,8 +77,8 @@ pub fn read_type_info<T: Serializer>(context: &mut 
ReadContext) -> Result<(), Er
         if context.is_share_meta() {
             let _meta_index = context.reader.read_varuint32()?;
         } else {
-            let _namespace_msb = context.read_meta_string_bytes()?;
-            let _type_name_msb = context.read_meta_string_bytes()?;
+            let _namespace_msb = context.read_meta_string()?;
+            let _type_name_msb = context.read_meta_string()?;
         }
     } else if local_type_id & 0xff == TypeId::NAMED_COMPATIBLE_STRUCT as u32
         || local_type_id & 0xff == TypeId::COMPATIBLE_STRUCT as u32
diff --git a/rust/tests/tests/test_cross_language.rs 
b/rust/tests/tests/test_cross_language.rs
index 9a61cc55c..207507eb4 100644
--- a/rust/tests/tests/test_cross_language.rs
+++ b/rust/tests/tests/test_cross_language.rs
@@ -662,25 +662,6 @@ fn _test_skip_custom(fory1: &Fory, fory2: &Fory) {
     fs::write(&data_file_path, bytes).unwrap();
 }
 
-#[test]
-fn test_kankankan() {
-    let mut fory1 = Fory::default().compatible(true);
-    fory1.register_serializer::<MyExt>(103).unwrap();
-    fory1.register::<Empty>(104).unwrap();
-    let mut fory2 = Fory::default().compatible(true);
-    fory2.register::<Color>(101).unwrap();
-    fory2.register::<MyStruct>(102).unwrap();
-    fory2.register_serializer::<MyExt>(103).unwrap();
-    fory2.register::<MyWrapper>(104).unwrap();
-    let wrapper = MyWrapper {
-        color: Color::White,
-        my_struct: MyStruct { id: 42 },
-        my_ext: MyExt { id: 43 },
-    };
-    let bytes = fory2.serialize(&wrapper).unwrap();
-    fory1.deserialize::<Empty>(&bytes).unwrap();
-}
-
 #[test]
 #[ignore]
 fn test_skip_id_custom() {
@@ -716,7 +697,7 @@ fn test_skip_name_custom() {
 #[test]
 #[ignore]
 fn test_consistent_named() {
-    let mut fory = Fory::default().compatible(true);
+    let mut fory = Fory::default().compatible(false);
     fory.register_by_name::<Color>("color").unwrap();
     fory.register_by_name::<MyStruct>("my_struct").unwrap();
     fory.register_serializer_by_name::<MyExt>("my_ext").unwrap();
@@ -730,47 +711,31 @@ fn test_consistent_named() {
     let reader = Reader::new(bytes.as_slice());
     let mut context = ReadContext::new_from_fory(reader, &fory);
 
-    assert_eq!(
-        fory.deserialize_with_context::<Color>(&mut context)
-            .unwrap(),
-        color
-    );
-    assert_eq!(
-        fory.deserialize_with_context::<Color>(&mut context)
-            .unwrap(),
-        color
-    );
-    assert_eq!(
-        fory.deserialize_with_context::<Color>(&mut context)
-            .unwrap(),
-        color
-    );
+    for _ in 0..3 {
+        assert_eq!(
+            fory.deserialize_with_context::<Color>(&mut context)
+                .unwrap(),
+            color
+        );
+    }
+    for _ in 0..3 {
+        assert_eq!(
+            fory.deserialize_with_context::<MyExt>(&mut context)
+                .unwrap(),
+            my_ext
+        );
+    }
     // assert_eq!(fory.deserialize_with_context::<MyStruct>(&mut 
context).unwrap(), my_struct);
-    assert_eq!(
-        fory.deserialize_with_context::<MyExt>(&mut context)
-            .unwrap(),
-        my_ext
-    );
-    assert_eq!(
-        fory.deserialize_with_context::<MyExt>(&mut context)
-            .unwrap(),
-        my_ext
-    );
-    assert_eq!(
-        fory.deserialize_with_context::<MyExt>(&mut context)
-            .unwrap(),
-        my_ext
-    );
 
     let writer = Writer::default();
     let mut context = WriteContext::new_from_fory(writer, &fory);
-    fory.serialize_with_context(&color, &mut context).unwrap();
-    fory.serialize_with_context(&color, &mut context).unwrap();
-    fory.serialize_with_context(&color, &mut context).unwrap();
-    // todo: checkVersion
-    // fory.serialize_with_context(&my_struct, &mut context);
-    fory.serialize_with_context(&my_ext, &mut context).unwrap();
-    fory.serialize_with_context(&my_ext, &mut context).unwrap();
-    fory.serialize_with_context(&my_ext, &mut context).unwrap();
+    for _ in 0..3 {
+        fory.serialize_with_context(&color, &mut context).unwrap();
+    }
+    for _ in 0..3 {
+        fory.serialize_with_context(&my_ext, &mut context).unwrap();
+    }
+    // // todo: checkVersion
+    // // fory.serialize_with_context(&my_struct, &mut context);
     fs::write(&data_file_path, context.writer.dump()).unwrap();
 }
diff --git a/rust/tests/tests/test_metastring_resolver.rs 
b/rust/tests/tests/test_metastring_resolver.rs
new file mode 100644
index 000000000..a3f6ff3d6
--- /dev/null
+++ b/rust/tests/tests/test_metastring_resolver.rs
@@ -0,0 +1,122 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use fory_core::meta::NAMESPACE_ENCODER;
+use fory_core::resolver::metastring_resolver::{
+    MetaStringReaderResolver, MetaStringWriterResolver,
+};
+use fory_core::{Reader, Writer};
+use std::rc::Rc;
+
+#[test]
+pub fn empty() {
+    let mut ms_writer = MetaStringWriterResolver::default();
+    let mut ms_reader = MetaStringReaderResolver::default();
+
+    for _ in 0..3 {
+        let ms = NAMESPACE_ENCODER.encode("").unwrap();
+        let rc_ms = Rc::from(ms);
+
+        let mut writer = Writer::default();
+        ms_writer
+            .write_meta_string_bytes(&mut writer, rc_ms.clone())
+            .unwrap();
+
+        let binding = writer.dump();
+        let mut reader = Reader::new(binding.as_slice());
+
+        let read_ms = ms_reader.read_meta_string(&mut reader).unwrap();
+        assert_eq!(&*rc_ms, read_ms);
+        ms_writer.reset();
+        ms_reader.reset();
+    }
+}
+
+#[test]
+pub fn small_ms() {
+    let mut ms_writer = MetaStringWriterResolver::default();
+    let mut ms_reader = MetaStringReaderResolver::default();
+    // test reset
+    for _ in 0..3 {
+        // write
+        let mut data = Vec::new();
+        for i in 0..20 {
+            let ms = NAMESPACE_ENCODER.encode(&format!("ms_{i}")).unwrap();
+            let rc_ms = Rc::from(ms);
+            // test cache
+            for _ in 0..3 {
+                data.push(rc_ms.clone());
+            }
+        }
+        let mut writer = Writer::default();
+        for ms in data.iter() {
+            ms_writer
+                .write_meta_string_bytes(&mut writer, ms.clone())
+                .unwrap();
+        }
+        // read
+        let binding = writer.dump();
+        let mut reader = Reader::new(binding.as_slice());
+        let read_data: Vec<_> = (0..60)
+            .map(|_| ms_reader.read_meta_string(&mut reader).unwrap().clone())
+            .collect();
+        for i in 0..60 {
+            assert_eq!(*data[i], read_data[i]);
+        }
+        ms_writer.reset();
+        ms_reader.reset();
+    }
+}
+
+#[test]
+pub fn big_ms() {
+    let long_string = "a".repeat(50);
+    let mut ms_writer = MetaStringWriterResolver::default();
+    let mut ms_reader = MetaStringReaderResolver::default();
+    // test reset
+    for _ in 0..3 {
+        // write
+        let mut data = Vec::new();
+        for i in 0..20 {
+            let ms = NAMESPACE_ENCODER
+                .encode(&format!("{long_string}_{i}"))
+                .unwrap();
+            let rc_ms = Rc::from(ms);
+            // test cache
+            for _ in 0..3 {
+                data.push(rc_ms.clone());
+            }
+        }
+        let mut writer = Writer::default();
+        for ms in data.iter() {
+            ms_writer
+                .write_meta_string_bytes(&mut writer, ms.clone())
+                .unwrap();
+        }
+        // read
+        let binding = writer.dump();
+        let mut reader = Reader::new(binding.as_slice());
+        let read_data: Vec<_> = (0..60)
+            .map(|_| ms_reader.read_meta_string(&mut reader).unwrap().clone())
+            .collect();
+        for i in 0..60 {
+            assert_eq!(*data[i], read_data[i]);
+        }
+        ms_writer.reset();
+        ms_reader.reset();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to