Skip to content

Commit 2fd7f0d

Browse files
committed
fixes and fully qualified import paths
1 parent 8ba9f87 commit 2fd7f0d

13 files changed

Lines changed: 190 additions & 173 deletions

File tree

Lines changed: 67 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,55 @@
1-
//! Procedural macros for zero-copy deserialization.
1+
//! Procedural macros for borsh compatible zero copy serialization.
2+
//!
23
//!
3-
//! This crate provides derive macros that generate efficient zero-copy data structures
4-
//! and deserialization code, eliminating the need for data copying during parsing.
54
//!
65
//! ## Main Macros
76
//!
8-
//! - `ZeroCopy`: Generates zero-copy structs and deserialization traits
9-
//! - `ZeroCopyMut`: Adds mutable zero-copy support
10-
//! - `ZeroCopyEq`: Adds PartialEq implementation for comparing with original structs
11-
//! - `ZeroCopyNew`: Generates configuration structs for initialization
7+
//! - `ZeroCopy`: Derives ZeroCopyAt
8+
//! - `ZeroCopyMut`: Derives ZeroCopyAtMut, ZeroCopyNew
9+
//! - `ZeroCopyEq`: Derives PartialEq for <StructName>::ZeroCopy == StructName
10+
//!
11+
//!
12+
//! ## Macro Rules
13+
//! 1. Create zero copy structs Z<StructName> for the struct
14+
//! 1.1. The first consecutive fixed-size fields are extracted into a meta struct Z<StructName>Meta
15+
//! 1.2. Meta extraction stops at first Vec, Option, or non-Copy type
16+
//! 1.3. Primitive types are converted to little-endian equivalents (u16→U16, u32→U32, u64→U64, bool→u8)
17+
//! 1.4. Fields after meta are included directly in the Z-struct and deserialized sequentially
18+
//! 1.5. Vec<u8> uses optimized slice operations, other Vec<T> types use ZeroCopySlice
19+
//! 1.6. Option<u64/u32/u16> are optimized, other Option<T> delegate to T's ZeroCopyAt
20+
//! 1.7. Non-Copy types must implement ZeroCopyAt trait
21+
//! Examples:
22+
//! ```rust
23+
//! use light_zero_copy::slice::ZeroCopySliceBorsh;
24+
//! use light_zero_copy::slice_mut::ZeroCopySliceMutBorsh;
25+
//!
26+
//! pub struct Struct1 {
27+
//! a: Vec<u8>,
28+
//! }
29+
//!
30+
//! pub struct ZStruct1<'a> {
31+
//! a: &'a [u8]
32+
//! }
33+
//! pub struct ZStruct1Mut<'a> {
34+
//! a: &'a mut [u8]
35+
//! }
36+
//!
37+
//! pub struct Struct2 {
38+
//! a: Vec<u64>,
39+
//! }
40+
//!
41+
//! pub struct ZStruct2<'a> {
42+
//! a: ZeroCopySliceBorsh<'a, u64>,
43+
//! }
44+
//! pub struct ZStruct2Mut<'a> {
45+
//! a: ZeroCopySliceMutBorsh<'a, u64>,
46+
//! }
47+
//! ```
48+
//! 2. Implement ZeroCopyAt trait which returns Z<StructName>
49+
//! 3. ZeroCopyMut (separate derive) adds:
50+
//! 3.1. Mutable variants with 'Mut' suffix (Z<StructName>Mut, Z<StructName>MetaMut)
51+
//! 3.2. ZeroCopyAtMut trait implementation
52+
//! 3.3. ZeroCopyNew trait with configuration struct for dynamic field initialization
1253
1354
use proc_macro::TokenStream;
1455

@@ -43,33 +84,6 @@ mod tests;
4384
/// }
4485
/// ```
4586
///
46-
/// To derive LightHasher for the generated ZStruct, use the #[light_hasher] attribute:
47-
/// ```ignore
48-
/// use light_zero_copy_derive::ZeroCopy;
49-
/// #[derive(ZeroCopy)]
50-
/// #[light_hasher] // Currently disabled due to Vec<u8>/&[u8] hash inconsistency
51-
/// pub struct MyStruct {
52-
/// pub a: u8,
53-
/// }
54-
/// ```
55-
///
56-
/// Note: #[light_hasher] is currently disabled due to hash inconsistency between
57-
/// Vec<u8> fields in the original struct and &[u8] slice fields in the generated ZStruct.
58-
///
59-
/// # Macro Rules
60-
/// 1. Create zero copy structs Z<StructName> and Z<StructName>Mut for the struct
61-
/// 1.1. The first fields are extracted into a meta struct until we reach a Vec, Option or type that does not implement Copy
62-
/// 1.2. Represent vectors to ZeroCopySlice & don't include these into the meta struct
63-
/// 1.3. Replace u16 with U16, u32 with U32, etc
64-
/// 1.4. Every field after the first vector is directly included in the ZStruct and deserialized 1 by 1
65-
/// 1.5. If a vector contains a nested vector (does not implement Copy) it must implement Deserialize
66-
/// 1.6. Elements in an Option must implement Deserialize
67-
/// 1.7. A type that does not implement Copy must implement Deserialize, and is deserialized 1 by 1
68-
/// 1.8. is u8 deserialized as u8::zero_copy_at instead of Ref<&'a [u8], u8> for non mut, for mut it is Ref<&'a mut [u8], u8>
69-
/// 2. Implement Deserialize and ZeroCopyAtMut which return Z<StructName> and Z<StructName>Mut
70-
/// 3. Implement From<Z<StructName>> for StructName and From<Z<StructName>Mut> for StructName
71-
///
72-
/// Note: Options are not supported in ZeroCopyEq
7387
#[proc_macro_derive(ZeroCopy, attributes(light_hasher, hash, skip))]
7488
pub fn derive_zero_copy(input: TokenStream) -> TokenStream {
7589
let res = zero_copy::derive_zero_copy_impl(input);
@@ -90,6 +104,7 @@ pub fn derive_zero_copy(input: TokenStream) -> TokenStream {
90104
/// pub a: u8,
91105
/// }
92106
/// ```
107+
/// Note: Options are not supported in ZeroCopyEq
93108
#[proc_macro_derive(ZeroCopyEq)]
94109
pub fn derive_zero_copy_eq(input: TokenStream) -> TokenStream {
95110
let res = zero_copy_eq::derive_zero_copy_eq_impl(input);
@@ -103,9 +118,10 @@ pub fn derive_zero_copy_eq(input: TokenStream) -> TokenStream {
103118
///
104119
/// This macro generates mutable zero-copy implementations including:
105120
/// - ZeroCopyAtMut trait implementation
106-
/// - Mutable Z-struct with `Mut` suffix
107-
/// - byte_len() method implementation
108-
/// - Mutable ZeroCopyStructInner implementation
121+
/// - Mutable Z-struct with `Mut` suffix (Z<StructName>Mut)
122+
/// - Mutable meta struct if there are fixed-size fields (Z<StructName>MetaMut)
123+
/// - ZeroCopyNew trait implementation with configuration support
124+
/// - Configuration struct for dynamic fields or unit type for fixed-size structs
109125
///
110126
/// # Usage
111127
///
@@ -120,9 +136,21 @@ pub fn derive_zero_copy_eq(input: TokenStream) -> TokenStream {
120136
/// ```
121137
///
122138
/// This will generate:
123-
/// - `MyStruct::zero_copy_at_mut()` method
124139
/// - `ZMyStructMut<'a>` type for mutable zero-copy access
125-
/// - `MyStruct::byte_len()` method
140+
/// - `MyStructConfig` struct with `vec: u32` field for Vec length
141+
/// - `ZeroCopyAtMut` trait implementation for deserialization
142+
/// - `ZeroCopyNew` trait implementation for initialization with config
143+
///
144+
/// For fixed-size structs, generates unit config:
145+
/// ```rust
146+
/// use light_zero_copy_derive::ZeroCopyMut;
147+
/// #[derive(ZeroCopyMut)]
148+
/// pub struct FixedStruct {
149+
/// pub a: u8,
150+
/// pub b: u16,
151+
/// }
152+
/// // Generates: pub type FixedStructConfig = ();
153+
/// ```
126154
///
127155
/// For both immutable and mutable functionality, use both derives:
128156
/// ```rust
@@ -142,41 +170,3 @@ pub fn derive_zero_copy_mut(input: TokenStream) -> TokenStream {
142170
Err(err) => err.to_compile_error(),
143171
})
144172
}
145-
146-
// /// ZeroCopyNew derivation macro for configuration-based zero-copy initialization
147-
// ///
148-
// /// This macro generates configuration structs and initialization methods for structs
149-
// /// with Vec and Option fields that need to be initialized with specific configurations.
150-
// ///
151-
// /// # Usage
152-
// ///
153-
// /// ```ignore
154-
// /// use light_zero_copy_derive::ZeroCopyNew;
155-
// ///
156-
// /// #[derive(ZeroCopyNew)]
157-
// /// pub struct MyStruct {
158-
// /// pub a: u8,
159-
// /// pub vec: Vec<u8>,
160-
// /// pub option: Option<u64>,
161-
// /// }
162-
// /// ```
163-
// ///
164-
// /// This will generate:
165-
// /// - `MyStructConfig` struct with configuration fields
166-
// /// - `ZeroCopyNew` implementation for `MyStruct`
167-
// /// - `new_zero_copy(bytes, config)` method for initialization
168-
// ///
169-
// /// The configuration struct will have fields based on the complexity of the original fields:
170-
// /// - `Vec<Primitive>` → `field_name: u32` (length)
171-
// /// - `Option<Primitive>` → `field_name: bool` (is_some)
172-
// /// - `Vec<Complex>` → `field_name: Vec<ComplexConfig>` (config per element)
173-
// /// - `Option<Complex>` → `field_name: Option<ComplexConfig>` (config if some)
174-
// #[cfg(feature = "mut")]
175-
// #[proc_macro_derive(ZeroCopyNew)]
176-
// pub fn derive_zero_copy_config(input: TokenStream) -> TokenStream {
177-
// let res = zero_copy_new::derive_zero_copy_config_impl(input);
178-
// TokenStream::from(match res {
179-
// Ok(res) => res,
180-
// Err(err) => err.to_compile_error(),
181-
// })
182-
// }

program-libs/zero-copy-derive/src/shared/meta_struct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub fn generate_meta_struct<const MUT: bool>(
4848
// Return the complete meta struct definition
4949
let result = quote! {
5050
#[repr(C)]
51-
#[derive(Debug, PartialEq, light_zero_copy::KnownLayout, light_zero_copy::Immutable, light_zero_copy::Unaligned, light_zero_copy::FromBytes, light_zero_copy::IntoBytes #hasher)]
51+
#[derive(Debug, PartialEq, ::light_zero_copy::KnownLayout, ::light_zero_copy::Immutable, ::light_zero_copy::Unaligned, ::light_zero_copy::FromBytes, ::light_zero_copy::IntoBytes #hasher)]
5252
pub struct #z_struct_meta_name {
5353
#(#meta_fields_with_converted_types,)*
5454
}

program-libs/zero-copy-derive/src/shared/utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,9 @@ pub fn convert_to_zerocopy_type(ty: &Type) -> TokenStream {
225225

226226
// Handle primitive types first
227227
match ident.to_string().as_str() {
228-
"u16" => quote! { light_zero_copy::little_endian::U16 },
229-
"u32" => quote! { light_zero_copy::little_endian::U32 },
230-
"u64" => quote! { light_zero_copy::little_endian::U64 },
228+
"u16" => quote! { ::light_zero_copy::little_endian::U16 },
229+
"u32" => quote! { ::light_zero_copy::little_endian::U32 },
230+
"u64" => quote! { ::light_zero_copy::little_endian::U64 },
231231
"bool" => quote! { u8 },
232232
_ => {
233233
// Handle container types recursively

program-libs/zero-copy-derive/src/shared/z_enum.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub fn generate_z_enum(z_enum_name: &Ident, enum_data: &DataEnum) -> syn::Result
2626
// Create a type alias for this variant to enable pattern matching
2727
let alias_name = format_ident!("{}Type", variant_name);
2828
type_aliases.push(quote! {
29-
pub type #alias_name<'a> = <#field_type as light_zero_copy::traits::ZeroCopyAt<'a>>::ZeroCopyAt;
29+
pub type #alias_name<'a> = <#field_type as ::light_zero_copy::traits::ZeroCopyAt<'a>>::ZeroCopyAt;
3030
});
3131

3232
Ok(quote! { #variant_name(#alias_name<'a>) })
@@ -96,7 +96,7 @@ pub fn generate_enum_deserialize_impl(
9696
Ok(quote! {
9797
#discriminant => {
9898
let (value, remaining_bytes) =
99-
<#field_type as light_zero_copy::traits::ZeroCopyAt>::zero_copy_at(remaining_data)?;
99+
<#field_type as ::light_zero_copy::traits::ZeroCopyAt>::zero_copy_at(remaining_data)?;
100100
Ok((#z_enum_name::#variant_name(value), remaining_bytes))
101101
}
102102
})
@@ -105,7 +105,7 @@ pub fn generate_enum_deserialize_impl(
105105
// Other cases already handled in generate_z_enum
106106
Ok(quote! {
107107
#discriminant => {
108-
Err(light_zero_copy::errors::ZeroCopyError::InvalidConversion)
108+
Err(::light_zero_copy::errors::ZeroCopyError::InvalidConversion)
109109
}
110110
})
111111
}
@@ -114,15 +114,15 @@ pub fn generate_enum_deserialize_impl(
114114
let match_arms = match_arms_result?;
115115

116116
Ok(quote! {
117-
impl<'a> light_zero_copy::traits::ZeroCopyAt<'a> for #original_name {
117+
impl<'a> ::light_zero_copy::traits::ZeroCopyAt<'a> for #original_name {
118118
type ZeroCopyAt = #z_enum_name<'a>;
119119

120120
fn zero_copy_at(
121121
data: &'a [u8],
122-
) -> Result<(Self::ZeroCopyAt, &'a [u8]), light_zero_copy::errors::ZeroCopyError> {
122+
) -> Result<(Self::ZeroCopyAt, &'a [u8]), ::light_zero_copy::errors::ZeroCopyError> {
123123
// Read discriminant (first 1 byte for borsh enum)
124124
if data.is_empty() {
125-
return Err(light_zero_copy::errors::ZeroCopyError::ArraySize(
125+
return Err(::light_zero_copy::errors::ZeroCopyError::ArraySize(
126126
1,
127127
data.len(),
128128
));
@@ -133,7 +133,7 @@ pub fn generate_enum_deserialize_impl(
133133

134134
match discriminant {
135135
#(#match_arms)*
136-
_ => Err(light_zero_copy::errors::ZeroCopyError::InvalidConversion),
136+
_ => Err(::light_zero_copy::errors::ZeroCopyError::InvalidConversion),
137137
}
138138
}
139139
}
@@ -146,7 +146,7 @@ pub fn generate_enum_zero_copy_struct_inner(
146146
z_enum_name: &Ident,
147147
) -> syn::Result<TokenStream> {
148148
Ok(quote! {
149-
impl light_zero_copy::traits::ZeroCopyStructInner for #original_name {
149+
impl ::light_zero_copy::traits::ZeroCopyStructInner for #original_name {
150150
type ZeroCopyInner = #z_enum_name<'static>;
151151
}
152152
})

program-libs/zero-copy-derive/src/shared/z_struct.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ fn generate_struct_fields_with_zerocopy_types<'a, const MUT: bool>(
259259
let field_type = utils::convert_to_zerocopy_type(field_type);
260260
quote! {
261261
#(#attributes)*
262-
pub #field_name: light_zero_copy::Ref<#mutability , #field_type>
262+
pub #field_name: ::light_zero_copy::Ref<#mutability , #field_type>
263263
}
264264
}
265265
FieldType::Option(field_name, field_type) => {
@@ -273,21 +273,21 @@ fn generate_struct_fields_with_zerocopy_types<'a, const MUT: bool>(
273273
let field_ty_zerocopy = utils::convert_to_zerocopy_type(&parse_quote!(u64));
274274
quote! {
275275
#(#attributes)*
276-
pub #field_name: Option<light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
276+
pub #field_name: Option<::light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
277277
}
278278
}
279279
FieldType::OptionU32(field_name) => {
280280
let field_ty_zerocopy = utils::convert_to_zerocopy_type(&parse_quote!(u32));
281281
quote! {
282282
#(#attributes)*
283-
pub #field_name: Option<light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
283+
pub #field_name: Option<::light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
284284
}
285285
}
286286
FieldType::OptionU16(field_name) => {
287287
let field_ty_zerocopy = utils::convert_to_zerocopy_type(&parse_quote!(u16));
288288
quote! {
289289
#(#attributes)*
290-
pub #field_name: Option<light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
290+
pub #field_name: Option<::light_zero_copy::Ref<#mutability, #field_ty_zerocopy>>
291291
}
292292
}
293293
FieldType::Pubkey(field_name) => {
@@ -306,7 +306,7 @@ fn generate_struct_fields_with_zerocopy_types<'a, const MUT: bool>(
306306
let zerocopy_type = utils::convert_to_zerocopy_type(field_type);
307307
quote! {
308308
#(#attributes)*
309-
pub #field_name: light_zero_copy::Ref<#mutability , #zerocopy_type>
309+
pub #field_name: ::light_zero_copy::Ref<#mutability , #zerocopy_type>
310310
}
311311
}
312312
FieldType::DynamicZeroCopy(field_name, field_type) => {
@@ -404,11 +404,11 @@ pub fn generate_z_struct<const MUT: bool>(
404404
#[derive(Debug #partial_eq_derive #derive_clone #derive_hasher)]
405405
pub struct #z_struct_name<'a> {
406406
#hasher_flatten
407-
__meta: light_zero_copy::Ref<#mutability, #z_struct_meta_name>,
407+
__meta: ::light_zero_copy::Ref<#mutability, #z_struct_meta_name>,
408408
#(#struct_fields_with_zerocopy_types,)*
409409
}
410-
impl<'a> core::ops::Deref for #z_struct_name<'a> {
411-
type Target = light_zero_copy::Ref<#mutability , #z_struct_meta_name>;
410+
impl<'a> ::core::ops::Deref for #z_struct_name<'a> {
411+
type Target = ::light_zero_copy::Ref<#mutability , #z_struct_meta_name>;
412412

413413
fn deref(&self) -> &Self::Target {
414414
&self.__meta
@@ -418,7 +418,7 @@ pub fn generate_z_struct<const MUT: bool>(
418418

419419
if MUT {
420420
tokens.append_all(quote! {
421-
impl<'a> core::ops::DerefMut for #z_struct_name<'a> {
421+
impl<'a> ::core::ops::DerefMut for #z_struct_name<'a> {
422422
fn deref_mut(&mut self) -> &mut Self::Target {
423423
&mut self.__meta
424424
}
@@ -428,24 +428,25 @@ pub fn generate_z_struct<const MUT: bool>(
428428
tokens
429429
};
430430

431-
if !meta_fields.is_empty() {
431+
// Only generate impl block if there are boolean fields that need accessors
432+
let has_bool_in_meta = meta_fields.iter().any(|f| utils::is_bool_type(&f.ty));
433+
if has_bool_in_meta {
432434
let meta_bool_accessor_methods = generate_bool_accessor_methods::<false>(meta_fields);
433435
z_struct.append_all(quote! {
434-
// Implement methods for ZStruct
435436
impl<'a> #z_struct_name<'a> {
436437
#(#meta_bool_accessor_methods)*
437438
}
438439
})
439-
};
440+
}
440441

441-
if !struct_fields.is_empty() {
442+
// Only generate impl block if there are boolean fields that need accessors
443+
let has_bool_in_struct = struct_fields.iter().any(|f| utils::is_bool_type(&f.ty));
444+
if has_bool_in_struct {
442445
let bool_accessor_methods = generate_bool_accessor_methods::<MUT>(struct_fields);
443446
z_struct.append_all(quote! {
444-
// Implement methods for ZStruct
445447
impl<'a> #z_struct_name<'a> {
446448
#(#bool_accessor_methods)*
447449
}
448-
449450
});
450451
}
451452
Ok(z_struct)

0 commit comments

Comments
 (0)