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 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
28 let mut colvec: syn::ItemStruct = syn::parse_quote!{
29 #vis struct #colvec_ident<A: ::colvec::alloc::Allocator>{
30 buf: ::colvec::raw::RawColVec<#fields_count, #ident, A>,
31 len: usize,
32 }
33 };
34
35 #[cfg(feature = "std")]
36 match colvec.generics.params.first_mut(){
37 Some(syn::GenericParam::Type(type_param))=>{
38 let eq_token=syn::Token);
39 let default=syn::parse_quote!{::colvec::alloc::Global};
40 type_param.default=Some((eq_token,default));
41 },
42 _ => unreachable!(),
44 }
45
46 #[cfg(feature = "std")]
47 let global = quote! {
48 impl #colvec_ident<::colvec::alloc::Global>{
49 #[inline]
50 #[must_use]
51 pub const fn new() -> Self {
52 Self { buf: ::colvec::raw::RawColVec::new_in(::colvec::alloc::Global), len: 0 }
53 }
54 #[inline]
55 #[must_use]
56 #[track_caller]
57 pub fn with_capacity(capacity: usize) -> Self {
58 Self::with_capacity_in(capacity, ::colvec::alloc::Global)
59 }
60 #[inline]
61 pub unsafe fn from_raw_parts(ptr: *mut u8, length: usize, capacity: usize) -> Self {
62 unsafe { Self::from_raw_parts_in(ptr, length, capacity, ::colvec::alloc::Global) }
63 }
64 }
65 };
66
67 let fields_types=fields.named.iter().map(|field|field.ty.clone());
69 let struct_info = quote! {
70 impl ::colvec::raw::StructInfo<#fields_count> for #ident{
71 const LAYOUT: ::core::alloc::Layout = unsafe {
72 let size = Self::FIELDS.size();
73 let align = align_of::<#ident>();
74 ::core::alloc::Layout::from_size_align_unchecked(size, align)
75 };
76 const FIELDS: ::colvec::fields::Fields<#fields_count> = ::colvec::fields::Fields::from_sizes([
77 #(size_of::<#fields_types>()),*
78 ]);
79 }
80 };
81
82 let field_indices=0..fields_count;
83 let field_types=fields.named.iter().map(|field|field.ty.clone());
84 let field_idents=fields.named.iter().map(|field|field.ident.as_ref().unwrap().clone());
85 let impls = quote! {
86 impl<A: ::colvec::alloc::Allocator> #colvec_ident<A>{
87 #[inline]
88 pub const fn new_in(alloc: A) -> Self {
89 Self { buf: ::colvec::raw::RawColVec::new_in(alloc), len: 0 }
90 }
91 #[inline]
92 #[track_caller]
93 pub fn with_capacity_in(capacity: usize, alloc: A) -> Self {
94 Self { buf: ::colvec::raw::RawColVec::with_capacity_in(capacity, alloc), len: 0 }
95 }
96 #[inline]
97 pub unsafe fn from_raw_parts_in(ptr: *mut u8, length: usize, capacity: usize, alloc: A) -> Self {
98 unsafe { Self { buf: ::colvec::raw::RawColVec::from_raw_parts_in(ptr, capacity, alloc), len: length } }
99 }
100 #[must_use = "losing the pointer will leak memory"]
101 pub fn into_raw_parts(self) -> (*mut u8, usize, usize) {
102 let mut me = ::core::mem::ManuallyDrop::new(self);
103 (me.as_mut_ptr(), me.len(), me.capacity())
104 }
105 #[must_use = "losing the pointer will leak memory"]
106 pub fn into_raw_parts_with_alloc(self) -> (*mut u8, usize, usize, A) {
107 let mut me = ::core::mem::ManuallyDrop::new(self);
108 let len = me.len();
109 let capacity = me.capacity();
110 let ptr = me.as_mut_ptr();
111 let alloc = unsafe { ::core::ptr::read(me.allocator()) };
112 (ptr, len, capacity, alloc)
113 }
114 #[inline]
115 pub const fn capacity(&self) -> usize {
116 self.buf.capacity()
117 }
118 #[track_caller]
119 pub fn reserve(&mut self, additional: usize) {
120 self.buf.reserve(self.len, additional);
121 }
122 #[inline]
123 const fn as_ptr(&self) -> *const u8 {
124 self.buf.ptr()
127 }
128 #[inline]
129 const fn as_mut_ptr(&mut self) -> *mut u8 {
130 self.buf.ptr()
133 }
134 #[inline]
135 pub fn allocator(&self) -> &A {
136 self.buf.allocator()
137 }
138 #[inline]
139 pub unsafe fn set_len(&mut self, new_len: usize) {
140 debug_assert!(new_len <= self.capacity());
141
142 self.len = new_len;
143 }
144 pub fn push(&mut self, value: #ident){
145 let len = self.len;
147 if len == self.buf.capacity() {
150 self.buf.grow_one();
151 }
152 unsafe {
153 #(
154 let end = self.as_mut_ptr()
155 .add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
156 .cast::<#field_types>()
157 .add(len);
158 ::core::ptr::write(end, value.#field_idents);
159 )*
160 }
161 self.len = len + 1;
162 }
163 #[inline]
164 #[track_caller]
165 pub fn append(&mut self, other: &mut Self) {
166 unsafe {
167 self.append_elements(other);
168 other.set_len(0);
169 }
170 }
171
172 #[inline]
174 #[track_caller]
175 unsafe fn append_elements(&mut self, other: &Self) {
176 let count = other.len();
177 self.reserve(count);
178 let len = self.len();
179 unsafe {
180 <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.move_fields(
181 other.as_ptr(),
182 self.as_mut_ptr(),
183 other.capacity(),
184 self.capacity(),
185 len,
186 count,
187 )
188 }
189 self.len += count;
190 }
191 #[inline]
192 pub const fn len(&self) -> usize {
193 self.len
194 }
195 }
196 };
197
198 let field_slice_mut_fn_idents=fields.named.iter().map(|field|{
199 let ident=field.ident.as_ref().unwrap();
200 let slice_ident=format!("{ident}_slice_mut");
201 syn::Ident::new(&slice_ident,ident.span())
202 });
203 let drop = quote! {
204 impl<A: ::colvec::alloc::Allocator> Drop for #colvec_ident<A>{
205 fn drop(&mut self){
206 unsafe {
207 #(
208 ::core::ptr::drop_in_place(self.#field_slice_mut_fn_idents());
209 )*
210 }
211 }
212 }
213 };
214
215 let field_indices=0..fields_count;
216 let field_types1=fields.named.iter().map(|field|field.ty.clone());
217 let field_types2=field_types1.clone();
218 let field_idents1=fields.named.iter().map(|field|field.ident.as_ref().unwrap().clone());
219 let field_idents2=field_idents1.clone();
220 let field_slice_fn_idents1=fields.named.iter().map(|field|{
221 let ident=field.ident.as_ref().unwrap();
222 let slice_ident=format!("{ident}_slice");
223 syn::Ident::new(&slice_ident,ident.span())
224 });
225 let field_slice_fn_idents2=field_slice_fn_idents1.clone();
226 let field_slice_fn_idents3=field_slice_fn_idents1.clone();
227 let field_slice_mut_fn_idents1=fields.named.iter().map(|field|{
228 let ident=field.ident.as_ref().unwrap();
229 let slice_ident=format!("{ident}_slice_mut");
230 syn::Ident::new(&slice_ident,ident.span())
231 });
232 let field_slice_mut_fn_idents2=field_slice_mut_fn_idents1.clone();
233 let clone = quote! {
234 impl<A: ::colvec::alloc::Allocator + Clone> Clone for #colvec_ident<A>
235 where
236 #(#field_types1: Clone,)*
237 {
238 fn clone(&self)->Self{
239 let alloc = self.allocator().clone();
240 struct DropGuard<'a, A: ::colvec::alloc::Allocator> {
241 colvec: &'a mut #colvec_ident<A>,
242 num_init: usize,
243 }
244 impl<'a, A: ::colvec::alloc::Allocator> Drop for DropGuard<'a, A> {
245 #[inline]
246 fn drop(&mut self) {
247 unsafe {
250 self.colvec.set_len(self.num_init);
251 }
252 }
253 }
254 let mut colvec = Self::with_capacity_in(self.len(), alloc);
255 let mut guard = DropGuard { colvec: &mut colvec, num_init: 0 };
256 #(
257 let #field_slice_fn_idents1 = self.#field_slice_fn_idents2();
258 )*
259 let cap = guard.colvec.buf.capacity();
261 let ptr = guard.colvec.as_mut_ptr();
262 #(
263 let #field_slice_mut_fn_idents1 = unsafe {
264 ::core::slice::from_raw_parts_mut(
265 ptr.add(cap * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
266 .cast::<::core::mem::MaybeUninit<#field_types2>>(),
267 self.len
268 )
269 };
270 )*
271 for i in 0..self.len() {
272 guard.num_init = i;
273 #(
275 let #field_idents1 = unsafe { #field_slice_fn_idents3.get_unchecked(i) }.clone();
276 )*
277 #(
278 unsafe{ #field_slice_mut_fn_idents2.get_unchecked_mut(i).write(#field_idents2) };
279 )*
280 }
281 ::core::mem::forget(guard);
282 unsafe {
285 colvec.set_len(self.len());
286 }
287 colvec
288 }
289 }
290 };
291
292 let field_indices=0..fields_count;
293 let field_types=fields.named.iter().map(|field|field.ty.clone());
294 let field_slice_fn_idents=fields.named.iter().map(|field|{
295 let ident=field.ident.as_ref().unwrap();
296 let slice_ident=format!("{ident}_slice");
297 syn::Ident::new(&slice_ident,ident.span())
298 });
299 let field_slice_mut_fn_idents=fields.named.iter().map(|field|{
300 let ident=field.ident.as_ref().unwrap();
301 let slice_ident=format!("{ident}_slice_mut");
302 syn::Ident::new(&slice_ident,ident.span())
303 });
304 let field_access = quote! {
305 impl<A: ::colvec::alloc::Allocator> #colvec_ident<A>{
306 #(
307 #[inline]
308 pub const fn #field_slice_fn_idents(&self) -> &[#field_types] {
309 unsafe {
310 ::core::slice::from_raw_parts(
311 self.as_ptr()
312 .add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
313 .cast::<#field_types>(),
314 self.len
315 )
316 }
317 }
318 #[inline]
319 pub const fn #field_slice_mut_fn_idents(&mut self) -> &mut [#field_types] {
320 unsafe {
321 ::core::slice::from_raw_parts_mut(
322 self.as_mut_ptr()
323 .add(self.buf.capacity() * <#ident as ::colvec::raw::StructInfo<#fields_count>>::FIELDS.offset_of(#field_indices))
324 .cast::<#field_types>(),
325 self.len
326 )
327 }
328 }
329 )*
330 }
331 };
332
333 #[cfg_attr(not(feature = "std"), expect(unused_mut))]
334 let mut output=quote! {
335 #colvec
336
337 #struct_info
338
339 #drop
340 #clone
341 #impls
342 #field_access
343 };
344
345 #[cfg(feature = "std")]
346 output.extend(global);
347
348 output.into()
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354 use syn::parse_quote;
355
356 #[test]
357 fn snapshot_test1() {
358 let test1:syn::ItemStruct = parse_quote! {
359 pub struct Test{
360 field0:u8,
361 field1:Option<u8>,
362 field2:i16,
363 field3:u32,
364 }
365 };
366
367 let output = colvec_derive_inner(test1.into());
368
369 let as_file = syn::parse_file(&output.to_string()).unwrap();
371
372 let formatted = prettyplease::unparse(&as_file);
374
375 insta::assert_snapshot!(formatted);
377 }
378}