This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to tag v0.14.1-rc1 in repository https://gitbox.apache.org/repos/asf/fory.git
commit b6063dedd3a899f729f374a720596377309298a3 Author: urlyy <[email protected]> AuthorDate: Thu Dec 18 12:36:25 2025 +0800 fix(Rust): Move the calculating of TypeMeta::bytes and TypeMeta::hash ahead of serialization (#3060) ## What does this PR do? Move the calculating of TypeMeta::bytes and TypeMeta::hash ahead of serialization. The TypeMeta object will save and hold its own bytes and hash when it is created. ## Related issues Fixes #3058 ## Does this PR introduce any user-facing change? No --- rust/fory-core/src/meta/type_meta.rs | 45 ++++++++++++++++++++-------- rust/fory-core/src/resolver/type_resolver.rs | 18 +++++------ rust/tests/tests/test_meta.rs | 39 ++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 22 deletions(-) diff --git a/rust/fory-core/src/meta/type_meta.rs b/rust/fory-core/src/meta/type_meta.rs index e21d64651..a4fe08086 100644 --- a/rust/fory-core/src/meta/type_meta.rs +++ b/rust/fory-core/src/meta/type_meta.rs @@ -326,6 +326,7 @@ pub struct TypeMeta { type_name: Rc<MetaString>, register_by_name: bool, field_infos: Vec<FieldInfo>, + bytes: Vec<u8>, } impl TypeMeta { @@ -335,15 +336,20 @@ impl TypeMeta { type_name: MetaString, register_by_name: bool, field_infos: Vec<FieldInfo>, - ) -> TypeMeta { - TypeMeta { + ) -> Result<TypeMeta, Error> { + let mut meta = TypeMeta { hash: 0, type_id, namespace: Rc::from(namespace), type_name: Rc::from(type_name), register_by_name, field_infos, - } + bytes: vec![], + }; + let (bytes, meta_hash) = meta.to_bytes()?; + meta.bytes = bytes; + meta.hash = meta_hash; + Ok(meta) } #[inline(always)] @@ -372,15 +378,25 @@ impl TypeMeta { } #[inline(always)] - pub fn empty() -> TypeMeta { - TypeMeta { + pub fn get_bytes(&self) -> &[u8] { + &self.bytes + } + + #[inline(always)] + pub fn empty() -> Result<TypeMeta, Error> { + let mut meta = TypeMeta { hash: 0, type_id: 0, namespace: Rc::from(MetaString::get_empty().clone()), type_name: Rc::from(MetaString::get_empty().clone()), register_by_name: false, field_infos: vec![], - } + bytes: vec![], + }; + let (bytes, meta_hash) = meta.to_bytes()?; + meta.bytes = bytes; + meta.hash = meta_hash; + Ok(meta) } /// Creates a deep clone with new Rc instances. @@ -393,6 +409,7 @@ impl TypeMeta { type_name: Rc::new((*self.type_name).clone()), register_by_name: self.register_by_name, field_infos: self.field_infos.clone(), + bytes: self.bytes.clone(), } } @@ -402,7 +419,7 @@ impl TypeMeta { type_name: MetaString, register_by_name: bool, field_infos: Vec<FieldInfo>, - ) -> TypeMeta { + ) -> Result<TypeMeta, Error> { TypeMeta::new(type_id, namespace, type_name, register_by_name, field_infos) } @@ -628,13 +645,13 @@ impl TypeMeta { Self::assign_field_ids(&type_info_current, &mut sorted_field_infos); } // if no type found, keep all fields id as -1 to be skipped. - Ok(TypeMeta::new( + TypeMeta::new( type_id, namespace, type_name, register_by_name, sorted_field_infos, - )) + ) } fn assign_field_ids(type_info_current: &TypeInfo, field_infos: &mut [FieldInfo]) { @@ -734,7 +751,7 @@ impl TypeMeta { Ok(()) } - pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, Error> { + fn to_bytes(&self) -> Result<(Vec<u8>, i64), Error> { // | global_binary_header | meta_bytes | let mut buffer = vec![]; let mut result = Writer::from_buffer(&mut buffer); @@ -752,13 +769,15 @@ impl TypeMeta { if is_compressed { header |= COMPRESS_META_FLAG; } - let meta_hash = murmurhash3_x64_128(meta_writer.dump().as_slice(), 47).0 as i64; - header |= (meta_hash << (64 - NUM_HASH_BITS)).abs(); + let hash_value = murmurhash3_x64_128(meta_writer.dump().as_slice(), 47).0 as i64; + let meta_hash_shifted = (hash_value << (64 - NUM_HASH_BITS)).abs(); + let meta_hash = meta_hash_shifted >> (64 - NUM_HASH_BITS); + header |= meta_hash_shifted; result.write_i64(header); if meta_size >= META_SIZE_MASK { result.write_varuint32((meta_size - META_SIZE_MASK) as u32); } result.write_bytes(meta_buffer.as_slice()); - Ok(buffer) + Ok((buffer, meta_hash)) } } diff --git a/rust/fory-core/src/resolver/type_resolver.rs b/rust/fory-core/src/resolver/type_resolver.rs index 4a88f8d2c..5f6e67fda 100644 --- a/rust/fory-core/src/resolver/type_resolver.rs +++ b/rust/fory-core/src/resolver/type_resolver.rs @@ -166,7 +166,7 @@ impl TypeInfo { TYPE_NAME_ENCODER.encode_with_encodings(type_name, TYPE_NAME_ENCODINGS)?; Ok(TypeInfo { type_def: Rc::from(vec![]), - type_meta: Rc::new(TypeMeta::empty()), + type_meta: Rc::new(TypeMeta::empty()?), type_id, namespace: Rc::from(namespace_meta_string), type_name: Rc::from(type_name_meta_string), @@ -180,7 +180,7 @@ impl TypeInfo { let namespace = type_meta.get_namespace(); let type_name = type_meta.get_type_name(); let register_by_name = !namespace.original.is_empty() || !type_name.original.is_empty(); - let type_def_bytes = type_meta.to_bytes()?; + let type_def_bytes = type_meta.get_bytes().to_owned(); Ok(TypeInfo { type_def: Rc::from(type_def_bytes), type_meta, @@ -250,7 +250,7 @@ impl TypeInfo { let type_id = remote_meta.get_type_id(); let namespace = remote_meta.get_namespace(); let type_name = remote_meta.get_type_name(); - let type_def_bytes = remote_meta.to_bytes().unwrap_or_default(); + let type_def_bytes = remote_meta.get_bytes().to_owned(); let register_by_name = !namespace.original.is_empty() || !type_name.original.is_empty(); let harness = if let Some(h) = local_harness { @@ -347,8 +347,8 @@ fn build_struct_type_infos<T: StructSerializer>( (*partial_info.type_name).clone(), partial_info.register_by_name, sorted_field_infos, - ); - let type_def_bytes = type_meta.to_bytes()?; + )?; + let type_def_bytes = type_meta.get_bytes().to_owned(); let main_type_info = TypeInfo { type_def: Rc::from(type_def_bytes), type_meta: Rc::new(type_meta), @@ -386,7 +386,7 @@ fn build_struct_type_infos<T: StructSerializer>( type_name_ms, true, fields_info.clone(), - ) + )? } else { // add a check to avoid collision with main enum type_id // since internal id is big alealdy, `74<<8 = 18944` is big enough to avoid collision most of time @@ -407,7 +407,7 @@ fn build_struct_type_infos<T: StructSerializer>( MetaString::get_empty().clone(), false, fields_info, - ) + )? }; let variant_type_info = @@ -433,8 +433,8 @@ fn build_serializer_type_infos( (*partial_info.type_name).clone(), partial_info.register_by_name, vec![], - ); - let type_def_bytes = type_meta.to_bytes()?; + )?; + let type_def_bytes = type_meta.get_bytes().to_owned(); let type_info = TypeInfo { type_def: Rc::from(type_def_bytes), type_meta: Rc::new(type_meta), diff --git a/rust/tests/tests/test_meta.rs b/rust/tests/tests/test_meta.rs new file mode 100644 index 000000000..dd5e85255 --- /dev/null +++ b/rust/tests/tests/test_meta.rs @@ -0,0 +1,39 @@ +// 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::{FieldInfo, FieldType, MetaString, TypeMeta}; + +#[test] +fn test_meta_hash() { + let meta = TypeMeta::new( + 42, + MetaString::get_empty().clone(), + MetaString::get_empty().clone(), + false, + vec![FieldInfo { + field_id: 43, + field_name: "f1".to_string(), + field_type: FieldType { + type_id: 44, + nullable: true, + generics: vec![], + }, + }], + ) + .unwrap(); + assert_ne!(meta.get_hash(), 0); +} --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
