diff --git a/bebytes/Cargo.toml b/bebytes/Cargo.toml index 1402afb..47f104a 100644 --- a/bebytes/Cargo.toml +++ b/bebytes/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bebytes" -version = "3.0.0" +version = "3.0.1" edition = "2021" rust-version = "1.75.0" license = "MIT" @@ -21,7 +21,7 @@ path = "./bin/performance_benchmark.rs" required-features = ["std"] [dependencies] -bebytes_derive = { version = "3.0.0" } +bebytes_derive = { version = "3.0.1" } bytes = { version = "1", optional = true } [dev-dependencies] diff --git a/bebytes_derive/Cargo.toml b/bebytes_derive/Cargo.toml index 1557aed..d44a545 100644 --- a/bebytes_derive/Cargo.toml +++ b/bebytes_derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "bebytes_derive" description = "A macro to generate/parse binary representation of messages with custom bit fields" -version = "3.0.0" +version = "3.0.1" edition = "2021" rust-version = "1.75.0" publish = true diff --git a/bebytes_derive/src/functional.rs b/bebytes_derive/src/functional.rs index bfaba5a..23aa062 100644 --- a/bebytes_derive/src/functional.rs +++ b/bebytes_derive/src/functional.rs @@ -214,12 +214,18 @@ pub mod pure_helpers { use quote::quote; use syn::Ident; - /// Create a field accessor without side effects - pub fn create_field_accessor(field_name: &Ident, needs_owned: bool) -> TokenStream { - if needs_owned { - quote! { let #field_name = self.#field_name.clone(); } - } else { - quote! { let #field_name = self.#field_name; } + #[derive(Clone, Copy)] + pub enum AccessorMode { + Copy, + Reference, + Clone, + } + + pub fn create_field_accessor(field_name: &Ident, mode: AccessorMode) -> TokenStream { + match mode { + AccessorMode::Copy => quote! { let #field_name = self.#field_name; }, + AccessorMode::Reference => quote! { let #field_name = &self.#field_name; }, + AccessorMode::Clone => quote! { let #field_name = self.#field_name.clone(); }, } } diff --git a/bebytes_derive/src/structs.rs b/bebytes_derive/src/structs.rs index 6087296..fbcd811 100644 --- a/bebytes_derive/src/structs.rs +++ b/bebytes_derive/src/structs.rs @@ -1,3 +1,4 @@ +use crate::functional::pure_helpers::{create_field_accessor, AccessorMode}; use crate::{attrs, utils}; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; @@ -482,7 +483,7 @@ fn process_bits_field_functional( let field_name = &context.field_name; let field_type = context.field_type; - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, false); + let accessor = create_field_accessor(field_name, AccessorMode::Copy); let bit_sum = crate::functional::pure_helpers::create_bit_sum(size); let limit_check = crate::functional::pure_helpers::create_bit_field_limit_check(field_name, field_type, size); @@ -769,7 +770,7 @@ fn process_primitive_type_functional( let field_size = utils::get_primitive_type_size(field_type)?; - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, false); + let accessor = create_field_accessor(field_name, AccessorMode::Copy); let bit_sum = crate::functional::pure_helpers::create_byte_bit_sum(field_size); let parsing_tokens = vec![ @@ -817,8 +818,7 @@ fn process_array_functional( if let syn::Type::Path(elem) = &*tp.elem { let segments = &elem.path.segments; if segments.len() == 1 && segments[0].ident == "u8" { - let accessor = - crate::functional::pure_helpers::create_field_accessor(field_name, true); + let accessor = create_field_accessor(field_name, AccessorMode::Copy); let bit_sum = crate::functional::pure_helpers::create_byte_bit_sum(length); let parsing = quote! { @@ -1011,7 +1011,7 @@ fn process_vector_functional( let field = context.field; let is_last_field = context.is_last_field; - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, true); + let accessor = create_field_accessor(field_name, AccessorMode::Clone); if let syn::Type::Path(tp) = field_type { if let Some(syn::Type::Path(ref inner_tp)) = utils::solve_for_inner_type(tp, "Vec") { @@ -1250,8 +1250,7 @@ fn process_option_array( let array_len: usize = lit_int.base10_parse()?; let total_size = array_len + 1; - let accessor = - crate::functional::pure_helpers::create_field_accessor(field_name, true); + let accessor = create_field_accessor(field_name, AccessorMode::Copy); let bit_sum = crate::functional::pure_helpers::create_byte_bit_sum(total_size); let parsing = quote! { @@ -1339,8 +1338,7 @@ fn process_option_type_functional( let field_size = utils::get_primitive_type_size(&inner_type)?; let total_size = field_size + 1; - let accessor = - crate::functional::pure_helpers::create_field_accessor(field_name, false); + let accessor = create_field_accessor(field_name, AccessorMode::Copy); let bit_sum = crate::functional::pure_helpers::create_byte_bit_sum(total_size); let value_parsing = create_option_inner_parsing( @@ -1430,8 +1428,7 @@ fn process_custom_type_functional( let field_name = &context.field_name; let field_type = context.field_type; - let needs_owned = !utils::is_copy(field_type); - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, needs_owned); + let accessor = create_field_accessor(field_name, AccessorMode::Clone); let bit_sum = quote! { bit_sum += 8 * #field_type::field_size(); @@ -1480,7 +1477,7 @@ fn process_string_functional( let field = context.field; let is_last_field = context.is_last_field; - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, true); + let accessor = create_field_accessor(field_name, AccessorMode::Reference); // Generate parsing code based on size constraints let (bit_sum, parsing, writing) = match (size, string_size_ident) { @@ -1523,7 +1520,7 @@ fn process_size_expression_functional( let field_name = &context.field_name; let field_type = context.field_type; - let accessor = crate::functional::pure_helpers::create_field_accessor(field_name, true); + let accessor = create_field_accessor(field_name, AccessorMode::Clone); // Generate the size calculation code let size_calculation = size_expr.generate_evaluation_code(); diff --git a/bebytes_derive/src/utils.rs b/bebytes_derive/src/utils.rs index 9b3a634..bfd0a0e 100644 --- a/bebytes_derive/src/utils.rs +++ b/bebytes_derive/src/utils.rs @@ -138,76 +138,6 @@ pub fn is_vec_of_vec_u8(tp: &syn::TypePath) -> bool { false } -pub(crate) fn is_copy(field_type: &syn::Type) -> bool { - match field_type { - syn::Type::Never(_) | syn::Type::Infer(_) => true, // ! and _ are Copy - - syn::Type::Path(type_path) => { - // Check if it's a known Copy primitive or standard library type - if let Some(ident) = type_path.path.get_ident() { - let name = ident.to_string(); - match name.as_str() { - // Types that are Copy - "bool" | "char" | "u8" | "u16" | "u32" | "u64" | "u128" | "usize" | "i8" - | "i16" | "i32" | "i64" | "i128" | "isize" | "f32" | "f64" | "NonZero" - | "NonZeroU8" | "NonZeroU16" | "NonZeroU32" | "NonZeroU64" | "NonZeroU128" - | "NonZeroUsize" | "NonZeroI8" | "NonZeroI16" | "NonZeroI32" | "NonZeroI64" - | "NonZeroI128" | "NonZeroIsize" => true, - - _ => false, // Conservatively assume non-Copy - } - } else if !type_path.path.segments.is_empty() { - // Handle generic types - let last_segment = &type_path.path.segments.last().unwrap(); - match last_segment.ident.to_string().as_str() { - "Option" => { - // Option is Copy if T is Copy - if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments { - if !args.args.is_empty() { - if let syn::GenericArgument::Type(ty) = &args.args[0] { - return is_copy(ty); - } - } - } - false - } - "Result" => { - // Result is Copy if both T and E are Copy - if let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments { - if args.args.len() >= 2 { - if let syn::GenericArgument::Type(t) = &args.args[0] { - if let syn::GenericArgument::Type(e) = &args.args[1] { - return is_copy(t) && is_copy(e); - } - } - } - } - false - } - // Add more cases for other generic types - _ => false, // Conservatively assume non-Copy - } - } else { - false - } - } - - syn::Type::Array(type_array) => is_copy(&type_array.elem), // Array is Copy if T is Copy - syn::Type::Tuple(type_tuple) => { - // A tuple is Copy if all its elements are Copy - type_tuple.elems.iter().all(is_copy) - } - syn::Type::Paren(type_paren) => is_copy(&type_paren.elem), - syn::Type::Group(type_group) => is_copy(&type_group.elem), - - syn::Type::Reference(type_reference) => { - // &T is always Copy, &mut T is never Copy - type_reference.mutability.is_none() - } - _ => false, // Conservative default for any other types - } -} - #[cfg(test)] mod tests { use super::*; @@ -302,44 +232,6 @@ mod tests { } } - #[test] - fn test_is_copy_trait() { - // Types that implement Copy - let copy_types = vec![ - parse_quote!(u32), - parse_quote!(i64), - parse_quote!([u8; 10]), - parse_quote!(Option), - parse_quote!((u32, u64)), - parse_quote!(Result), - ]; - - for ty in copy_types { - assert!(is_copy(&ty), "Type should implement Copy"); - } - - // Types that don't implement Copy - let non_copy_types = vec![ - parse_quote!(String), - parse_quote!(Vec), - parse_quote!(Option), - parse_quote!(Result), - ]; - - for ty in non_copy_types { - assert!(!is_copy(&ty), "Type should not implement Copy"); - } - } - - #[test] - fn test_is_copy_nested_types() { - // Test nested type checking - assert!(is_copy(&parse_quote!(Option>))); - assert!(!is_copy(&parse_quote!(Option>))); - assert!(is_copy(&parse_quote!(Result<[u8; 10], u32>))); - assert!(!is_copy(&parse_quote!(Result))); - } - #[test] fn test_solve_for_inner_type() { // Test Option inner type extraction @@ -424,21 +316,9 @@ mod tests { #[test] fn test_edge_cases() { - // Test empty path segments let empty_path: syn::Type = parse_quote!(::std::vec::Vec); if let syn::Type::Path(tp) = &empty_path { assert!(!is_primitive_type(tp)); } - - // Test parenthesized types - let paren_ty: syn::Type = parse_quote!((u32)); - assert!(is_copy(&paren_ty)); - - // Test reference types - let ref_ty: syn::Type = parse_quote!(&u32); - assert!(is_copy(&ref_ty)); - - let mut_ref_ty: syn::Type = parse_quote!(&mut u32); - assert!(!is_copy(&mut_ref_ty)); } }