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 707de6029 feat(rust): support skipping fields bytes when deserializing
in compatible mode (#2545)
707de6029 is described below
commit 707de6029dd84088eba48b436ddecdf231cd620c
Author: urlyy <[email protected]>
AuthorDate: Fri Aug 29 15:22:09 2025 +0800
feat(rust): support skipping fields bytes when deserializing in compatible
mode (#2545)
## What does this PR do?
Support skipping unneeded field bytes when deserializing in compatible
mode.
## Related issues
#2531
---
rust/fory-core/src/fory.rs | 2 +-
rust/fory-core/src/meta/type_meta.rs | 18 ++---
rust/fory-core/src/resolver/meta_resolver.rs | 2 +-
rust/fory-core/src/resolver/type_resolver.rs | 66 +++-------------
rust/fory-core/src/serializer/any.rs | 2 +-
rust/fory-core/src/serializer/mod.rs | 3 +-
rust/fory-core/src/serializer/nonexistent.rs | 108 +++++++++++++++++++++++++++
rust/fory-core/src/types.rs | 27 +++----
rust/fory-derive/src/object/derive_enum.rs | 2 +-
rust/fory-derive/src/object/misc.rs | 4 +-
rust/fory-derive/src/object/read.rs | 9 +--
rust/tests/tests/test_compatible.rs | 55 +++++++++++++-
12 files changed, 202 insertions(+), 96 deletions(-)
diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs
index 8a8b576e7..42c4492e1 100644
--- a/rust/fory-core/src/fory.rs
+++ b/rust/fory-core/src/fory.rs
@@ -90,7 +90,7 @@ impl Fory {
&self.type_resolver
}
- pub fn register<T: 'static + StructSerializer>(&mut self, id: u32) {
+ pub fn register<T: 'static + StructSerializer>(&mut self, id: i16) {
let type_info = TypeInfo::new::<T>(self, id);
self.type_resolver.register::<T>(type_info, id);
}
diff --git a/rust/fory-core/src/meta/type_meta.rs
b/rust/fory-core/src/meta/type_meta.rs
index eca526511..a436f2e89 100644
--- a/rust/fory-core/src/meta/type_meta.rs
+++ b/rust/fory-core/src/meta/type_meta.rs
@@ -39,10 +39,10 @@ static ENCODING_OPTIONS: &[Encoding] = &[
Encoding::LowerUpperDigitSpecial,
];
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FieldType {
pub type_id: i16,
- generics: Vec<FieldType>,
+ pub generics: Vec<FieldType>,
}
impl FieldType {
@@ -128,7 +128,7 @@ impl FieldType {
}
}
-#[derive(Debug, PartialEq, Eq)]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FieldInfo {
pub field_name: String,
pub field_type: FieldType,
@@ -213,19 +213,19 @@ impl FieldInfo {
#[derive(Debug)]
pub struct TypeMetaLayer {
- type_id: u32,
+ type_id: i16,
field_infos: Vec<FieldInfo>,
}
impl TypeMetaLayer {
- pub fn new(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
+ pub fn new(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMetaLayer {
TypeMetaLayer {
type_id,
field_infos,
}
}
- pub fn get_type_id(&self) -> u32 {
+ pub fn get_type_id(&self) -> i16 {
self.type_id
}
@@ -271,7 +271,7 @@ impl TypeMetaLayer {
if is_register_by_name {
todo!()
} else {
- type_id = reader.var_int32() as u32;
+ type_id = reader.var_int32() as i16;
}
let mut field_infos = Vec::with_capacity(num_fields);
for _ in 0..num_fields {
@@ -293,11 +293,11 @@ impl TypeMeta {
self.layers.first().unwrap().get_field_infos()
}
- pub fn get_type_id(&self) -> u32 {
+ pub fn get_type_id(&self) -> i16 {
self.layers.first().unwrap().get_type_id()
}
- pub fn from_fields(type_id: u32, field_infos: Vec<FieldInfo>) -> TypeMeta {
+ pub fn from_fields(type_id: i16, field_infos: Vec<FieldInfo>) -> TypeMeta {
TypeMeta {
// hash: 0,
layers: vec![TypeMetaLayer::new(type_id, field_infos)],
diff --git a/rust/fory-core/src/resolver/meta_resolver.rs
b/rust/fory-core/src/resolver/meta_resolver.rs
index 25eb0afd5..8423bd268 100644
--- a/rust/fory-core/src/resolver/meta_resolver.rs
+++ b/rust/fory-core/src/resolver/meta_resolver.rs
@@ -24,7 +24,7 @@ use std::rc::Rc;
#[derive(Default)]
pub struct MetaReaderResolver {
- reading_type_defs: Vec<Rc<TypeMeta>>,
+ pub reading_type_defs: Vec<Rc<TypeMeta>>,
}
impl MetaReaderResolver {
diff --git a/rust/fory-core/src/resolver/type_resolver.rs
b/rust/fory-core/src/resolver/type_resolver.rs
index eea1f347f..41fcdc557 100644
--- a/rust/fory-core/src/resolver/type_resolver.rs
+++ b/rust/fory-core/src/resolver/type_resolver.rs
@@ -18,9 +18,7 @@
use super::context::{ReadContext, WriteContext};
use crate::error::Error;
use crate::fory::Fory;
-use crate::serializer::{Serializer, StructSerializer};
-use crate::types::TypeId;
-use chrono::{NaiveDate, NaiveDateTime};
+use crate::serializer::StructSerializer;
use std::{any::Any, collections::HashMap};
pub struct Harness {
@@ -50,18 +48,18 @@ impl Harness {
pub struct TypeInfo {
type_def: Vec<u8>,
- type_id: u32,
+ type_id: i16,
}
impl TypeInfo {
- pub fn new<T: StructSerializer>(fory: &Fory, type_id: u32) -> TypeInfo {
+ pub fn new<T: StructSerializer>(fory: &Fory, type_id: i16) -> TypeInfo {
TypeInfo {
- type_def: T::type_def(fory),
+ type_def: T::type_def(fory, type_id),
type_id,
}
}
- pub fn get_type_id(&self) -> u32 {
+ pub fn get_type_id(&self) -> i16 {
self.type_id
}
@@ -70,56 +68,12 @@ impl TypeInfo {
}
}
+#[derive(Default)]
pub struct TypeResolver {
- serialize_map: HashMap<u32, Harness>,
- type_id_map: HashMap<std::any::TypeId, u32>,
+ serialize_map: HashMap<i16, Harness>,
+ type_id_map: HashMap<std::any::TypeId, i16>,
type_info_map: HashMap<std::any::TypeId, TypeInfo>,
}
-macro_rules! register_harness {
- ($ty:ty, $id:expr, $map:expr) => {{
- fn serializer(this: &dyn std::any::Any, context: &mut WriteContext) {
- let this = this.downcast_ref::<$ty>();
- match this {
- Some(v) => <$ty>::serialize(v, context),
- None => todo!(""),
- }
- }
-
- fn deserializer(context: &mut ReadContext) -> Result<Box<dyn
std::any::Any>, Error> {
- match <$ty>::deserialize(context) {
- Ok(v) => Ok(Box::new(v)),
- Err(e) => Err(e),
- }
- }
-
- $map.insert($id as u32, Harness::new(serializer, deserializer));
- }};
-}
-
-impl Default for TypeResolver {
- fn default() -> Self {
- let mut serialize_map = HashMap::new();
-
- register_harness!(bool, TypeId::BOOL, serialize_map);
- register_harness!(i8, TypeId::INT8, serialize_map);
- register_harness!(i16, TypeId::INT16, serialize_map);
- register_harness!(i32, TypeId::INT32, serialize_map);
- register_harness!(i64, TypeId::INT64, serialize_map);
- register_harness!(f32, TypeId::FLOAT32, serialize_map);
- register_harness!(f64, TypeId::FLOAT64, serialize_map);
-
- register_harness!(String, TypeId::STRING, serialize_map);
-
- register_harness!(NaiveDate, TypeId::LOCAL_DATE, serialize_map);
- register_harness!(NaiveDateTime, TypeId::TIMESTAMP, serialize_map);
-
- TypeResolver {
- serialize_map,
- type_id_map: HashMap::new(),
- type_info_map: HashMap::new(),
- }
- }
-}
impl TypeResolver {
pub fn get_type_info(&self, type_id: std::any::TypeId) -> &TypeInfo {
@@ -131,7 +85,7 @@ impl TypeResolver {
})
}
- pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id:
u32) {
+ pub fn register<T: StructSerializer>(&mut self, type_info: TypeInfo, id:
i16) {
fn serializer<T2: 'static + StructSerializer>(this: &dyn Any, context:
&mut WriteContext) {
let this = this.downcast_ref::<T2>();
match this {
@@ -161,7 +115,7 @@ impl TypeResolver {
self.get_harness(*self.type_id_map.get(&type_id).unwrap())
}
- pub fn get_harness(&self, id: u32) -> Option<&Harness> {
+ pub fn get_harness(&self, id: i16) -> Option<&Harness> {
self.serialize_map.get(&id)
}
}
diff --git a/rust/fory-core/src/serializer/any.rs
b/rust/fory-core/src/serializer/any.rs
index cf9ab52ce..321f75fc2 100644
--- a/rust/fory-core/src/serializer/any.rs
+++ b/rust/fory-core/src/serializer/any.rs
@@ -71,7 +71,7 @@ impl Serializer for Box<dyn Any> {
context
.get_fory()
.get_type_resolver()
- .get_harness(type_id as u32)
+ .get_harness(type_id)
.unwrap()
.get_deserializer()(context)
}
diff --git a/rust/fory-core/src/serializer/mod.rs
b/rust/fory-core/src/serializer/mod.rs
index be75e39f2..10a84175f 100644
--- a/rust/fory-core/src/serializer/mod.rs
+++ b/rust/fory-core/src/serializer/mod.rs
@@ -27,6 +27,7 @@ mod bool;
mod datetime;
mod list;
mod map;
+pub mod nonexistent;
mod number;
mod option;
mod primitive_list;
@@ -92,5 +93,5 @@ where
}
pub trait StructSerializer: Serializer + 'static {
- fn type_def(fory: &Fory) -> Vec<u8>;
+ fn type_def(fory: &Fory, type_id: i16) -> Vec<u8>;
}
diff --git a/rust/fory-core/src/serializer/nonexistent.rs
b/rust/fory-core/src/serializer/nonexistent.rs
new file mode 100644
index 000000000..b9732ea64
--- /dev/null
+++ b/rust/fory-core/src/serializer/nonexistent.rs
@@ -0,0 +1,108 @@
+// 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 crate::ensure;
+use crate::error::Error;
+use crate::meta::FieldType;
+use crate::resolver::context::ReadContext;
+use crate::serializer::Serializer;
+use crate::types::{RefFlag, TypeId, BASIC_TYPES, COLLECTION_TYPES};
+use anyhow::anyhow;
+use chrono::{NaiveDate, NaiveDateTime};
+
+macro_rules! basic_type_deserialize {
+ ($tid:expr, $context:expr; $(($ty:ty, $id:ident)),+ $(,)?) => {
+ $(
+ if $tid == TypeId::$id {
+ <$ty>::deserialize($context)?;
+ return Ok(());
+ }
+ )+else {
+ unreachable!()
+ }
+ };
+}
+
+pub fn skip_field_value(context: &mut ReadContext, field_type: &FieldType) ->
Result<(), Error> {
+ match TypeId::try_from(field_type.type_id) {
+ Ok(type_id) => {
+ if BASIC_TYPES.contains(&type_id) {
+ basic_type_deserialize!(type_id, context;
+ (bool, BOOL),
+ (i8, INT8),
+ (i16, INT16),
+ (i32, INT32),
+ (i64, INT64),
+ (f32, FLOAT32),
+ (f64, FLOAT64),
+ (String, STRING),
+ (NaiveDate, LOCAL_DATE),
+ (NaiveDateTime, TIMESTAMP),
+ );
+ } else if COLLECTION_TYPES.contains(&type_id) {
+ let ref_flag = context.reader.i8();
+ let actual_type_id = context.reader.i16();
+ let type_id_num = type_id.into();
+ ensure!(
+ actual_type_id == type_id_num,
+ anyhow!("Invalid field type, expected:{type_id_num},
actual:{actual_type_id}")
+ );
+ if ref_flag == (RefFlag::NotNullValue as i8)
+ || ref_flag == (RefFlag::RefValue as i8)
+ {
+ if type_id == TypeId::ARRAY || type_id == TypeId::SET {
+ let length = context.reader.var_int32() as usize;
+ println!("skipping array with length {}", length);
+ for _ in 0..length {
+ skip_field_value(context,
field_type.generics.first().unwrap())?;
+ }
+ } else if type_id == TypeId::MAP {
+ let length = context.reader.var_int32() as usize;
+ for _ in 0..length {
+ skip_field_value(context,
field_type.generics.first().unwrap())?;
+ skip_field_value(context,
field_type.generics.get(1).unwrap())?;
+ }
+ }
+ Ok(())
+ } else if ref_flag == (RefFlag::Null as i8) {
+ Err(anyhow!("Try to deserialize non-option type to null"))?
+ } else if ref_flag == (RefFlag::Ref as i8) {
+ Err(Error::Ref)
+ } else {
+ Err(anyhow!("Unknown ref flag, value:{ref_flag}"))?
+ }
+ } else {
+ unreachable!()
+ }
+ }
+ Err(_) => {
+ // skip ref_flag and meta_index
+ context.reader.i8();
+ context.reader.i16();
+ let type_defs: Vec<_> =
context.meta_resolver.reading_type_defs.to_vec();
+ for type_def in type_defs.iter() {
+ if type_def.get_type_id() == field_type.type_id {
+ let field_infos: Vec<_> =
type_def.get_field_infos().to_vec();
+ for field_info in field_infos.iter() {
+ skip_field_value(context, &field_info.field_type)?;
+ }
+ }
+ }
+ Ok(())
+ }
+ }
+}
diff --git a/rust/fory-core/src/types.rs b/rust/fory-core/src/types.rs
index f8bddebdc..6128447f0 100644
--- a/rust/fory-core/src/types.rs
+++ b/rust/fory-core/src/types.rs
@@ -112,19 +112,20 @@ pub fn compute_string_hash(s: &str) -> u32 {
hash as u32
}
-// const BASIC_TYPES: [FieldType; 11] = [
-// FieldType::BOOL,
-// FieldType::INT8,
-// FieldType::INT16,
-// FieldType::INT32,
-// FieldType::INT64,
-// FieldType::FLOAT,
-// FieldType::DOUBLE,
-// FieldType::STRING,
-// FieldType::BINARY,
-// FieldType::DATE,
-// FieldType::TIMESTAMP,
-// ];
+pub const BASIC_TYPES: [TypeId; 10] = [
+ TypeId::BOOL,
+ TypeId::INT8,
+ TypeId::INT16,
+ TypeId::INT32,
+ TypeId::INT64,
+ TypeId::FLOAT32,
+ TypeId::FLOAT64,
+ TypeId::STRING,
+ TypeId::LOCAL_DATE,
+ TypeId::TIMESTAMP,
+];
+
+pub const COLLECTION_TYPES: [TypeId; 3] = [TypeId::ARRAY, TypeId::SET,
TypeId::MAP];
pub fn compute_field_hash(hash: u32, id: i16) -> u32 {
let mut new_hash: u64 = (hash as u64) * 31 + (id as u64);
diff --git a/rust/fory-derive/src/object/derive_enum.rs
b/rust/fory-derive/src/object/derive_enum.rs
index 2f76c4df9..a571dd441 100644
--- a/rust/fory-derive/src/object/derive_enum.rs
+++ b/rust/fory-derive/src/object/derive_enum.rs
@@ -21,7 +21,7 @@ use syn::DataEnum;
pub fn gen_type_def(_data_enum: &DataEnum) -> TokenStream {
quote! {
- fn type_def(fory: &fory_core::fory::Fory) -> Vec<u8> {
+ fn type_def(fory: &fory_core::fory::Fory, type_id: i16) -> Vec<u8> {
Vec::new()
}
}
diff --git a/rust/fory-derive/src/object/misc.rs
b/rust/fory-derive/src/object/misc.rs
index 0c8f83dbf..867dd255e 100644
--- a/rust/fory-derive/src/object/misc.rs
+++ b/rust/fory-derive/src/object/misc.rs
@@ -56,9 +56,9 @@ fn type_def(fields: &[&Field]) -> TokenStream {
}
});
quote! {
- fn type_def(fory: &fory_core::fory::Fory) -> Vec<u8> {
+ fn type_def(fory: &fory_core::fory::Fory, layer_id: i16) -> Vec<u8> {
fory_core::meta::TypeMeta::from_fields(
- 0,
+ layer_id,
vec![#(#field_infos),*]
).to_bytes().unwrap()
}
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index 5652c11bd..3678a2697 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -113,14 +113,7 @@ fn deserialize_compatible(fields: &[&Field]) ->
TokenStream {
_ => {
// skip bytes
println!("no need to deserialize {:?}:{:?}",
_field.field_name.as_str(), _field.field_type);
- let _ = context
- .get_fory()
- .get_type_resolver()
- .get_harness((&_field.field_type).type_id as u32)
- .unwrap_or_else(|| {
- panic!("missing harness for type_id {}",
_field.field_type.type_id);
- })
- .get_deserializer()(context);
+
fory_core::serializer::nonexistent::skip_field_value(context,
&_field.field_type).unwrap();
}
}
}
diff --git a/rust/tests/tests/test_compatible.rs
b/rust/tests/tests/test_compatible.rs
index f31e5d4a1..d9d94d72b 100644
--- a/rust/tests/tests/test_compatible.rs
+++ b/rust/tests/tests/test_compatible.rs
@@ -30,7 +30,9 @@ fn simple() {
f3: Vec<i8>,
// f4: String,
f5: String,
- // f6: Vec<i8>,
+ f6: Vec<i8>,
+ f7: i8,
+ f8: i8,
}
#[derive(Fory, Debug)]
@@ -40,7 +42,9 @@ fn simple() {
f3: Vec<i8>,
f4: String,
f5: i8,
- // f6: Vec<i16>,
+ f6: Vec<i16>,
+ f7: i16,
+ f8: i8,
}
let mut fory1 = Fory::default().mode(Compatible);
@@ -52,7 +56,9 @@ fn simple() {
f2: String::from("hello"),
f3: vec![1, 2, 3],
f5: String::from("f5"),
- // f6: vec![42]
+ f6: vec![42],
+ f7: 43,
+ f8: 44,
};
let bin = fory1.serialize(&animal);
let obj: Animal2 = fory2.deserialize(&bin).unwrap();
@@ -61,6 +67,49 @@ fn simple() {
assert_eq!(animal.f3, obj.f3);
assert_eq!(obj.f4, String::default());
assert_eq!(obj.f5, i8::default());
+ assert_eq!(obj.f6, Vec::<i16>::default());
+ assert_eq!(obj.f7, i16::default());
+ assert_eq!(animal.f8, obj.f8);
+}
+
+#[test]
+fn nonexistent_struct() {
+ #[derive(Fory, Debug, Default)]
+ pub struct Item1 {
+ f1: i8,
+ }
+ #[derive(Fory, Debug, Default, PartialEq)]
+ pub struct Item2 {
+ f1: i64,
+ }
+ #[derive(Fory, Debug)]
+ struct Person1 {
+ f2: Item1,
+ f3: i8,
+ f4: String,
+ }
+ #[derive(Fory, Debug)]
+ struct Person2 {
+ f2: Item2,
+ f3: i64,
+ f4: String,
+ }
+ let mut fory1 = Fory::default().mode(Compatible);
+ let mut fory2 = Fory::default().mode(Compatible);
+ fory1.register::<Item1>(899);
+ fory1.register::<Person1>(999);
+ fory2.register::<Item2>(799);
+ fory2.register::<Person2>(999);
+ let person = Person1 {
+ f2: Item1 { f1: 42 },
+ f3: 24,
+ f4: String::from("foo"),
+ };
+ let bin = fory1.serialize(&person);
+ let obj: Person2 = fory2.deserialize(&bin).unwrap();
+ assert_eq!(obj.f2, Item2::default());
+ assert_eq!(obj.f3, i64::default());
+ assert_eq!(obj.f4, person.f4);
}
#[test]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]