colvec_derive/
lib.rs

1#[cfg(not(test))]
2use proc_macro::TokenStream;
3#[cfg(test)]
4use proc_macro2::TokenStream;
5use quote::quote;
6use syn::DeriveInput;
7
8#[cfg(not(test))]
9#[proc_macro_derive(ColVec)]
10pub fn colvec_derive(input:TokenStream)->TokenStream{
11	let input:DeriveInput=syn::parse_macro_input!(input);
12	colvec_derive_inner(input)
13}
14
15fn colvec_derive_inner(input:DeriveInput)->TokenStream{
16	match input.data{
17		syn::Data::Struct(syn::DataStruct{fields:syn::Fields::Named(fields_named),..})=>derive_struct(input.ident,input.vis,fields_named),
18		_=>unimplemented!("Only structs are supported"),
19	}
20}
21
22fn derive_struct(ident:syn::Ident,vis:syn::Visibility,fields:syn::FieldsNamed)->TokenStream{
23	let colvec_ident_string=format!("{ident}ColVec");
24	let colvec_ident=syn::Ident::new(&colvec_ident_string,ident.span());
25
26	let fields_count=fields.named.len();
27	let colvec = quote!{
28		#vis struct #colvec_ident<A: ::colvec::alloc::Allocator = ::colvec::alloc::Global>{
29			buf: ::colvec::raw::RawColVec<#fields_count, #ident, A>,
30			len: usize,
31		}
32	};
33
34	let global = quote! {
35		impl #colvec_ident<::colvec::alloc::Global>{
36			#[inline]
37			#[must_use]
38			pub const fn new() -> Self {
39				Self { buf: ::colvec::raw::RawColVec::new(), len: 0 }
40			}
41			#[inline]
42			#[must_use]
43			#[track_caller]
44			pub fn with_capacity(capacity: usize) -> Self {
45				Self::with_capacity_in(capacity, ::colvec::alloc::Global)
46			}
47		}
48	};
49
50	// this trait smuggles information about the input type into RawColVec and RawColVecInner
51	let fields_types=fields.named.iter().map(|field|field.ty.clone());
52	let struct_info = quote! {
53		impl ::colvec::raw::StructInfo<#fields_count> for #ident{
54			const LAYOUT: ::core::alloc::Layout = unsafe {
55				let size = Self::FIELDS.size();
56				let align = align_of::<#ident>();
57				::core::alloc::Layout::from_size_align_unchecked(size, align)
58			};
59			const FIELDS: ::colvec::fields::Fields<#fields_count> = ::colvec::fields::Fields::from_sizes([
60				#(size_of::<#fields_types>()),*
61			]);
62		}
63	};
64
65	let field_indices=0..fields_count;
66	let field_types=fields.named.iter().map(|field|field.ty.clone());
67	let field_idents=fields.named.iter().map(|field|field.ident.as_ref().unwrap().clone());
68	let impls = quote! {
69		impl<A: ::colvec::alloc::Allocator> #colvec_ident<A>{
70			#[inline]
71			pub const fn new_in(alloc: A) -> Self {
72				Self { buf: ::colvec::raw::RawColVec::new_in(alloc), len: 0 }
73			}
74			#[inline]
75			#[track_caller]
76			pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
77				Self { buf: ::colvec::raw::RawColVec::with_capacity_in(capacity, alloc), len: 0 }
78			}
79			#[inline]
80			pub const fn capacity(&self) -> usize {
81				self.buf.capacity()
82			}
83			#[track_caller]
84			pub fn reserve(&mut self, additional: usize) {
85				self.buf.reserve(self.len, additional);
86			}
87			#[inline]
88			const fn as_ptr(&self) -> *const u8 {
89				// We shadow the slice method of the same name to avoid going through
90				// `deref`, which creates an intermediate reference.
91				self.buf.ptr()
92			}
93			#[inline]
94			const fn as_mut_ptr(&mut self) -> *mut u8 {
95				// We shadow the slice method of the same name to avoid going through
96				// `deref_mut`, which creates an intermediate reference.
97				self.buf.ptr()
98			}
99			#[inline]
100			pub unsafe fn set_len(&mut self, new_len: usize) {
101				debug_assert!(new_len <= self.capacity());
102
103				self.len = new_len;
104			}
105			pub fn push(&mut self, value: #ident){
106				// Inform codegen that the length does not change across grow_one().
107				let len = self.len;
108				// This will panic or abort if we would allocate > isize::MAX bytes
109				// or if the length increment would overflow for zero-sized types.
110				if len == self.buf.capacity() {
111					self.buf.grow_one();
112				}
113				unsafe {
114					#(
115						let end = self.as_mut_ptr()
116							.add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
117							.cast::<#field_types>()
118							.add(len);
119						::core::ptr::write(end, value.#field_idents);
120					)*
121				}
122				self.len = len + 1;
123			}
124			#[inline]
125			#[track_caller]
126			pub fn append(&mut self, other: &mut Self) {
127				unsafe {
128					self.append_elements(other);
129					other.set_len(0);
130				}
131			}
132
133			/// Appends elements to `self` from other buffer.
134			#[inline]
135			#[track_caller]
136			unsafe fn append_elements(&mut self, other: &Self) {
137				let count = other.len();
138				self.reserve(count);
139				let len = self.len();
140				unsafe {
141					<#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.move_fields(
142						other.as_ptr(),
143						self.as_mut_ptr(),
144						other.capacity(),
145						self.capacity(),
146						len,
147						count,
148					)
149				}
150				self.len += count;
151			}
152		   #[inline]
153			pub const fn len(&self) -> usize {
154				self.len
155			}
156		}
157	};
158
159	let field_indices=0..fields_count;
160	let field_types=fields.named.iter().map(|field|field.ty.clone());
161	let field_slice_fn_idents=fields.named.iter().map(|field|{
162		let ident=field.ident.as_ref().unwrap();
163		let slice_ident=format!("{ident}_slice");
164		syn::Ident::new(&slice_ident,ident.span())
165	});
166	let field_slice_mut_fn_idents=fields.named.iter().map(|field|{
167		let ident=field.ident.as_ref().unwrap();
168		let slice_ident=format!("{ident}_slice_mut");
169		syn::Ident::new(&slice_ident,ident.span())
170	});
171	let field_access = quote! {
172		impl<A: ::colvec::alloc::Allocator> #colvec_ident<A>{
173			#(
174				#[inline]
175				pub const fn #field_slice_fn_idents(&self) -> &[#field_types] {
176					unsafe {
177						core::slice::from_raw_parts(
178							self.as_ptr()
179								.add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
180								.cast::<#field_types>(),
181							self.len
182						)
183					}
184				}
185				#[inline]
186				pub const fn #field_slice_mut_fn_idents(&mut self) -> &mut [#field_types] {
187					unsafe {
188						core::slice::from_raw_parts_mut(
189							self.as_mut_ptr()
190								.add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
191								.cast::<#field_types>(),
192							self.len
193						)
194					}
195				}
196			)*
197		}
198	};
199
200	quote! {
201		#colvec
202		#global
203
204		#struct_info
205
206		#impls
207		#field_access
208	}.into()
209}
210
211#[cfg(test)]
212mod tests {
213	use super::*;
214	use syn::parse_quote;
215
216	#[test]
217	fn snapshot_test1() {
218		let test1:syn::ItemStruct = parse_quote! {
219			pub struct Test{
220				field0:u8,
221				field1:Option<u8>,
222				field2:i16,
223				field3:u32,
224			}
225		};
226
227		let output = colvec_derive_inner(test1.into());
228
229		// pretend it outputs a file
230		let as_file = syn::parse_file(&output.to_string()).unwrap();
231
232		// format it in a pretty way
233		let formatted = prettyplease::unparse(&as_file);
234
235		// snapshot-test it
236		insta::assert_snapshot!(formatted);
237	}
238}