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 23299d7b0 feat(rust): direct derive primitve write/read (#2890)
23299d7b0 is described below
commit 23299d7b0aae7d9715967c47b38b14165ea24ef3
Author: Shawn Yang <[email protected]>
AuthorDate: Tue Nov 4 13:03:44 2025 +0800
feat(rust): direct derive primitve write/read (#2890)
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
direct derive primitve write/read for smaller code size and beffer
compiler optimization
## 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/buffer.rs | 5 ++++
rust/fory-derive/src/object/read.rs | 16 +++++++++--
rust/fory-derive/src/object/util.rs | 53 ++++++++++++++++++++++++++++++++++++
rust/fory-derive/src/object/write.rs | 24 ++++++++++++++--
4 files changed, 92 insertions(+), 6 deletions(-)
diff --git a/rust/fory-core/src/buffer.rs b/rust/fory-core/src/buffer.rs
index b91d9617b..6d214710b 100644
--- a/rust/fory-core/src/buffer.rs
+++ b/rust/fory-core/src/buffer.rs
@@ -79,6 +79,11 @@ impl<'a> Writer<'a> {
v.len()
}
+ #[inline(always)]
+ pub fn write_bool(&mut self, value: bool) {
+ self.bf.write_u8(if value { 1 } else { 0 }).unwrap();
+ }
+
#[inline(always)]
pub fn write_u8(&mut self, value: u8) {
self.bf.write_u8(value).unwrap();
diff --git a/rust/fory-derive/src/object/read.rs
b/rust/fory-derive/src/object/read.rs
index cc76199d2..015f2736c 100644
--- a/rust/fory-derive/src/object/read.rs
+++ b/rust/fory-derive/src/object/read.rs
@@ -21,8 +21,9 @@ use syn::Field;
use super::util::{
classify_trait_object_field, compute_struct_version_hash,
create_wrapper_types_arc,
- create_wrapper_types_rc, extract_type_name, get_struct_name,
is_debug_enabled,
- is_primitive_type, is_skip_field, should_skip_type_info_for_field,
skip_ref_flag, StructField,
+ create_wrapper_types_rc, extract_type_name, get_primitive_reader_method,
get_struct_name,
+ is_debug_enabled, is_direct_primitive_numeric_type, is_primitive_type,
is_skip_field,
+ should_skip_type_info_for_field, skip_ref_flag, StructField,
};
pub(crate) fn create_private_field_name(field: &Field) -> Ident {
@@ -185,7 +186,16 @@ pub fn gen_read_field(field: &Field, private_ident:
&Ident) -> TokenStream {
_ => {
let skip_ref_flag = skip_ref_flag(ty);
let skip_type_info = should_skip_type_info_for_field(ty);
- if skip_type_info {
+
+ // Check if this is a direct primitive numeric type that can use
direct reader calls
+ if is_direct_primitive_numeric_type(ty) {
+ let type_name = extract_type_name(ty);
+ let reader_method = get_primitive_reader_method(&type_name);
+ let reader_ident = syn::Ident::new(reader_method,
proc_macro2::Span::call_site());
+ quote! {
+ let #private_ident = context.reader.#reader_ident()?;
+ }
+ } else if skip_type_info {
// Known types (primitives, strings, collections) - skip type
info at compile time
if skip_ref_flag {
quote! {
diff --git a/rust/fory-derive/src/object/util.rs
b/rust/fory-derive/src/object/util.rs
index 578d38d0c..b9d8bd2e8 100644
--- a/rust/fory-derive/src/object/util.rs
+++ b/rust/fory-derive/src/object/util.rs
@@ -567,6 +567,59 @@ pub(super) fn is_primitive_type(ty: &str) -> bool {
PRIMITIVE_TYPE_NAMES.contains(&ty)
}
+/// Mapping of primitive type names to their writer and reader method names
+/// Order: (type_name, writer_method, reader_method)
+static PRIMITIVE_IO_METHODS: &[(&str, &str, &str)] = &[
+ ("bool", "write_bool", "read_bool"),
+ ("i8", "write_i8", "read_i8"),
+ ("i16", "write_i16", "read_i16"),
+ ("i32", "write_varint32", "read_varint32"),
+ ("i64", "write_varint64", "read_varint64"),
+ ("f32", "write_f32", "read_f32"),
+ ("f64", "write_f64", "read_f64"),
+ ("u8", "write_u8", "read_u8"),
+ ("u16", "write_u16", "read_u16"),
+ ("u32", "write_u32", "read_u32"),
+ ("u64", "write_u64", "read_u64"),
+ ("usize", "write_usize", "read_usize"),
+];
+
+/// Check if a type is a direct primitive numeric type (not wrapped in Option,
Vec, etc.)
+pub(super) fn is_direct_primitive_numeric_type(ty: &Type) -> bool {
+ if let Type::Path(type_path) = ty {
+ if let Some(seg) = type_path.path.segments.last() {
+ // Check if it's a simple type path without generics
+ if matches!(seg.arguments, PathArguments::None) {
+ let type_name = seg.ident.to_string();
+ return PRIMITIVE_IO_METHODS
+ .iter()
+ .any(|(name, _, _)| *name == type_name.as_str());
+ }
+ }
+ }
+ false
+}
+
+/// Get the writer method name for a primitive numeric type
+/// Panics if type_name is not a primitive type
+pub(super) fn get_primitive_writer_method(type_name: &str) -> &'static str {
+ PRIMITIVE_IO_METHODS
+ .iter()
+ .find(|(name, _, _)| *name == type_name)
+ .map(|(_, writer, _)| *writer)
+ .unwrap_or_else(|| panic!("type_name '{}' must be a primitive type",
type_name))
+}
+
+/// Get the reader method name for a primitive numeric type
+/// Panics if type_name is not a primitive type
+pub(super) fn get_primitive_reader_method(type_name: &str) -> &'static str {
+ PRIMITIVE_IO_METHODS
+ .iter()
+ .find(|(name, _, _)| *name == type_name)
+ .map(|(_, _, reader)| *reader)
+ .unwrap_or_else(|| panic!("type_name '{}' must be a primitive type",
type_name))
+}
+
pub(crate) fn get_type_id_by_type_ast(ty: &Type) -> u32 {
let ty_str: String = ty
.to_token_stream()
diff --git a/rust/fory-derive/src/object/write.rs
b/rust/fory-derive/src/object/write.rs
index fd9981bb5..4f95f766e 100644
--- a/rust/fory-derive/src/object/write.rs
+++ b/rust/fory-derive/src/object/write.rs
@@ -17,8 +17,9 @@
use super::util::{
classify_trait_object_field, compute_struct_version_hash,
create_wrapper_types_arc,
- create_wrapper_types_rc, get_filtered_fields_iter, get_struct_name,
get_type_id_by_type_ast,
- is_debug_enabled, should_skip_type_info_for_field, skip_ref_flag,
StructField,
+ create_wrapper_types_rc, extract_type_name, get_filtered_fields_iter,
+ get_primitive_writer_method, get_struct_name, get_type_id_by_type_ast,
is_debug_enabled,
+ is_direct_primitive_numeric_type, should_skip_type_info_for_field,
skip_ref_flag, StructField,
};
use fory_core::types::TypeId;
use proc_macro2::{Ident, TokenStream};
@@ -185,7 +186,24 @@ pub fn gen_write_field(field: &Field, ident: &Ident,
use_self: bool) -> TokenStr
let skip_ref_flag = skip_ref_flag(ty);
let skip_type_info = should_skip_type_info_for_field(ty);
let type_id = get_type_id_by_type_ast(ty);
- if type_id == TypeId::LIST as u32
+
+ // Check if this is a direct primitive numeric type that can use
direct writer calls
+ if is_direct_primitive_numeric_type(ty) {
+ let type_name = extract_type_name(ty);
+ let writer_method = get_primitive_writer_method(&type_name);
+ let writer_ident = syn::Ident::new(writer_method,
proc_macro2::Span::call_site());
+ // For primitives:
+ // - use_self=true: #value_ts is `self.field`, which is T
(copy happens automatically)
+ // - use_self=false: #value_ts is `field` from pattern match
on &self, which is &T
+ let value_expr = if use_self {
+ quote! { #value_ts }
+ } else {
+ quote! { *#value_ts }
+ };
+ quote! {
+ context.writer.#writer_ident(#value_expr);
+ }
+ } else if type_id == TypeId::LIST as u32
|| type_id == TypeId::SET as u32
|| type_id == TypeId::MAP as u32
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]