This is an automated email from the ASF dual-hosted git repository.
wangweipeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fury.git
The following commit(s) were added to refs/heads/main by this push:
new e99b46f4 feat(Rust): Support polymorphism (#1795)
e99b46f4 is described below
commit e99b46f4b83f79a23cf240466479da9e3657810d
Author: weipeng <[email protected]>
AuthorDate: Thu Aug 8 16:45:31 2024 +0800
feat(Rust): Support polymorphism (#1795)
## What does this PR do?
1. Support polymorphism
Add a class resolver which is used for managing the relationship between
type_id and serializer. The serializer is generated by a generic
function which will be called by dynamic dispatch. When a struct uses
`Box<dyn Any>` as a field type and does not specify the specific type,
we will use the class resolver to load the handler by type_id. In this
situation, there is some performance overhead due to hash lookup, but it
greatly improves convenience.
Use as follow:
```Rust
#[test]
fn any() {
#[derive(Fury, Debug)]
struct Animal {
f3: String,
}
#[derive(Fury, Debug)]
struct Person {
f1: Box<dyn Any>,
}
let person = Person {
f1: Box::new(Animal {
f3: String::from("hello"),
}),
};
let mut fury = Fury::default();
fury.register::<Animal>(999);
fury.register::<Person>(1000);
let bin = fury.serialize(&person);
let obj: Person = fury.deserialize(&bin).expect("");
assert_eq!(true, obj.f1.is::<Animal>())
}
```
2. Add a register function for user to register Struct and id
3. Remove tag and hash which were generate by macro before and were
removed in our protocol now.
## TODO
1. Internal types like StringăSet and Map should be registered by fury
by default and lookup by pattern match to avoid hash overhead.
2. More unit testcases.
3. Support `Box<dyn customTrait> `
---
rust/fury-core/src/buffer.rs | 11 +++
rust/fury-core/src/error.rs | 13 ++-
rust/fury-core/src/fury.rs | 14 ++-
rust/fury-core/src/meta/type_meta.rs | 21 +++--
rust/fury-core/src/resolver/class_resolver.rs | 116 ++++++++++++++++++++++++
rust/fury-core/src/resolver/context.rs | 4 +-
rust/fury-core/src/resolver/meta_resolver.rs | 11 ++-
rust/fury-core/src/resolver/mod.rs | 1 +
rust/fury-core/src/serializer/any.rs | 87 ++++++++++++++++++
rust/fury-core/src/serializer/bool.rs | 5 +-
rust/fury-core/src/serializer/datetime.rs | 9 +-
rust/fury-core/src/serializer/list.rs | 5 +-
rust/fury-core/src/serializer/map.rs | 5 +-
rust/fury-core/src/serializer/mod.rs | 25 +++--
rust/fury-core/src/serializer/number.rs | 11 +--
rust/fury-core/src/serializer/option.rs | 19 ++--
rust/fury-core/src/serializer/primitive_list.rs | 31 ++++---
rust/fury-core/src/serializer/set.rs | 5 +-
rust/fury-core/src/serializer/string.rs | 5 +-
rust/fury-core/src/types.rs | 6 +-
rust/fury-derive/src/lib.rs | 16 +---
rust/fury-derive/src/object/misc.rs | 37 +++-----
rust/fury-derive/src/object/read.rs | 85 ++++++++++-------
rust/fury-derive/src/object/serializer.rs | 10 +-
rust/fury-derive/src/object/write.rs | 16 +---
rust/tests/tests/test_complex_struct.rs | 40 ++++++--
26 files changed, 437 insertions(+), 171 deletions(-)
diff --git a/rust/fury-core/src/buffer.rs b/rust/fury-core/src/buffer.rs
index c81ec17f..5668034c 100644
--- a/rust/fury-core/src/buffer.rs
+++ b/rust/fury-core/src/buffer.rs
@@ -247,4 +247,15 @@ impl<'bf> Reader<'bf> {
self.move_next(len);
result
}
+
+ pub fn reset_cursor_to_here(&self) -> impl FnOnce(&mut Self) {
+ let raw_cursor = self.cursor;
+ move |this: &mut Self| {
+ this.cursor = raw_cursor;
+ }
+ }
+
+ pub fn aligned<T>(&self) -> bool {
+ unsafe { (self.bf.as_ptr().add(self.cursor) as usize) %
std::mem::align_of::<T>() == 0 }
+ }
}
diff --git a/rust/fury-core/src/error.rs b/rust/fury-core/src/error.rs
index a6fc9967..e2b2e251 100644
--- a/rust/fury-core/src/error.rs
+++ b/rust/fury-core/src/error.rs
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-use super::types::{FieldType, Language};
+use super::types::Language;
#[derive(thiserror::Error, Debug)]
pub enum Error {
@@ -31,8 +31,8 @@ pub enum Error {
#[error("BadRefFlag")]
BadRefFlag,
- #[error("Bad FieldType; expected: {expected:?}, actual: {actial:?}")]
- FieldType { expected: FieldType, actial: i16 },
+ #[error("Bad FieldType; expected: {expected:?}, actual: {actual:?}")]
+ FieldType { expected: i16, actual: i16 },
#[error("Bad timestamp; out-of-range number of milliseconds")]
NaiveDateTime,
@@ -40,8 +40,8 @@ pub enum Error {
#[error("Bad date; out-of-range")]
NaiveDate,
- #[error("Schema is not consistent; expected: {expected:?}, actual:
{actial:?}")]
- StructHash { expected: u32, actial: u32 },
+ #[error("Schema is not consistent; expected: {expected:?}, actual:
{actual:?}")]
+ StructHash { expected: u32, actual: u32 },
#[error("Bad Tag Type: {0}")]
TagType(u8),
@@ -75,4 +75,7 @@ pub enum Error {
#[error("Invalid character value for LOWER_UPPER_DIGIT_SPECIAL decoding:
{value:?}")]
InvalidLowerUpperDigitSpecialValue { value: u8 },
+
+ #[error("Unregistered type when serializing or deserializing object of Any
type: {value:?}")]
+ UnregisteredType { value: u32 },
}
diff --git a/rust/fury-core/src/fury.rs b/rust/fury-core/src/fury.rs
index 376374c9..ecf3c404 100644
--- a/rust/fury-core/src/fury.rs
+++ b/rust/fury-core/src/fury.rs
@@ -17,19 +17,22 @@
use crate::buffer::{Reader, Writer};
use crate::error::Error;
+use crate::resolver::class_resolver::{ClassInfo, ClassResolver};
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
-use crate::serializer::Serializer;
+use crate::serializer::{Serializer, StructSerializer};
use crate::types::{config_flags, Language, Mode, SIZE_OF_REF_AND_TYPE};
pub struct Fury {
mode: Mode,
+ class_resolver: ClassResolver,
}
impl Default for Fury {
fn default() -> Self {
Fury {
mode: Mode::SchemaConsistent,
+ class_resolver: ClassResolver::default(),
}
}
}
@@ -82,4 +85,13 @@ impl Fury {
}
writer.dump()
}
+
+ pub fn get_class_resolver(&self) -> &ClassResolver {
+ &self.class_resolver
+ }
+
+ pub fn register<T: 'static + StructSerializer>(&mut self, id: u32) {
+ let class_info = ClassInfo::new::<T>(self, id);
+ self.class_resolver.register::<T>(class_info, id);
+ }
}
diff --git a/rust/fury-core/src/meta/type_meta.rs
b/rust/fury-core/src/meta/type_meta.rs
index f7f781df..c1086e0f 100644
--- a/rust/fury-core/src/meta/type_meta.rs
+++ b/rust/fury-core/src/meta/type_meta.rs
@@ -19,20 +19,17 @@ use super::meta_string::MetaStringEncoder;
use crate::buffer::{Reader, Writer};
use crate::error::Error;
use crate::meta::{Encoding, MetaStringDecoder};
-use crate::types::FieldType;
-//todo backward/forward compatibility
-#[allow(dead_code)]
pub struct FieldInfo {
field_name: String,
- field_type: FieldType,
+ field_id: i16,
}
impl FieldInfo {
- pub fn new(field_name: &str, field_type: FieldType) -> FieldInfo {
+ pub fn new(field_name: &str, field_type: i16) -> FieldInfo {
FieldInfo {
field_name: field_name.to_string(),
- field_type,
+ field_id: field_type,
}
}
@@ -60,7 +57,7 @@ impl FieldInfo {
.unwrap();
FieldInfo {
field_name,
- field_type: FieldType::try_from(type_id).unwrap(),
+ field_id: type_id,
}
}
@@ -80,7 +77,7 @@ impl FieldInfo {
header |= (size << 5) as u8;
writer.u8(header);
}
- writer.i16(self.field_type as i16);
+ writer.i16(self.field_id);
writer.bytes(encoded);
Ok(writer.dump())
}
@@ -99,6 +96,10 @@ impl TypeMetaLayer {
}
}
+ pub fn get_type_id(&self) -> u32 {
+ self.type_id
+ }
+
pub fn get_field_info(&self) -> &Vec<FieldInfo> {
&self.field_info
}
@@ -133,6 +134,10 @@ impl TypeMeta {
self.layers.first().unwrap().get_field_info()
}
+ pub fn get_type_id(&self) -> u32 {
+ self.layers.first().unwrap().get_type_id()
+ }
+
pub fn from_fields(type_id: u32, field_info: Vec<FieldInfo>) -> TypeMeta {
TypeMeta {
hash: 0,
diff --git a/rust/fury-core/src/resolver/class_resolver.rs
b/rust/fury-core/src/resolver/class_resolver.rs
new file mode 100644
index 00000000..544da22d
--- /dev/null
+++ b/rust/fury-core/src/resolver/class_resolver.rs
@@ -0,0 +1,116 @@
+// 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 super::context::{ReadContext, WriteContext};
+use crate::error::Error;
+use crate::fury::Fury;
+use crate::serializer::StructSerializer;
+use std::any::TypeId;
+use std::{any::Any, collections::HashMap};
+
+pub struct Harness {
+ serializer: fn(&dyn Any, &mut WriteContext),
+ deserializer: fn(&mut ReadContext) -> Result<Box<dyn Any>, Error>,
+}
+
+impl Harness {
+ pub fn new(
+ serializer: fn(&dyn Any, &mut WriteContext),
+ deserializer: fn(&mut ReadContext) -> Result<Box<dyn Any>, Error>,
+ ) -> Harness {
+ Harness {
+ serializer,
+ deserializer,
+ }
+ }
+
+ pub fn get_serializer(&self) -> fn(&dyn Any, &mut WriteContext) {
+ self.serializer
+ }
+
+ pub fn get_deserializer(&self) -> fn(&mut ReadContext) -> Result<Box<dyn
Any>, Error> {
+ self.deserializer
+ }
+}
+
+pub struct ClassInfo {
+ type_def: Vec<u8>,
+ type_id: u32,
+}
+
+impl ClassInfo {
+ pub fn new<T: StructSerializer>(fury: &Fury, type_id: u32) -> ClassInfo {
+ ClassInfo {
+ type_def: T::type_def(fury),
+ type_id,
+ }
+ }
+
+ pub fn get_type_id(&self) -> u32 {
+ self.type_id
+ }
+
+ pub fn get_type_def(&self) -> &Vec<u8> {
+ &self.type_def
+ }
+}
+
+#[derive(Default)]
+pub struct ClassResolver {
+ serialize_map: HashMap<u32, Harness>,
+ type_id_map: HashMap<TypeId, u32>,
+ class_info_map: HashMap<TypeId, ClassInfo>,
+}
+
+impl ClassResolver {
+ pub fn get_class_info(&self, type_id: TypeId) -> &ClassInfo {
+ self.class_info_map.get(&type_id).unwrap()
+ }
+
+ pub fn register<T: StructSerializer>(&mut self, class_info: ClassInfo, id:
u32) {
+ fn serializer<T2: 'static + StructSerializer>(this: &dyn Any, context:
&mut WriteContext) {
+ let this = this.downcast_ref::<T2>();
+ match this {
+ Some(v) => {
+ T2::serialize(v, context);
+ }
+ None => todo!(),
+ }
+ }
+
+ fn deserializer<T2: 'static + StructSerializer>(
+ context: &mut ReadContext,
+ ) -> Result<Box<dyn Any>, Error> {
+ match T2::deserialize(context) {
+ Ok(v) => Ok(Box::new(v)),
+ Err(e) => Err(e),
+ }
+ }
+ self.type_id_map.insert(TypeId::of::<T>(), id);
+ self.serialize_map
+ .insert(id, Harness::new(serializer::<T>, deserializer::<T>));
+ self.class_info_map.insert(TypeId::of::<T>(), class_info);
+ }
+
+ pub fn get_harness_by_type(&self, type_id: TypeId) -> Option<&Harness> {
+ self.get_harness(*self.type_id_map.get(&type_id).unwrap())
+ }
+
+ pub fn get_harness(&self, id: u32) -> Option<&Harness> {
+ self.serialize_map.get(&id)
+ }
+}
diff --git a/rust/fury-core/src/resolver/context.rs
b/rust/fury-core/src/resolver/context.rs
index ab24ed41..635d161d 100644
--- a/rust/fury-core/src/resolver/context.rs
+++ b/rust/fury-core/src/resolver/context.rs
@@ -41,8 +41,8 @@ impl<'se> WriteContext<'se> {
}
}
- pub fn push_meta(&mut self, type_id: TypeId, type_def: &'static [u8]) ->
usize {
- self.meta_resolver.push(type_id, type_def)
+ pub fn push_meta(&mut self, type_id: TypeId) -> usize {
+ self.meta_resolver.push(type_id, self.fury)
}
pub fn write_meta(&mut self, offset: usize) {
diff --git a/rust/fury-core/src/resolver/meta_resolver.rs
b/rust/fury-core/src/resolver/meta_resolver.rs
index d39eea6e..b9a442c5 100644
--- a/rust/fury-core/src/resolver/meta_resolver.rs
+++ b/rust/fury-core/src/resolver/meta_resolver.rs
@@ -17,6 +17,7 @@
use crate::buffer::{Reader, Writer};
use crate::error::Error;
+use crate::fury::Fury;
use crate::meta::TypeMeta;
use std::any::TypeId;
use std::collections::HashMap;
@@ -44,17 +45,21 @@ impl MetaReaderResolver {
#[derive(Default)]
pub struct MetaWriterResolver<'a> {
- type_defs: Vec<&'a [u8]>,
+ type_defs: Vec<&'a Vec<u8>>,
type_id_index_map: HashMap<TypeId, usize>,
}
#[allow(dead_code)]
impl<'a> MetaWriterResolver<'a> {
- pub fn push<'b: 'a>(&mut self, type_id: TypeId, type_meta_bytes: &'b [u8])
-> usize {
+ pub fn push<'b: 'a>(&mut self, type_id: TypeId, fury: &'a Fury) -> usize {
match self.type_id_index_map.get(&type_id) {
None => {
let index = self.type_defs.len();
- self.type_defs.push(type_meta_bytes);
+ self.type_defs.push(
+ fury.get_class_resolver()
+ .get_class_info(type_id)
+ .get_type_def(),
+ );
self.type_id_index_map.insert(type_id, index);
index
}
diff --git a/rust/fury-core/src/resolver/mod.rs
b/rust/fury-core/src/resolver/mod.rs
index ac511400..49b3890d 100644
--- a/rust/fury-core/src/resolver/mod.rs
+++ b/rust/fury-core/src/resolver/mod.rs
@@ -15,5 +15,6 @@
// specific language governing permissions and limitations
// under the License.
+pub mod class_resolver;
pub mod context;
pub mod meta_resolver;
diff --git a/rust/fury-core/src/serializer/any.rs
b/rust/fury-core/src/serializer/any.rs
new file mode 100644
index 00000000..f05c435c
--- /dev/null
+++ b/rust/fury-core/src/serializer/any.rs
@@ -0,0 +1,87 @@
+// 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::error::Error;
+use crate::fury::Fury;
+use crate::resolver::context::{ReadContext, WriteContext};
+use crate::serializer::Serializer;
+use crate::types::{FieldType, Mode, RefFlag};
+use std::any::Any;
+
+impl Serializer for Box<dyn Any> {
+ fn reserved_space() -> usize {
+ 0
+ }
+
+ fn write(&self, _context: &mut WriteContext) {
+ panic!("unreachable")
+ }
+
+ fn read(_context: &mut ReadContext) -> Result<Self, Error> {
+ panic!("unreachable")
+ }
+
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::FuryTypeTag.into()
+ }
+
+ fn serialize(&self, context: &mut WriteContext) {
+ context
+ .get_fury()
+ .get_class_resolver()
+ .get_harness_by_type(self.as_ref().type_id())
+ .unwrap()
+ .get_serializer()(self.as_ref(), context);
+ }
+
+ fn deserialize(context: &mut ReadContext) -> Result<Self, Error> {
+ let reset_cursor = context.reader.reset_cursor_to_here();
+ // ref flag
+ let ref_flag = context.reader.i8();
+
+ if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
+ if context.get_fury().get_mode().eq(&Mode::Compatible) {
+ let meta_index = context.reader.i16();
+ let type_id = context.meta_resolver.get(meta_index as
usize).get_type_id();
+ reset_cursor(&mut context.reader);
+ context
+ .get_fury()
+ .get_class_resolver()
+ .get_harness(type_id)
+ .unwrap()
+ .get_deserializer()(context)
+ } else {
+ let type_id = context.reader.i16();
+ reset_cursor(&mut context.reader);
+ context
+ .get_fury()
+ .get_class_resolver()
+ .get_harness(type_id as u32)
+ .unwrap()
+ .get_deserializer()(context)
+ }
+ } else if ref_flag == (RefFlag::Null as i8) {
+ Err(Error::Null)
+ } else if ref_flag == (RefFlag::Ref as i8) {
+ reset_cursor(&mut context.reader);
+ Err(Error::Ref)
+ } else {
+ reset_cursor(&mut context.reader);
+ Err(Error::BadRefFlag)
+ }
+ }
+}
diff --git a/rust/fury-core/src/serializer/bool.rs
b/rust/fury-core/src/serializer/bool.rs
index e3e631d3..b3a98a61 100644
--- a/rust/fury-core/src/serializer/bool.rs
+++ b/rust/fury-core/src/serializer/bool.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -35,7 +36,7 @@ impl Serializer for bool {
Ok(context.reader.u8() == 1)
}
- fn ty() -> FieldType {
- FieldType::BOOL
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::BOOL.into()
}
}
diff --git a/rust/fury-core/src/serializer/datetime.rs
b/rust/fury-core/src/serializer/datetime.rs
index a1f42d72..f6bfa736 100644
--- a/rust/fury-core/src/serializer/datetime.rs
+++ b/rust/fury-core/src/serializer/datetime.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -42,8 +43,8 @@ impl Serializer for NaiveDateTime {
mem::size_of::<u64>()
}
- fn ty() -> FieldType {
- FieldType::TIMESTAMP
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::TIMESTAMP.into()
}
}
@@ -67,8 +68,8 @@ impl Serializer for NaiveDate {
}
}
- fn ty() -> FieldType {
- FieldType::DATE
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::DATE.into()
}
}
diff --git a/rust/fury-core/src/serializer/list.rs
b/rust/fury-core/src/serializer/list.rs
index 233fe93f..0b147f43 100644
--- a/rust/fury-core/src/serializer/list.rs
+++ b/rust/fury-core/src/serializer/list.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -52,8 +53,8 @@ where
mem::size_of::<u32>()
}
- fn ty() -> FieldType {
- FieldType::ARRAY
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::ARRAY.into()
}
}
diff --git a/rust/fury-core/src/serializer/map.rs
b/rust/fury-core/src/serializer/map.rs
index d324a582..e79740f4 100644
--- a/rust/fury-core/src/serializer/map.rs
+++ b/rust/fury-core/src/serializer/map.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -58,8 +59,8 @@ impl<T1: Serializer + Eq + std::hash::Hash, T2: Serializer>
Serializer for HashM
mem::size_of::<i32>()
}
- fn ty() -> FieldType {
- FieldType::MAP
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::MAP.into()
}
}
diff --git a/rust/fury-core/src/serializer/mod.rs
b/rust/fury-core/src/serializer/mod.rs
index 8a92c67b..1803bdfb 100644
--- a/rust/fury-core/src/serializer/mod.rs
+++ b/rust/fury-core/src/serializer/mod.rs
@@ -16,9 +16,11 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::{ReadContext, WriteContext};
-use crate::types::{FieldType, RefFlag};
+use crate::types::RefFlag;
+mod any;
mod bool;
mod datetime;
mod list;
@@ -33,7 +35,7 @@ pub fn serialize<T: Serializer>(this: &T, context: &mut
WriteContext) {
// ref flag
context.writer.i8(RefFlag::NotNullValue as i8);
// type
- context.writer.i16(T::ty() as i16);
+ context.writer.i16(T::get_type_id(context.get_fury()));
this.write(context);
}
@@ -42,13 +44,12 @@ pub fn deserialize<T: Serializer>(context: &mut
ReadContext) -> Result<T, Error>
let ref_flag = context.reader.i8();
if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
- // type_id
- let type_id = context.reader.i16();
- let ty = T::ty();
- if type_id != ty as i16 {
+ let actual_type_id = context.reader.i16();
+ let expected_type_id = T::get_type_id(context.get_fury());
+ if actual_type_id != expected_type_id {
Err(Error::FieldType {
- expected: ty,
- actial: type_id,
+ expected: expected_type_id,
+ actual: actual_type_id,
})
} else {
Ok(T::read(context)?)
@@ -64,7 +65,7 @@ pub fn deserialize<T: Serializer>(context: &mut ReadContext)
-> Result<T, Error>
pub trait Serializer
where
- Self: Default,
+ Self: Sized,
{
/// The fixed memory size of the Type.
/// Avoid the memory check, which would hurt performance.
@@ -87,5 +88,9 @@ where
deserialize(context)
}
- fn ty() -> FieldType;
+ fn get_type_id(_fury: &Fury) -> i16;
+}
+
+pub trait StructSerializer: Serializer + 'static {
+ fn type_def(fury: &Fury) -> Vec<u8>;
}
diff --git a/rust/fury-core/src/serializer/number.rs
b/rust/fury-core/src/serializer/number.rs
index 4412007e..17a79928 100644
--- a/rust/fury-core/src/serializer/number.rs
+++ b/rust/fury-core/src/serializer/number.rs
@@ -16,17 +16,12 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
use crate::types::{FieldType, FuryGeneralList};
-#[allow(dead_code)]
-fn to_u8_slice<T>(slice: &[T]) -> &[u8] {
- let byte_len = std::mem::size_of_val(slice);
- unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<u8>(), byte_len)
}
-}
-
macro_rules! impl_num_serializer {
($name: ident, $ty:tt, $field_type: expr) => {
impl Serializer for $ty {
@@ -42,8 +37,8 @@ macro_rules! impl_num_serializer {
std::mem::size_of::<$ty>()
}
- fn ty() -> FieldType {
- $field_type
+ fn get_type_id(_fury: &Fury) -> i16 {
+ ($field_type).into()
}
}
};
diff --git a/rust/fury-core/src/serializer/option.rs
b/rust/fury-core/src/serializer/option.rs
index 023297f5..dfd607bf 100644
--- a/rust/fury-core/src/serializer/option.rs
+++ b/rust/fury-core/src/serializer/option.rs
@@ -16,10 +16,11 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
-use crate::types::{FieldType, FuryGeneralList, RefFlag};
+use crate::types::{FuryGeneralList, RefFlag};
impl<T: Serializer> Serializer for Option<T> {
fn read(context: &mut ReadContext) -> Result<Self, Error> {
@@ -32,12 +33,12 @@ impl<T: Serializer> Serializer for Option<T> {
if ref_flag == (RefFlag::NotNullValue as i8) || ref_flag ==
(RefFlag::RefValue as i8) {
// type_id
- let type_id = context.reader.i16();
-
- if type_id != T::ty() as i16 {
+ let actual_type_id = context.reader.i16();
+ let expected_type_id = T::get_type_id(context.get_fury());
+ if actual_type_id != expected_type_id {
Err(Error::FieldType {
- expected: T::ty(),
- actial: type_id,
+ expected: expected_type_id,
+ actual: actual_type_id,
})
} else {
Ok(Some(T::read(context)?))
@@ -65,7 +66,7 @@ impl<T: Serializer> Serializer for Option<T> {
// ref flag
context.writer.i8(RefFlag::NotNullValue as i8);
// type
- context.writer.i16(T::ty() as i16);
+ context.writer.i16(T::get_type_id(context.get_fury()));
v.write(context);
}
@@ -79,8 +80,8 @@ impl<T: Serializer> Serializer for Option<T> {
std::mem::size_of::<T>()
}
- fn ty() -> FieldType {
- T::ty()
+ fn get_type_id(fury: &Fury) -> i16 {
+ T::get_type_id(fury)
}
}
diff --git a/rust/fury-core/src/serializer/primitive_list.rs
b/rust/fury-core/src/serializer/primitive_list.rs
index b033bb42..cac36f1c 100644
--- a/rust/fury-core/src/serializer/primitive_list.rs
+++ b/rust/fury-core/src/serializer/primitive_list.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -27,11 +28,6 @@ pub fn to_u8_slice<T>(slice: &[T]) -> &[u8] {
unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<u8>(), byte_len)
}
}
-fn from_u8_slice<T: Clone>(slice: &[u8]) -> Vec<T> {
- let byte_len = slice.len() / mem::size_of::<T>();
- unsafe { std::slice::from_raw_parts(slice.as_ptr().cast::<T>(), byte_len)
}.to_vec()
-}
-
macro_rules! impl_primitive_vec {
($name: ident, $ty:tt, $field_type: expr) => {
impl Serializer for Vec<$ty> {
@@ -43,16 +39,29 @@ macro_rules! impl_primitive_vec {
fn read(context: &mut ReadContext) -> Result<Self, Error> {
// length
- let len = (context.reader.var_int32() as usize) *
mem::size_of::<$ty>();
- Ok(from_u8_slice::<$ty>(context.reader.bytes(len as usize)))
+ let len = (context.reader.var_int32() as usize);
+ let is_aligned = context.reader.aligned::<$ty>();
+ if is_aligned {
+ let slice = context.reader.bytes(len *
mem::size_of::<$ty>());
+ Ok(
+ unsafe {
std::slice::from_raw_parts(slice.as_ptr().cast::<$ty>(), len) }
+ .to_vec(),
+ )
+ } else {
+ let mut result = Vec::with_capacity(len);
+ for _i in 0..len {
+ result.push(context.reader.$name());
+ }
+ Ok(result)
+ }
}
fn reserved_space() -> usize {
mem::size_of::<i32>()
}
- fn ty() -> FieldType {
- $field_type
+ fn get_type_id(_fury: &Fury) -> i16 {
+ ($field_type).into()
}
}
};
@@ -68,8 +77,8 @@ impl Serializer for Vec<bool> {
mem::size_of::<u8>()
}
- fn ty() -> FieldType {
- FieldType::FuryPrimitiveBoolArray
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::FuryPrimitiveBoolArray.into()
}
fn read(context: &mut ReadContext) -> Result<Self, Error> {
diff --git a/rust/fury-core/src/serializer/set.rs
b/rust/fury-core/src/serializer/set.rs
index a652dbd3..4b8b84db 100644
--- a/rust/fury-core/src/serializer/set.rs
+++ b/rust/fury-core/src/serializer/set.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -53,8 +54,8 @@ impl<T: Serializer + Eq + std::hash::Hash> Serializer for
HashSet<T> {
mem::size_of::<i32>()
}
- fn ty() -> FieldType {
- FieldType::FurySet
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::FurySet.into()
}
}
diff --git a/rust/fury-core/src/serializer/string.rs
b/rust/fury-core/src/serializer/string.rs
index 64190fa2..7f2c1bf3 100644
--- a/rust/fury-core/src/serializer/string.rs
+++ b/rust/fury-core/src/serializer/string.rs
@@ -16,6 +16,7 @@
// under the License.
use crate::error::Error;
+use crate::fury::Fury;
use crate::resolver::context::ReadContext;
use crate::resolver::context::WriteContext;
use crate::serializer::Serializer;
@@ -37,8 +38,8 @@ impl Serializer for String {
Ok(context.reader.string(len as usize))
}
- fn ty() -> FieldType {
- FieldType::STRING
+ fn get_type_id(_fury: &Fury) -> i16 {
+ FieldType::STRING.into()
}
}
diff --git a/rust/fury-core/src/types.rs b/rust/fury-core/src/types.rs
index c1ba2b1f..12e43af1 100644
--- a/rust/fury-core/src/types.rs
+++ b/rust/fury-core/src/types.rs
@@ -16,7 +16,7 @@
// under the License.
use crate::error::Error;
-use num_enum::TryFromPrimitive;
+use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::mem;
#[allow(dead_code)]
@@ -38,7 +38,7 @@ pub enum RefFlag {
RefValue = 0,
}
-#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)]
+#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive)]
#[repr(i16)]
pub enum FieldType {
BOOL = 1,
@@ -124,8 +124,6 @@ pub fn compute_struct_hash(props: Vec<(&str, FieldType)>)
-> u32 {
hash
}
-// todo: flag check
-#[allow(dead_code)]
pub mod config_flags {
pub const IS_NULL_FLAG: u8 = 1 << 0;
pub const IS_LITTLE_ENDIAN_FLAG: u8 = 2;
diff --git a/rust/fury-derive/src/lib.rs b/rust/fury-derive/src/lib.rs
index a9f2605b..0d8ed47e 100644
--- a/rust/fury-derive/src/lib.rs
+++ b/rust/fury-derive/src/lib.rs
@@ -23,22 +23,10 @@ mod fury_row;
mod object;
mod util;
-#[proc_macro_derive(Fury, attributes(tag))]
+#[proc_macro_derive(Fury)]
pub fn proc_macro_derive_fury_object(input: proc_macro::TokenStream) ->
TokenStream {
let input = parse_macro_input!(input as DeriveInput);
- let tag = input
- .attrs
- .iter()
- .find(|attr| attr.path().is_ident("tag"))
- .expect("should have tag");
- let expr: syn::ExprLit = tag.parse_args().expect("should tag contain
string value");
- let tag = match expr.lit {
- syn::Lit::Str(s) => s.value(),
- _ => {
- panic!("tag should be string")
- }
- };
- object::derive_serializer(&input, &tag)
+ object::derive_serializer(&input)
}
#[proc_macro_derive(FuryRow)]
diff --git a/rust/fury-derive/src/object/misc.rs
b/rust/fury-derive/src/object/misc.rs
index a9416e2d..0f9aac66 100644
--- a/rust/fury-derive/src/object/misc.rs
+++ b/rust/fury-derive/src/object/misc.rs
@@ -24,7 +24,7 @@ fn hash(fields: &[&Field]) -> TokenStream {
let ty = &field.ty;
let name = format!("{}", field.ident.as_ref().expect("should be field
name"));
quote! {
- (#name, <#ty as fury_core::serializer::Serializer>::ty())
+ (#name, <#ty as fury_core::serializer::Serializer>::get_type_id())
}
});
@@ -48,46 +48,31 @@ fn type_def(fields: &[&Field]) -> TokenStream {
let ty = &field.ty;
let name = format!("{}", field.ident.as_ref().expect("should be field
name"));
quote! {
- fury_core::meta::FieldInfo::new(#name, <#ty as
fury_core::serializer::Serializer>::ty())
+ fury_core::meta::FieldInfo::new(#name, <#ty as
fury_core::serializer::Serializer>::get_type_id(fury))
}
});
quote! {
- fn fury_type_def() -> &'static [u8] {
- use std::sync::Once;
- static mut type_definition: Vec<u8> = Vec::new();
- static type_definition_once: Once = Once::new();
- unsafe {
- type_definition_once.call_once(|| {
- type_definition = fury_core::meta::TypeMeta::from_fields(
- 0,
- vec![#(#field_infos),*]
- ).to_bytes().unwrap();
- });
- type_definition.as_slice()
- }
+ fn type_def(fury: &fury_core::fury::Fury) -> Vec<u8> {
+ fury_core::meta::TypeMeta::from_fields(
+ 0,
+ vec![#(#field_infos),*]
+ ).to_bytes().unwrap()
}
}
}
-pub fn gen_in_struct_impl(fields: &[&Field], tag: &String) -> TokenStream {
- let hash_token_stream = hash(fields);
+pub fn gen_in_struct_impl(fields: &[&Field]) -> TokenStream {
+ let _hash_token_stream = hash(fields);
let type_def_token_stream = type_def(fields);
quote! {
- #hash_token_stream
-
#type_def_token_stream
-
- fn fury_tag() -> &'static str {
- #tag
- }
}
}
-
pub fn gen() -> TokenStream {
quote! {
- fn ty() -> fury_core::types::FieldType {
- fury_core::types::FieldType::FuryTypeTag
+ fn get_type_id(fury: &fury_core::fury::Fury) -> i16 {
+
fury.get_class_resolver().get_class_info(std::any::TypeId::of::<Self>()).get_type_id()
as i16
}
}
}
diff --git a/rust/fury-derive/src/object/read.rs
b/rust/fury-derive/src/object/read.rs
index de7176af..7249609a 100644
--- a/rust/fury-derive/src/object/read.rs
+++ b/rust/fury-derive/src/object/read.rs
@@ -16,69 +16,87 @@
// under the License.
use proc_macro2::{Ident, TokenStream};
-use quote::quote;
+use quote::{format_ident, quote};
use syn::Field;
-fn read(name: &Ident, fields: &[&Field]) -> TokenStream {
+fn create_private_field_name(field: &Field) -> Ident {
+ format_ident!("_{}", field.ident.as_ref().expect(""))
+}
+
+fn bind(fields: &[&Field]) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let ty = &field.ty;
+ let var_name = create_private_field_name(field);
+ quote! {
+ let mut #var_name: Option<#ty> = None;
+ }
+ })
+ .collect()
+}
+
+fn create(fields: &[&Field]) -> Vec<TokenStream> {
+ fields
+ .iter()
+ .map(|field| {
+ let name = &field.ident;
+ let var_name = create_private_field_name(field);
+ quote! {
+ #name: #var_name.unwrap()
+ }
+ })
+ .collect()
+}
+
+fn read(fields: &[&Field]) -> TokenStream {
let assign_stmt = fields.iter().map(|field| {
let ty = &field.ty;
- let ident = &field.ident;
+ let name = &field.ident;
quote! {
- #ident: <#ty as
fury_core::serializer::Serializer>::deserialize(context)?
+ #name: <#ty as
fury_core::serializer::Serializer>::deserialize(context)?
}
});
+
quote! {
fn read(context: &mut fury_core::resolver::context::ReadContext) ->
Result<Self, fury_core::error::Error> {
- // read tag string
- context.read_tag()?;
- // read tag hash
- let hash = context.reader.u32();
- let expected = #name::fury_hash();
- if(hash != expected) {
- Err(fury_core::error::Error::StructHash{ expected, actial:
hash })
- } else {
- Ok(Self {
- #(#assign_stmt),*
- })
- }
+ Ok(Self {
+ #(#assign_stmt),*
+ })
}
}
}
-fn deserialize_compatible(name: &Ident, fields: &[&Field]) -> TokenStream {
+fn deserialize_compatible(fields: &[&Field]) -> TokenStream {
let pattern_item = fields.iter().enumerate().map(|(index, field)| {
let ty = &field.ty;
- let name = &field.ident;
+ let var_name = create_private_field_name(field);
quote! {
#index => {
- result.#name = <#ty as
fury_core::serializer::Serializer>::deserialize(context)?
+ #var_name = Some(<#ty as
fury_core::serializer::Serializer>::deserialize(context)?);
}
}
});
+ let bind: Vec<TokenStream> = bind(fields);
+ let create: Vec<TokenStream> = create(fields);
quote! {
let ref_flag = context.reader.i8();
if ref_flag == (fury_core::types::RefFlag::NotNullValue as i8) ||
ref_flag == (fury_core::types::RefFlag::RefValue as i8) {
- let mut result = Self::default();
let meta_index = context.reader.i16() as usize;
let meta = context.get_meta(meta_index).clone();
let fields = meta.get_field_info();
- // read tag string
- context.read_tag()?;
- // read tag hash
- let hash = context.reader.u32();
- let expected = #name::fury_hash();
- if(hash != expected) {
- return Err(fury_core::error::Error::StructHash{ expected,
actial: hash })
- }
+ #(#bind)*
for (idx, _field_info) in fields.iter().enumerate() {
match idx {
#(#pattern_item),*
_ => {
- panic!("not implement yet")
+ panic!("not implement yet");
}
}
}
- Ok(result)
+ Ok(Self {
+ #(#create),*
+ })
} else if ref_flag == (fury_core::types::RefFlag::Null as i8) {
Err(fury_core::error::Error::Null)
} else if ref_flag == (fury_core::types::RefFlag::Ref as i8) {
@@ -89,9 +107,10 @@ fn deserialize_compatible(name: &Ident, fields: &[&Field])
-> TokenStream {
}
}
-pub fn gen(name: &Ident, fields: &[&Field]) -> TokenStream {
- let read_token_stream = read(name, fields);
- let compatible_token_stream = deserialize_compatible(name, fields);
+pub fn gen(fields: &[&Field]) -> TokenStream {
+ let read_token_stream = read(fields);
+ let compatible_token_stream = deserialize_compatible(fields);
+
quote! {
fn deserialize(context: &mut
fury_core::resolver::context::ReadContext) -> Result<Self,
fury_core::error::Error> {
match context.get_fury().get_mode() {
diff --git a/rust/fury-derive/src/object/serializer.rs
b/rust/fury-derive/src/object/serializer.rs
index c8cbd721..c87ad0dd 100644
--- a/rust/fury-derive/src/object/serializer.rs
+++ b/rust/fury-derive/src/object/serializer.rs
@@ -20,7 +20,7 @@ use crate::util::sorted_fields;
use proc_macro::TokenStream;
use quote::quote;
-pub fn derive_serializer(ast: &syn::DeriveInput, tag: &String) -> TokenStream {
+pub fn derive_serializer(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let fields = match &ast.data {
syn::Data::Struct(s) => sorted_fields(&s.fields),
@@ -30,12 +30,12 @@ pub fn derive_serializer(ast: &syn::DeriveInput, tag:
&String) -> TokenStream {
};
let misc_token_stream = misc::gen();
- let struct_impl_token_stream = misc::gen_in_struct_impl(&fields, tag);
- let write_token_stream = write::gen(name, &fields);
- let read_token_stream = read::gen(name, &fields);
+ let struct_impl_token_stream = misc::gen_in_struct_impl(&fields);
+ let write_token_stream = write::gen(&fields);
+ let read_token_stream = read::gen(&fields);
let gen = quote! {
- impl #name {
+ impl fury_core::serializer::StructSerializer for #name {
#struct_impl_token_stream
}
impl fury_core::types::FuryGeneralList for #name {}
diff --git a/rust/fury-derive/src/object/write.rs
b/rust/fury-derive/src/object/write.rs
index 83236be7..1a3bd2f5 100644
--- a/rust/fury-derive/src/object/write.rs
+++ b/rust/fury-derive/src/object/write.rs
@@ -15,11 +15,11 @@
// specific language governing permissions and limitations
// under the License.
-use proc_macro2::{Ident, TokenStream};
+use proc_macro2::TokenStream;
use quote::quote;
use syn::Field;
-pub fn gen(name: &Ident, fields: &[&Field]) -> TokenStream {
+pub fn gen(fields: &[&Field]) -> TokenStream {
let accessor_expr = fields.iter().map(|field| {
let ty = &field.ty;
let ident = &field.ident;
@@ -36,8 +36,6 @@ pub fn gen(name: &Ident, fields: &[&Field]) -> TokenStream {
}
});
- let tag_byte_len = format!("{}", name).len();
-
quote! {
fn serialize(&self, context: &mut
fury_core::resolver::context::WriteContext) {
match context.get_fury().get_mode() {
@@ -47,8 +45,7 @@ pub fn gen(name: &Ident, fields: &[&Field]) -> TokenStream {
fury_core::types::Mode::Compatible => {
context.writer.i8(fury_core::types::RefFlag::NotNullValue
as i8);
let meta_index = context.push_meta(
- std::any::TypeId::of::<Self>(),
- #name::fury_type_def()
+ std::any::TypeId::of::<Self>()
) as i16;
context.writer.i16(meta_index);
self.write(context);
@@ -58,17 +55,12 @@ pub fn gen(name: &Ident, fields: &[&Field]) -> TokenStream {
fn write(&self, context: &mut
fury_core::resolver::context::WriteContext) {
- // write tag string
- context.write_tag(#name::fury_tag());
- // write tag hash
- context.writer.u32(#name::fury_hash());
// write fields
#(#accessor_expr)*
}
fn reserved_space() -> usize {
- // struct have four byte hash
- #tag_byte_len + 4 + #(#reserved_size_expr)+*
+ #(#reserved_size_expr)+*
}
}
}
diff --git a/rust/tests/tests/test_complex_struct.rs
b/rust/tests/tests/test_complex_struct.rs
index 4eea7a9e..0406d170 100644
--- a/rust/tests/tests/test_complex_struct.rs
+++ b/rust/tests/tests/test_complex_struct.rs
@@ -19,18 +19,43 @@ use chrono::{DateTime, NaiveDate, NaiveDateTime};
use fury_core::fury::Fury;
use fury_core::types::Mode;
use fury_derive::Fury;
+use std::any::Any;
use std::collections::HashMap;
+#[test]
+fn any() {
+ #[derive(Fury, Debug)]
+ struct Animal {
+ f3: String,
+ }
+
+ #[derive(Fury, Debug)]
+ struct Person {
+ f1: Box<dyn Any>,
+ }
+
+ let person = Person {
+ f1: Box::new(Animal {
+ f3: String::from("hello"),
+ }),
+ };
+
+ let mut fury = Fury::default();
+ fury.register::<Animal>(999);
+ fury.register::<Person>(1000);
+ let bin = fury.serialize(&person);
+ let obj: Person = fury.deserialize(&bin).expect("");
+ assert!(obj.f1.is::<Animal>())
+}
+
#[test]
fn complex_struct() {
#[derive(Fury, Debug, PartialEq, Default)]
- #[tag("example.foo2")]
struct Animal {
category: String,
}
#[derive(Fury, Debug, PartialEq, Default)]
- #[tag("example.foo")]
struct Person {
c1: Vec<u8>, // binary
c2: Vec<i16>, // primitive array
@@ -66,7 +91,10 @@ fn complex_struct() {
c5: 2.0,
c6: 4.0,
};
- let fury = Fury::default().mode(Mode::Compatible);
+ let mut fury = Fury::default().mode(Mode::Compatible);
+ fury.register::<Person>(999);
+ fury.register::<Animal>(899);
+
let bin: Vec<u8> = fury.serialize(&person);
let obj: Person = fury.deserialize(&bin).expect("should success");
assert_eq!(person, obj);
@@ -75,13 +103,11 @@ fn complex_struct() {
#[test]
fn encode_to_obin() {
#[derive(Fury, Debug, PartialEq, Default)]
- #[tag("example.foo2")]
struct Animal {
category: String,
}
#[derive(Fury, Debug, PartialEq, Default)]
- #[tag("example.ComplexObject")]
struct Person {
f1: String,
f2: HashMap<String, i8>,
@@ -93,7 +119,9 @@ fn encode_to_obin() {
f8: f64,
f10: HashMap<i32, f64>,
}
- let fury = Fury::default();
+ let mut fury = Fury::default();
+ fury.register::<Person>(999);
+ fury.register::<Animal>(899);
let bin: Vec<u8> = fury.serialize(&Person {
f1: "Hello".to_string(),
f2: HashMap::from([("hello1".to_string(), 1), ("hello2".to_string(),
2)]),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]