This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to tag v0.13.2-rc1 in repository https://gitbox.apache.org/repos/asf/fory.git
commit a19ca3d5c31ef6f2ace0ad59033eb970b033d9de Author: Shawn Yang <[email protected]> AuthorDate: Sat Nov 29 17:31:08 2025 +0800 feat(rust): add fory config for rust (#2947) ## Why? <!-- Describe the purpose of this PR. --> ## What does this PR do? add fory config for rust ## Related issues <!-- Is there any related issue? If this PR closes them you say say fix/closes: - #xxxx0 - #xxxx1 - Fixes #xxxx2 --> ## Does this PR introduce any user-facing change? <!-- If any user-facing interface changes, please [open an issue](https://github.com/apache/fory/issues/new/choose) describing the need to do so and update the document if necessary. Delete section if not applicable. --> - [ ] Does this PR introduce any public API change? - [ ] Does this PR introduce any binary protocol compatibility change? ## Benchmark <!-- When the PR has an impact on performance (if you don't know whether the PR will have an impact on performance, you can submit the PR first, and if it will have impact on performance, the code reviewer will explain it), be sure to attach a benchmark data here. Delete section if not applicable. --> --- rust/fory-core/src/config.rs | 93 ++++++++++++++++++++++++++ rust/fory-core/src/fory.rs | 118 +++++++++++++++------------------ rust/fory-core/src/lib.rs | 2 + rust/fory-core/src/resolver/context.rs | 46 ++++--------- 4 files changed, 164 insertions(+), 95 deletions(-) diff --git a/rust/fory-core/src/config.rs b/rust/fory-core/src/config.rs new file mode 100644 index 000000000..696ab8e19 --- /dev/null +++ b/rust/fory-core/src/config.rs @@ -0,0 +1,93 @@ +// 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. + +/// Configuration for Fory serialization. +/// +/// This struct holds all the configuration options that control how Fory +/// serializes and deserializes data. It is shared between the main `Fory` +/// instance and the `WriteContext`/`ReadContext` to ensure consistent behavior. +#[derive(Clone, Debug)] +pub struct Config { + /// Whether compatible mode is enabled for schema evolution support. + pub compatible: bool, + /// Whether cross-language serialization is enabled. + pub xlang: bool, + /// Whether metadata sharing is enabled. + pub share_meta: bool, + /// Whether meta string compression is enabled. + pub compress_string: bool, + /// Maximum depth for nested dynamic object serialization. + pub max_dyn_depth: u32, + /// Whether class version checking is enabled. + pub check_struct_version: bool, +} + +impl Default for Config { + fn default() -> Self { + Config { + compatible: false, + xlang: false, + share_meta: false, + compress_string: false, + max_dyn_depth: 5, + check_struct_version: false, + } + } +} + +impl Config { + /// Creates a new Config with default values. + pub fn new() -> Self { + Self::default() + } + + /// Check if compatible mode is enabled. + #[inline(always)] + pub fn is_compatible(&self) -> bool { + self.compatible + } + + /// Check if cross-language mode is enabled. + #[inline(always)] + pub fn is_xlang(&self) -> bool { + self.xlang + } + + /// Check if meta sharing is enabled. + #[inline(always)] + pub fn is_share_meta(&self) -> bool { + self.share_meta + } + + /// Check if string compression is enabled. + #[inline(always)] + pub fn is_compress_string(&self) -> bool { + self.compress_string + } + + /// Get maximum dynamic depth. + #[inline(always)] + pub fn max_dyn_depth(&self) -> u32 { + self.max_dyn_depth + } + + /// Check if class version checking is enabled. + #[inline(always)] + pub fn is_check_struct_version(&self) -> bool { + self.check_struct_version + } +} diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs index 83b609b67..6816e4d40 100644 --- a/rust/fory-core/src/fory.rs +++ b/rust/fory-core/src/fory.rs @@ -16,6 +16,7 @@ // under the License. use crate::buffer::{Reader, Writer}; +use crate::config::Config; use crate::ensure; use crate::error::Error; use crate::resolver::context::{ContextCache, ReadContext, WriteContext}; @@ -91,13 +92,9 @@ thread_local! { pub struct Fory { /// Unique identifier for this Fory instance, used as key in thread-local context maps. id: u64, - compatible: bool, - xlang: bool, - share_meta: bool, + /// Configuration for serialization behavior. + config: Config, type_resolver: TypeResolver, - compress_string: bool, - max_dyn_depth: u32, - check_struct_version: bool, /// Lazy-initialized final type resolver (thread-safe, one-time initialization). final_type_resolver: OnceLock<Result<TypeResolver, Error>>, } @@ -106,13 +103,8 @@ impl Default for Fory { fn default() -> Self { Fory { id: FORY_ID_COUNTER.fetch_add(1, Ordering::Relaxed), - compatible: false, - xlang: false, - share_meta: false, + config: Config::default(), type_resolver: TypeResolver::default(), - compress_string: false, - max_dyn_depth: 5, - check_struct_version: false, final_type_resolver: OnceLock::new(), } } @@ -148,11 +140,11 @@ impl Fory { /// ``` pub fn compatible(mut self, compatible: bool) -> Self { // Setting share_meta individually is not supported currently - self.share_meta = compatible; - self.compatible = compatible; + self.config.share_meta = compatible; + self.config.compatible = compatible; self.type_resolver.set_compatible(compatible); if compatible { - self.check_struct_version = false; + self.config.check_struct_version = false; } self } @@ -187,9 +179,9 @@ impl Fory { /// let fory = Fory::default().xlang(false); /// ``` pub fn xlang(mut self, xlang: bool) -> Self { - self.xlang = xlang; - if !self.check_struct_version { - self.check_struct_version = !self.compatible; + self.config.xlang = xlang; + if !self.config.check_struct_version { + self.config.check_struct_version = !self.config.compatible; } self } @@ -223,7 +215,7 @@ impl Fory { /// let fory = Fory::default().compress_string(true); /// ``` pub fn compress_string(mut self, compress_string: bool) -> Self { - self.compress_string = compress_string; + self.config.compress_string = compress_string; self } @@ -259,11 +251,11 @@ impl Fory { /// .check_struct_version(true); /// ``` pub fn check_struct_version(mut self, check_struct_version: bool) -> Self { - if self.compatible && check_struct_version { + if self.config.compatible && check_struct_version { // ignore setting if compatible mode is on return self; } - self.check_struct_version = check_struct_version; + self.config.check_struct_version = check_struct_version; self } @@ -300,13 +292,13 @@ impl Fory { /// let fory = Fory::default().max_dyn_depth(3); /// ``` pub fn max_dyn_depth(mut self, max_dyn_depth: u32) -> Self { - self.max_dyn_depth = max_dyn_depth; + self.config.max_dyn_depth = max_dyn_depth; self } /// Returns whether cross-language serialization is enabled. pub fn is_xlang(&self) -> bool { - self.xlang + self.config.xlang } /// Returns the current serialization mode. @@ -315,7 +307,7 @@ impl Fory { /// /// `true` if the serialization mode is compatible, `false` otherwise`. pub fn is_compatible(&self) -> bool { - self.compatible + self.config.compatible } /// Returns whether string compression is enabled. @@ -324,7 +316,7 @@ impl Fory { /// /// `true` if meta string compression is enabled, `false` otherwise. pub fn is_compress_string(&self) -> bool { - self.compress_string + self.config.compress_string } /// Returns whether metadata sharing is enabled. @@ -333,12 +325,12 @@ impl Fory { /// /// `true` if metadata sharing is enabled (automatically set based on mode), `false` otherwise. pub fn is_share_meta(&self) -> bool { - self.share_meta + self.config.share_meta } /// Returns the maximum depth for nested dynamic object serialization. pub fn get_max_dyn_depth(&self) -> u32 { - self.max_dyn_depth + self.config.max_dyn_depth } /// Returns whether class version checking is enabled. @@ -347,7 +339,12 @@ impl Fory { /// /// `true` if class version checking is enabled, `false` otherwise. pub fn is_check_struct_version(&self) -> bool { - self.check_struct_version + self.config.check_struct_version + } + + /// Returns a reference to the configuration. + pub fn config(&self) -> &Config { + &self.config } /// Serializes a value of type `T` into a byte vector. @@ -556,23 +553,12 @@ impl Fory { WRITE_CONTEXTS.with(|cache| { let cache = unsafe { &mut *cache.get() }; let id = self.id; - let compatible = self.compatible; - let share_meta = self.share_meta; - let compress_string = self.compress_string; - let xlang = self.xlang; - let check_struct_version = self.check_struct_version; + let config = self.config.clone(); let context = cache.get_or_insert_result(id, || { // Only fetch type resolver when creating a new context let type_resolver = self.get_final_type_resolver()?; - Ok(Box::new(WriteContext::new( - type_resolver.clone(), - compatible, - share_meta, - compress_string, - xlang, - check_struct_version, - ))) + Ok(Box::new(WriteContext::new(type_resolver.clone(), config))) })?; f(context) }) @@ -584,6 +570,17 @@ impl Fory { &self, record: &T, context: &mut WriteContext, + ) -> Result<(), Error> { + let result = self.serialize_with_context_inner::<T>(record, context); + context.reset(); + result + } + + #[inline(always)] + fn serialize_with_context_inner<T: Serializer>( + &self, + record: &T, + context: &mut WriteContext, ) -> Result<(), Error> { let is_none = record.fory_is_none(); self.write_head::<T>(is_none, &mut context.writer); @@ -597,7 +594,6 @@ impl Fory { context.write_meta(meta_start_offset); } } - context.reset(); Ok(()) } @@ -800,14 +796,14 @@ impl Fory { pub fn write_head<T: Serializer>(&self, is_none: bool, writer: &mut Writer) { const HEAD_SIZE: usize = 10; writer.reserve(T::fory_reserved_space() + SIZE_OF_REF_AND_TYPE + HEAD_SIZE); - if self.xlang { + if self.config.xlang { writer.write_u16(MAGIC_NUMBER); } #[cfg(target_endian = "big")] let mut bitmap = 0; #[cfg(target_endian = "little")] let mut bitmap = IS_LITTLE_ENDIAN_FLAG; - if self.xlang { + if self.config.xlang { bitmap |= IS_CROSS_LANGUAGE_FLAG; } if is_none { @@ -817,7 +813,7 @@ impl Fory { if is_none { return; } - if self.xlang { + if self.config.xlang { writer.write_u8(Language::Rust as u8); } } @@ -858,7 +854,6 @@ impl Fory { /// ``` pub fn deserialize<T: Serializer + ForyDefault>(&self, bf: &[u8]) -> Result<T, Error> { self.with_read_context(|context| { - context.init(self.max_dyn_depth); let outlive_buffer = unsafe { mem::transmute::<&[u8], &[u8]>(bf) }; context.attach_reader(Reader::new(outlive_buffer)); let result = self.deserialize_with_context(context); @@ -919,7 +914,6 @@ impl Fory { reader: &mut Reader, ) -> Result<T, Error> { self.with_read_context(|context| { - context.init(self.max_dyn_depth); let outlive_buffer = unsafe { mem::transmute::<&[u8], &[u8]>(reader.bf) }; let mut new_reader = Reader::new(outlive_buffer); new_reader.set_cursor(reader.cursor); @@ -945,23 +939,12 @@ impl Fory { READ_CONTEXTS.with(|cache| { let cache = unsafe { &mut *cache.get() }; let id = self.id; - let compatible = self.compatible; - let share_meta = self.share_meta; - let xlang = self.xlang; - let max_dyn_depth = self.max_dyn_depth; - let check_struct_version = self.check_struct_version; + let config = self.config.clone(); let context = cache.get_or_insert_result(id, || { // Only fetch type resolver when creating a new context let type_resolver = self.get_final_type_resolver()?; - Ok(Box::new(ReadContext::new( - type_resolver.clone(), - compatible, - share_meta, - xlang, - max_dyn_depth, - check_struct_version, - ))) + Ok(Box::new(ReadContext::new(type_resolver.clone(), config))) })?; f(context) }) @@ -971,6 +954,16 @@ impl Fory { fn deserialize_with_context<T: Serializer + ForyDefault>( &self, context: &mut ReadContext, + ) -> Result<T, Error> { + let result = self.deserialize_with_context_inner::<T>(context); + context.reset(); + result + } + + #[inline(always)] + fn deserialize_with_context_inner<T: Serializer + ForyDefault>( + &self, + context: &mut ReadContext, ) -> Result<T, Error> { let is_none = self.read_head(&mut context.reader)?; if is_none { @@ -988,13 +981,12 @@ impl Fory { context.reader.skip(bytes_to_skip)?; } context.ref_reader.resolve_callbacks(); - context.reset(); result } #[inline(always)] fn read_head(&self, reader: &mut Reader) -> Result<bool, Error> { - if self.xlang { + if self.config.xlang { let magic_numer = reader.read_u16()?; ensure!( magic_numer == MAGIC_NUMBER, @@ -1009,7 +1001,7 @@ impl Fory { let bitmap = reader.read_u8()?; let peer_is_xlang = (bitmap & IS_CROSS_LANGUAGE_FLAG) != 0; ensure!( - self.xlang == peer_is_xlang, + self.config.xlang == peer_is_xlang, Error::invalid_data("header bitmap mismatch at xlang bit") ); let is_little_endian = (bitmap & IS_LITTLE_ENDIAN_FLAG) != 0; diff --git a/rust/fory-core/src/lib.rs b/rust/fory-core/src/lib.rs index c58559b88..a9d611c20 100644 --- a/rust/fory-core/src/lib.rs +++ b/rust/fory-core/src/lib.rs @@ -177,6 +177,7 @@ //! ``` pub mod buffer; +pub mod config; pub mod error; pub mod fory; pub mod meta; @@ -190,6 +191,7 @@ pub mod util; pub use paste; pub use crate::buffer::{Reader, Writer}; +pub use crate::config::Config; pub use crate::error::Error; pub use crate::fory::Fory; pub use crate::resolver::context::{ReadContext, WriteContext}; diff --git a/rust/fory-core/src/resolver/context.rs b/rust/fory-core/src/resolver/context.rs index 8c937278b..615c7ac70 100644 --- a/rust/fory-core/src/resolver/context.rs +++ b/rust/fory-core/src/resolver/context.rs @@ -16,6 +16,7 @@ // under the License. use crate::buffer::{Reader, Writer}; +use crate::config::Config; use std::collections::HashMap; use std::mem; @@ -130,21 +131,14 @@ pub struct WriteContext<'a> { #[allow(clippy::needless_lifetimes)] impl<'a> WriteContext<'a> { - pub fn new( - type_resolver: TypeResolver, - compatible: bool, - share_meta: bool, - compress_string: bool, - xlang: bool, - check_struct_version: bool, - ) -> WriteContext<'a> { + pub fn new(type_resolver: TypeResolver, config: Config) -> WriteContext<'a> { WriteContext { type_resolver, - compatible, - share_meta, - compress_string, - xlang, - check_struct_version, + compatible: config.compatible, + share_meta: config.share_meta, + compress_string: config.compress_string, + xlang: config.xlang, + check_struct_version: config.check_struct_version, default_writer: None, writer: Writer::from_buffer(Self::get_leak_buffer()), meta_resolver: MetaWriterResolver::default(), @@ -334,21 +328,14 @@ unsafe impl<'a> Send for ReadContext<'a> {} unsafe impl<'a> Sync for ReadContext<'a> {} impl<'a> ReadContext<'a> { - pub fn new( - type_resolver: TypeResolver, - compatible: bool, - share_meta: bool, - xlang: bool, - max_dyn_depth: u32, - check_struct_version: bool, - ) -> ReadContext<'a> { + pub fn new(type_resolver: TypeResolver, config: Config) -> ReadContext<'a> { ReadContext { type_resolver, - compatible, - share_meta, - xlang, - max_dyn_depth, - check_struct_version, + compatible: config.compatible, + share_meta: config.share_meta, + xlang: config.xlang, + max_dyn_depth: config.max_dyn_depth, + check_struct_version: config.check_struct_version, reader: Reader::default(), meta_resolver: MetaReaderResolver::default(), meta_string_resolver: MetaStringReaderResolver::default(), @@ -393,12 +380,6 @@ impl<'a> ReadContext<'a> { self.max_dyn_depth } - #[inline(always)] - pub fn init(&mut self, max_dyn_depth: u32) { - self.max_dyn_depth = max_dyn_depth; - self.current_depth = 0; - } - #[inline(always)] pub fn attach_reader(&mut self, reader: Reader<'a>) { self.reader = reader; @@ -495,5 +476,6 @@ impl<'a> ReadContext<'a> { self.meta_resolver.reset(); self.meta_string_resolver.reset(); self.ref_reader.reset(); + self.current_depth = 0; } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
