1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#![recursion_limit="512"]

//! This crate provides a custom derive (`#[derive(StructOfArray)]`) to
//! automatically generate code from a given struct `T` that allow to replace
//! `Vec<T>` with a struct of arrays. For example, the following code
//!
//! ```ignore
//! #[derive(StructOfArray)]
//! pub struct Particle {
//!     pub x: f64,
//!     pub y: f64,
//!     pub z: f64,
//! }
//! ```
//!
//! will generate a `ParticleVec` struct that looks like this:
//!
//! ```ignore
//! pub struct ParticleVec {
//!     pub x: Vec<f64>,
//!     pub y: Vec<f64>,
//!     pub z: Vec<f64>,
//! }
//! ```
//!
//! It will also generate the same functions that a `Vec<Particle>` would have,
//! and a few helper structs: `ParticleSlice`, `ParticleSliceMut`,
//! `ParticleRef` and `ParticleRefMut`.
//!
//! # How to use it
//!
//! Add `#[derive(StructOfArray)]` to each struct you want to derive a struct
//! of array version. If you need the helper structs to derive additional
//! traits (such as `Debug` or `PartialEq`), you can add an attribute
//! `#[soa_derive = "Debug, PartialEq"]` to the struct declaration.
//!
//! ```ignore
//! #[derive(Debug, PartialEq, StructOfArray)]
//! #[soa_derive = "Debug, PartialEq"]
//! pub struct Particle {
//!     pub x: f64,
//!     pub y: f64,
//!     pub z: f64,
//! }
//! ```
//!
//! # Caveats and limitations
//!
//! `Vec<T>` functionalities rely a lot on references and automatic *deref*
//! feature, for getting function from `[T]` and indexing. But the SoA vector
//! (let's call it `CheeseVec`, generated from the `Cheese` struct) generated
//! by this crate can not implement `Deref<Target=CheeseSlice>`, because
//! `Deref` is required to return a reference, and `CheeseSlice` is not a
//! reference. The same applies to `Index` and `IndexMut` trait, that can not
//! return `CheeseRef/CheeseRefMut`.
//!
//! This means that the we can not index into a `CheeseVec`, and that a few
//! functions are duplicated, or require a call to `as_ref()/as_mut()` to
//! change the type used.
//!
//! # Documentation
//!
//! Please see http://lumol.org/soa-derive/soa_derive_example/ for a small
//! example and the documentation of all the generated code.

extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;

use proc_macro::TokenStream;

mod structs;
mod vec;
mod refs;
mod slice;
mod iter;

#[proc_macro_derive(StructOfArray, attributes(soa_derive))]
pub fn soa_derive(input: TokenStream) -> TokenStream {
    let source = input.to_string();
    let ast = syn::parse_macro_input(&source).unwrap();
    let input = structs::Struct::new(ast);

    let mut generated = quote::Tokens::new();
    generated.append(vec::derive(&input).as_str());
    generated.append(refs::derive(&input).as_str());
    generated.append(slice::derive_slice(&input).as_str());
    generated.append(slice::derive_slice_mut(&input).as_str());
    generated.append(iter::derive(&input).as_str());
    generated.parse().unwrap()
}