Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class RustXlangTest extends ForyTestBase {
@BeforeClass
public void isRustJavaCIEnabled() {
String enabled = System.getenv("FORY_RUST_JAVA_CI");
enabled = null;
if (enabled == null || !enabled.equals("1")) {
throw new SkipException("Skipping RustXlangTest: FORY_RUST_JAVA_CI not set to 1");
}
Expand Down
55 changes: 45 additions & 10 deletions rust/fory-core/src/fory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
use crate::buffer::{Reader, Writer};
use crate::ensure;
use crate::error::Error;
use crate::resolver::context::WriteContext;
use crate::resolver::context::{Pool, ReadContext};
use crate::resolver::context::{Pool, ReadContext, WriteContext};
use crate::resolver::type_resolver::TypeResolver;
use crate::serializer::ForyDefault;
use crate::serializer::{Serializer, StructSerializer};
Expand Down Expand Up @@ -79,6 +78,7 @@ pub struct Fory {
xlang: bool,
share_meta: bool,
type_resolver: TypeResolver,
compress_int: bool,
compress_string: bool,
max_dyn_depth: u32,
check_struct_version: bool,
Expand All @@ -94,6 +94,7 @@ impl Default for Fory {
xlang: false,
share_meta: false,
type_resolver: TypeResolver::default(),
compress_int: true,
compress_string: false,
max_dyn_depth: 5,
check_struct_version: false,
Expand Down Expand Up @@ -179,6 +180,40 @@ impl Fory {
self
}

/// Enables or disables integer compression.
///
/// # Arguments
///
/// * `compress_int` - If `true`, enables compact integer encoding to reduce serialized
/// payload size by using variable-length integer encoding. If `false`, integers are
/// serialized using fixed-width representation.
///
/// # Returns
///
/// Returns `self` for method chaining.
///
/// # Default
///
/// The default value is `false`.
///
/// # Trade-offs
///
/// - **Enabled**: Smaller payload size, slightly higher CPU overhead
/// - **Disabled**: Larger payload size, faster serialization/deserialization
///
/// # Examples
///
/// ```rust
/// use fory_core::Fory;
///
/// let fory = Fory::default().compress_int(true);
/// ```
pub fn compress_int(mut self, compress_int: bool) -> Self {
self.compress_int = compress_int;
self.type_resolver.set_compress_int(compress_int);
self
}

/// Enables or disables meta string compression.
///
/// # Arguments
Expand Down Expand Up @@ -523,7 +558,6 @@ impl Fory {
let type_resolver = self.type_resolver.build_final_type_resolver()?;
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;

Expand All @@ -532,7 +566,6 @@ impl Fory {
type_resolver.clone(),
compatible,
share_meta,
compress_string,
xlang,
check_struct_version,
))
Expand All @@ -552,7 +585,7 @@ impl Fory {
context: &mut WriteContext,
) -> Result<(), Error> {
let is_none = record.fory_is_none();
self.write_head::<T>(is_none, &mut context.writer);
self.write_head::<T>(is_none, context);
let meta_start_offset = context.writer.len();
if !is_none {
if context.is_compatible() {
Expand Down Expand Up @@ -763,11 +796,13 @@ impl Fory {

/// Writes the serialization header to the writer.
#[inline(always)]
pub fn write_head<T: Serializer>(&self, is_none: bool, writer: &mut Writer) {
pub fn write_head<T: Serializer>(&self, is_none: bool, context: &mut WriteContext) {
const HEAD_SIZE: usize = 10;
writer.reserve(T::fory_reserved_space() + SIZE_OF_REF_AND_TYPE + HEAD_SIZE);
context.writer.reserve(
T::fory_reserved_space(context.get_type_resolver()) + SIZE_OF_REF_AND_TYPE + HEAD_SIZE,
);
if self.xlang {
writer.write_u16(MAGIC_NUMBER);
context.writer.write_u16(MAGIC_NUMBER);
}
#[cfg(target_endian = "big")]
let mut bitmap = 0;
Expand All @@ -779,12 +814,12 @@ impl Fory {
if is_none {
bitmap |= IS_NULL_FLAG;
}
writer.write_u8(bitmap);
context.writer.write_u8(bitmap);
if is_none {
return;
}
if self.xlang {
writer.write_u8(Language::Rust as u8);
context.writer.write_u8(Language::Rust as u8);
}
}

Expand Down
9 changes: 0 additions & 9 deletions rust/fory-core/src/resolver/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub struct WriteContext<'a> {
type_resolver: TypeResolver,
compatible: bool,
share_meta: bool,
compress_string: bool,
xlang: bool,
check_struct_version: bool,

Expand All @@ -54,15 +53,13 @@ impl<'a> WriteContext<'a> {
type_resolver: TypeResolver,
compatible: bool,
share_meta: bool,
compress_string: bool,
xlang: bool,
check_struct_version: bool,
) -> WriteContext<'a> {
WriteContext {
type_resolver,
compatible,
share_meta,
compress_string,
xlang,
check_struct_version,
default_writer: None,
Expand Down Expand Up @@ -113,12 +110,6 @@ impl<'a> WriteContext<'a> {
self.share_meta
}

/// Check if string compression is enabled
#[inline(always)]
pub fn is_compress_string(&self) -> bool {
self.compress_string
}

/// Check if cross-language mode is enabled
#[inline(always)]
pub fn is_xlang(&self) -> bool {
Expand Down
35 changes: 26 additions & 9 deletions rust/fory-core/src/resolver/type_resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ fn build_struct_type_infos<T: StructSerializer>(
let mut result = vec![(std::any::TypeId::of::<T>(), main_type_info)];

// Handle enum variants in compatible mode
if type_resolver.compatible && T::fory_static_type_id() == TypeId::ENUM {
if type_resolver.compatible && T::fory_static_type_id(type_resolver) == TypeId::ENUM {
// Fields are already sorted with IDs assigned by the macro
let variants_info = T::fory_variants_fields_info(type_resolver)?;
for (idx, (variant_name, variant_type_id, fields_info)) in
Expand Down Expand Up @@ -415,6 +415,7 @@ pub struct TypeResolver {
// Fast lookup by numeric ID for common types
type_id_index: Vec<u32>,
compatible: bool,
compress_int: bool,
}

// Safety: TypeResolver instances are only shared through higher-level synchronization that
Expand All @@ -435,13 +436,19 @@ impl Default for TypeResolver {
type_id_index: Vec::new(),
partial_type_infos: HashMap::new(),
compatible: false,
compress_int: true,
};
registry.register_builtin_types().unwrap();
registry
}
}

impl TypeResolver {
#[inline(always)]
pub fn is_compress_int(&self) -> bool {
self.compress_int
}

pub fn get_type_info(&self, type_id: &std::any::TypeId) -> Result<Rc<TypeInfo>, Error> {
self.type_info_map
.get(type_id)
Expand Down Expand Up @@ -544,7 +551,11 @@ impl TypeResolver {
self.register_internal_serializer::<bool>(TypeId::BOOL)?;
self.register_internal_serializer::<i8>(TypeId::INT8)?;
self.register_internal_serializer::<i16>(TypeId::INT16)?;
self.register_internal_serializer::<i32>(TypeId::INT32)?;
if self.compress_int {
self.register_internal_serializer::<i32>(TypeId::VAR_INT32)?;
} else {
self.register_internal_serializer::<i32>(TypeId::INT32)?;
}
self.register_internal_serializer::<i64>(TypeId::INT64)?;
self.register_internal_serializer::<f32>(TypeId::FLOAT32)?;
self.register_internal_serializer::<f64>(TypeId::FLOAT64)?;
Expand Down Expand Up @@ -627,7 +638,7 @@ impl TypeResolver {
None => Err(Error::type_error(format!(
"Cast type to {:?} error when writing: {:?}",
std::any::type_name::<T2>(),
T2::fory_static_type_id()
T2::fory_static_type_id(context.get_type_resolver())
))),
}
}
Expand Down Expand Up @@ -655,7 +666,7 @@ impl TypeResolver {
None => Err(Error::type_error(format!(
"Cast type to {:?} error when writing data: {:?}",
std::any::type_name::<T2>(),
T2::fory_static_type_id()
T2::fory_static_type_id(context.get_type_resolver())
))),
}
}
Expand Down Expand Up @@ -749,7 +760,7 @@ impl TypeResolver {
id: u32,
) -> Result<(), Error> {
let actual_type_id = get_ext_actual_type_id(id, false);
let static_type_id = T::fory_static_type_id();
let static_type_id = T::fory_static_type_id(self);
if static_type_id != TypeId::EXT && static_type_id != TypeId::NAMED_EXT {
return Err(Error::not_allowed(
"register_serializer can only be used for ext and named_ext types",
Expand All @@ -764,7 +775,7 @@ impl TypeResolver {
type_name: &str,
) -> Result<(), Error> {
let actual_type_id = get_ext_actual_type_id(0, true);
let static_type_id = T::fory_static_type_id();
let static_type_id = T::fory_static_type_id(self);
if static_type_id != TypeId::EXT && static_type_id != TypeId::NAMED_EXT {
return Err(Error::not_allowed(
"register_serializer can only be used for ext and named_ext types",
Expand Down Expand Up @@ -809,7 +820,7 @@ impl TypeResolver {
None => Err(Error::type_error(format!(
"Cast type to {:?} error when writing: {:?}",
std::any::type_name::<T2>(),
T2::fory_static_type_id()
T2::fory_static_type_id(context.get_type_resolver())
))),
}
}
Expand Down Expand Up @@ -837,7 +848,7 @@ impl TypeResolver {
None => Err(Error::type_error(format!(
"Cast type to {:?} error when writing data: {:?}",
std::any::type_name::<T2>(),
T2::fory_static_type_id()
T2::fory_static_type_id(context.get_type_resolver())
))),
}
}
Expand Down Expand Up @@ -931,7 +942,7 @@ impl TypeResolver {
rs_type_id
)));
}
let type_id = T::fory_static_type_id();
let type_id = T::fory_static_type_id(self);
if type_id != TypeId::LIST && type_id != TypeId::MAP && type_id != TypeId::SET {
return Err(Error::not_allowed(format!(
"register_generic_trait can only be used for generic trait types: List, Map, Set, but got type {}",
Expand All @@ -945,6 +956,10 @@ impl TypeResolver {
self.compatible = compatible;
}

pub(crate) fn set_compress_int(&mut self, compress_int: bool) {
self.compress_int = compress_int;
}

/// Builds the final TypeResolver by completing all partial type infos created during registration.
///
/// This method processes all types that were registered with lazy initialization enabled.
Expand Down Expand Up @@ -1006,6 +1021,7 @@ impl TypeResolver {
partial_type_infos: HashMap::new(),
type_id_index,
compatible: self.compatible,
compress_int: self.compress_int,
})
}

Expand Down Expand Up @@ -1038,6 +1054,7 @@ impl TypeResolver {
partial_type_infos: HashMap::new(),
type_id_index: self.type_id_index.clone(),
compatible: self.compatible,
compress_int: self.compress_int,
}
}
}
6 changes: 3 additions & 3 deletions rust/fory-core/src/serializer/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ impl Serializer for Box<dyn Any> {
false
}

fn fory_static_type_id() -> TypeId {
fn fory_static_type_id(_: &TypeResolver) -> TypeId {
TypeId::UNKNOWN
}

Expand Down Expand Up @@ -298,7 +298,7 @@ impl Serializer for Rc<dyn Any> {
true
}

fn fory_static_type_id() -> TypeId {
fn fory_static_type_id(_: &TypeResolver) -> TypeId {
TypeId::UNKNOWN
}

Expand Down Expand Up @@ -467,7 +467,7 @@ impl Serializer for Arc<dyn Any> {
true
}

fn fory_static_type_id() -> TypeId {
fn fory_static_type_id(_: &TypeResolver) -> TypeId {
TypeId::UNKNOWN
}

Expand Down
6 changes: 3 additions & 3 deletions rust/fory-core/src/serializer/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> Serializer for Arc<T>
T::fory_read_type_info(context)
}

fn fory_reserved_space() -> usize {
fn fory_reserved_space(_: &TypeResolver) -> usize {
// Arc is a shared ref, so we just need space for the ref tracking
// We don't recursively compute inner type's space to avoid infinite recursion
4
Expand All @@ -118,8 +118,8 @@ impl<T: Serializer + ForyDefault + Send + Sync + 'static> Serializer for Arc<T>
(**self).fory_type_id_dyn(type_resolver)
}

fn fory_static_type_id() -> TypeId {
T::fory_static_type_id()
fn fory_static_type_id(type_resolver: &TypeResolver) -> TypeId {
T::fory_static_type_id(type_resolver)
}

fn as_any(&self) -> &dyn std::any::Any {
Expand Down
Loading
Loading