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 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 self.buf.ptr()
92 }
93 #[inline]
94 const fn as_mut_ptr(&mut self) -> *mut u8 {
95 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 let len = self.len;
108 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 #[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 let as_file = syn::parse_file(&output.to_string()).unwrap();
231
232 let formatted = prettyplease::unparse(&as_file);
234
235 insta::assert_snapshot!(formatted);
237 }
238}