├── .github └── workflows │ ├── docs.yml │ └── tests.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches └── soa.rs ├── build.rs ├── example ├── Cargo.toml ├── index.html └── lib.rs ├── rustfmt.toml ├── soa-derive-internal ├── Cargo.toml └── src │ ├── generic.rs │ ├── index.rs │ ├── input.rs │ ├── iter.rs │ ├── lib.rs │ ├── names.rs │ ├── ptr.rs │ ├── refs.rs │ ├── slice.rs │ └── vec.rs ├── src └── lib.rs └── tests ├── extreme.rs ├── generic.rs ├── index.rs ├── iter.rs ├── nested_soa.rs ├── particles └── mod.rs ├── ptr.rs ├── replace.rs ├── serde.rs ├── slice.rs ├── slice_mut.rs ├── soa_attr.rs ├── vec.rs └── zip.rs /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | tags: ["*"] 7 | pull_request: 8 | # Check all PR 9 | 10 | jobs: 11 | build-and-publish: 12 | runs-on: ubuntu-20.04 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: true 17 | - name: setup rust 18 | uses: dtolnay/rust-toolchain@master 19 | with: 20 | toolchain: stable 21 | - name: build documentation 22 | run: | 23 | cd example 24 | cargo doc --no-deps 25 | - name: put documentation in the website 26 | run: | 27 | git clone https://github.com/$GITHUB_REPOSITORY --branch gh-pages gh-pages 28 | rm -rf gh-pages/.git 29 | cd gh-pages 30 | 31 | REF_KIND=$(echo $GITHUB_REF | cut -d / -f2) 32 | if [[ "$REF_KIND" == "tags" ]]; then 33 | TAG=${GITHUB_REF#refs/tags/} 34 | mv ../target/doc/ $TAG 35 | cp ../example/index.html $TAG/index.html 36 | else 37 | rm -rf latest 38 | mv ../target/doc/ latest 39 | cp ../example/index.html latest/index.html 40 | fi 41 | - name: deploy to gh-pages 42 | if: github.event_name == 'push' 43 | uses: peaceiris/actions-gh-pages@v3 44 | with: 45 | github_token: ${{ secrets.GITHUB_TOKEN }} 46 | publish_dir: ./gh-pages/ 47 | force_orphan: true 48 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | # Check all PR 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-20.04 12 | strategy: 13 | matrix: 14 | rust-version: ["1.65", stable, beta, nightly] 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: true 19 | - name: setup rust 20 | uses: dtolnay/rust-toolchain@master 21 | with: 22 | toolchain: ${{ matrix.rust-version }} 23 | - name: run tests in debug mode 24 | run: cargo test 25 | - name: run tests in release mode 26 | run: cargo test --release 27 | - name: check that benchmarks still compile 28 | run: cargo bench --no-run 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | rust-toolchain.toml -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soa_derive" 3 | version = "0.13.0" 4 | edition = "2018" 5 | rust-version = "1.65" 6 | 7 | authors = ["Guillaume Fraux "] 8 | license = "MIT/Apache-2.0" 9 | 10 | readme = "README.md" 11 | description = "Automatic Struct of Array generation" 12 | repository = "https://github.com/lumol-org/soa-derive" 13 | homepage = "https://github.com/lumol-org/soa-derive" 14 | documentation = "https://docs.rs/soa_derive/" 15 | 16 | [workspace] 17 | members = [ 18 | "soa-derive-internal", 19 | "example", 20 | ] 21 | 22 | [dependencies] 23 | soa_derive_internal = {path = "soa-derive-internal", version = "0.13"} 24 | permutation = "0.4.0" 25 | 26 | [dev-dependencies] 27 | bencher = "0.1" 28 | serde = { version = "1", features = ["derive"] } 29 | serde_json = "1" 30 | itertools = "0.14.0" 31 | 32 | [build-dependencies] 33 | rustc_version = "0.4" 34 | 35 | [[bench]] 36 | name = "soa" 37 | harness = false 38 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 The Rust Project Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automatic Struct of Array generation for Rust 2 | 3 | [![Test](https://github.com/lumol-org/soa-derive/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/lumol-org/soa-derive/actions/workflows/tests.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/soa_derive.svg)](https://crates.io/crates/soa_derive) 5 | 6 | This crate provides a custom derive (`#[derive(StructOfArray)]`) to 7 | automatically generate code from a given struct `T` that allow to replace 8 | `Vec` with a struct of arrays. For example, the following code 9 | 10 | ```rust 11 | #[derive(StructOfArray)] 12 | pub struct Cheese { 13 | pub smell: f64, 14 | pub color: (f64, f64, f64), 15 | pub with_mushrooms: bool, 16 | pub name: String, 17 | } 18 | ``` 19 | 20 | will generate a `CheeseVec` struct that looks like this: 21 | 22 | ```rust 23 | pub struct CheeseVec { 24 | pub smell: Vec, 25 | pub color: Vec<(f64, f64, f64)>, 26 | pub with_mushrooms: Vec, 27 | pub name: Vec, 28 | } 29 | ``` 30 | 31 | It will also generate the same functions that a `Vec` would have, and a 32 | few helper structs: `CheeseSlice`, `CheeseSliceMut`, `CheeseRef` and 33 | `CheeseRefMut` corresponding respectivly to `&[Cheese]`, `&mut [Cheese]`, 34 | `&Cheese` and `&mut Cheese`. 35 | 36 | Any struct derived by StructOfArray will auto impl trait `StructOfArray`. 37 | You can use `::Type` instead of the explicitly named type `CheeseVec`. 38 | 39 | ## How to use it 40 | 41 | Add `#[derive(StructOfArray)]` to each struct you want to derive a struct of 42 | array version. If you need the helper structs to derive additional traits (such 43 | as `Debug` or `PartialEq`), you can add an attribute `#[soa_derive(Debug, 44 | PartialEq)]` to the struct declaration. 45 | 46 | ```rust 47 | #[derive(Debug, PartialEq, StructOfArray)] 48 | #[soa_derive(Debug, PartialEq)] 49 | pub struct Cheese { 50 | pub smell: f64, 51 | pub color: (f64, f64, f64), 52 | pub with_mushrooms: bool, 53 | pub name: String, 54 | } 55 | ``` 56 | 57 | If you want to add attribute to a specific generated struct(such as 58 | `#[cfg_attr(test, derive(PartialEq))]` on `CheeseVec`), you can add an 59 | attribute `#[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))]` to the 60 | struct declaration. 61 | 62 | ```rust 63 | #[derive(Debug, PartialEq, StructOfArray)] 64 | #[soa_attr(Vec, cfg_attr(test, derive(PartialEq)))] 65 | pub struct Cheese { 66 | pub smell: f64, 67 | pub color: (f64, f64, f64), 68 | pub with_mushrooms: bool, 69 | pub name: String, 70 | } 71 | ``` 72 | 73 | Mappings for first argument of ``soa_attr`` to the generated struct for ``Cheese``: 74 | * `Vec` => `CheeseVec` 75 | * `Slice` => `CheeseSlice` 76 | * `SliceMut` => `CheeseSliceMut` 77 | * `Ref` => `CheeseRef` 78 | * `RefMut` => `CheeseRefMut` 79 | * `Ptr` => `CheesePtr` 80 | * `PtrMut` => `CheesePtrMut` 81 | 82 | ## Usage and API 83 | 84 | All the generated code have some generated documentation with it, so you 85 | should be able to use `cargo doc` on your crate and see the documentation 86 | for all the generated structs and functions. 87 | Most of the time, you should be able to replace `Vec` by 88 | `CheeseVec`, with exception of code using direct indexing in the vector and 89 | a few other caveats listed below. 90 | 91 | ### Caveats and limitations 92 | 93 | `Vec` functionalities rely a lot on references and automatic *deref* feature, 94 | for getting function from `[T]` and indexing. But the SoA vector (let's call it 95 | `CheeseVec`, generated from the `Cheese` struct) generated by this crate can not 96 | implement `Deref`, because `Deref` is required to return a 97 | reference, and `CheeseSlice` is not a reference. The same applies to `Index` and 98 | `IndexMut` trait, that can not return `CheeseRef/CheeseRefMut`. This means that 99 | the we can not index into a `CheeseVec`, and that a few functions are 100 | duplicated, or require a call to `as_ref()/as_mut()` to change the type used. 101 | 102 | ## Iteration 103 | 104 | It is possible to iterate over the values in a `CheeseVec` 105 | 106 | ```rust 107 | let mut vec = CheeseVec::new(); 108 | vec.push(Cheese::new("stilton")); 109 | vec.push(Cheese::new("brie")); 110 | 111 | for cheese in vec.iter() { 112 | // when iterating over a CheeseVec, we load all members from memory 113 | // in a CheeseRef 114 | let typeof_cheese: CheeseRef = cheese; 115 | println!("this is {}, with a smell power of {}", cheese.name, cheese.smell); 116 | } 117 | ``` 118 | One of the main advantage of the SoA layout is to be able to only load some 119 | fields from memory when iterating over the vector. In order to do so, one 120 | can manually pick the needed fields: 121 | 122 | ```rust 123 | for name in &vec.name { 124 | // We get referenes to the names 125 | let typeof_name: &String = name; 126 | println!("got cheese {}", name); 127 | } 128 | ``` 129 | 130 | In order to iterate over multiple fields at the same time, one can use the 131 | [soa_zip!](https://docs.rs/soa_derive/*/soa_derive/macro.soa_zip.html) macro. 132 | 133 | ```rust 134 | for (name, smell, color) in soa_zip!(vec, [name, mut smell, color]) { 135 | println!("this is {}, with color {:#?}", name, color); 136 | // smell is a mutable reference 137 | *smell += 1.0; 138 | } 139 | ``` 140 | 141 | ## Nested Struct of Arrays 142 | 143 | In order to nest a struct of arrays inside another struct of arrays, one can use the `#[nested_soa]` attribute. 144 | 145 | For example, the following code 146 | 147 | ```rust 148 | #[derive(StructOfArray)] 149 | pub struct Point { 150 | x: f32, 151 | y: f32, 152 | } 153 | #[derive(StructOfArray)] 154 | pub struct Particle { 155 | #[nested_soa] 156 | point: Point, 157 | mass: f32, 158 | } 159 | ``` 160 | 161 | will generate structs that looks like this: 162 | 163 | ```rust 164 | pub struct PointVec { 165 | x: Vec, 166 | y: Vec, 167 | } 168 | pub struct ParticleVec { 169 | point: PointVec, // rather than Vec 170 | mass: Vec 171 | } 172 | ``` 173 | 174 | All helper structs will be also nested, for example `PointSlice` will be nested in `ParticleSlice`. 175 | 176 | ## Documentation 177 | 178 | Please see http://lumol.org/soa-derive/soa_derive_example/ for a small 179 | example and the documentation of all the generated code. 180 | 181 | ## Benchmarks 182 | 183 | Here are a few [simple benchmarks](benches/soa.rs) results, on my machine: 184 | 185 | ``` 186 | running 10 tests 187 | test aos_big_do_work_100k ... bench: 415,315 ns/iter (+/- 72,861) 188 | test aos_big_do_work_10k ... bench: 10,087 ns/iter (+/- 219) 189 | test aos_big_push ... bench: 50 ns/iter (+/- 10) 190 | test aos_small_do_work_100k ... bench: 93,377 ns/iter (+/- 1,106) 191 | test aos_small_push ... bench: 3 ns/iter (+/- 1) 192 | test soa_big_do_work_100k ... bench: 93,719 ns/iter (+/- 2,793) 193 | test soa_big_do_work_10k ... bench: 9,253 ns/iter (+/- 103) 194 | test soa_big_push ... bench: 39 ns/iter (+/- 13) 195 | test soa_small_do_work_100k ... bench: 93,301 ns/iter (+/- 1,765) 196 | test soa_small_push ... bench: 4 ns/iter (+/- 1) 197 | ``` 198 | 199 | Benchmarks tests exist for soa (struct of array) and aos (array of struct) 200 | versions of the same code, using a small (24 bytes) and a big (240 bytes) struct. 201 | 202 | You can run the same benchmarks on your own system by cloning this repository 203 | and running `cargo bench`. 204 | 205 | ## Licensing and contributions 206 | 207 | This crate distributed under either the MIT or the Apache license, at your 208 | choice. Contributions are welcome, please open an issue before to discuss your 209 | changes ! 210 | 211 | Thanks to @maikklein for the initial idea: https://maikklein.github.io/soa-rust/ 212 | -------------------------------------------------------------------------------- /benches/soa.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::needless_return)] 2 | 3 | use soa_derive::StructOfArray; 4 | 5 | use bencher::{Bencher, benchmark_group, benchmark_main}; 6 | 7 | #[derive(StructOfArray)] 8 | pub struct Small { 9 | x: f64, 10 | y: f64, 11 | z: f64, 12 | } 13 | 14 | impl Small { 15 | fn new() -> Small { 16 | Small { 17 | x: 1.0, 18 | y: 0.2, 19 | z: -2.3, 20 | } 21 | } 22 | 23 | fn aos_vec(size: usize) -> Vec { 24 | let mut vec = Vec::new(); 25 | for _ in 0..size { 26 | vec.push(Small::new()) 27 | } 28 | return vec; 29 | } 30 | 31 | fn soa_vec(size: usize) -> SmallVec { 32 | let mut vec = SmallVec::new(); 33 | for _ in 0..size { 34 | vec.push(Small::new()) 35 | } 36 | return vec; 37 | } 38 | } 39 | 40 | #[derive(StructOfArray)] 41 | pub struct Big { 42 | position: (f64, f64, f64), 43 | velocity: (f64, f64, f64), 44 | data: [usize; 18], 45 | name: String, 46 | userdata: String 47 | } 48 | 49 | impl Big { 50 | fn new() -> Big { 51 | Big { 52 | position: (1.0, 0.2, -2.3), 53 | velocity: (1.0, 0.2, -2.3), 54 | data: [67; 18], 55 | name: "foo".into(), 56 | userdata: "bar".into() 57 | } 58 | } 59 | 60 | fn aos_vec(size: usize) -> Vec { 61 | let mut vec = Vec::new(); 62 | for _ in 0..size { 63 | vec.push(Big::new()) 64 | } 65 | return vec; 66 | } 67 | 68 | fn soa_vec(size: usize) -> BigVec { 69 | let mut vec = BigVec::new(); 70 | for _ in 0..size { 71 | vec.push(Big::new()) 72 | } 73 | return vec; 74 | } 75 | } 76 | 77 | fn aos_small_push(bencher: &mut Bencher) { 78 | let mut vec = Vec::new(); 79 | bencher.iter(||{ 80 | vec.push(Small::new()) 81 | }) 82 | } 83 | 84 | fn soa_small_push(bencher: &mut Bencher) { 85 | let mut vec = SmallVec::new(); 86 | bencher.iter(||{ 87 | vec.push(Small::new()) 88 | }) 89 | } 90 | 91 | fn aos_big_push(bencher: &mut Bencher) { 92 | let mut vec = Vec::new(); 93 | bencher.iter(||{ 94 | vec.push(Big::new()) 95 | }) 96 | } 97 | 98 | fn soa_big_push(bencher: &mut Bencher) { 99 | let mut vec = BigVec::new(); 100 | bencher.iter(||{ 101 | vec.push(Big::new()) 102 | }) 103 | } 104 | 105 | fn aos_small_do_work_100k(bencher: &mut Bencher) { 106 | let vec = Small::aos_vec(100_000); 107 | bencher.iter(||{ 108 | let mut s = 0.0; 109 | for v in &vec { 110 | s += v.x + v.y; 111 | } 112 | s 113 | }) 114 | } 115 | 116 | fn soa_small_do_work_100k(bencher: &mut Bencher) { 117 | let vec = Small::soa_vec(100_000); 118 | bencher.iter(||{ 119 | let mut s = 0.0; 120 | for (x, y) in vec.x.iter().zip(&vec.y) { 121 | s += x + y; 122 | } 123 | s 124 | }) 125 | } 126 | 127 | fn aos_big_do_work_10k(bencher: &mut Bencher) { 128 | let vec = Big::aos_vec(10_000); 129 | bencher.iter(||{ 130 | let mut s = 0.0; 131 | for v in &vec { 132 | s += v.position.0 + v.velocity.0 * 0.1; 133 | } 134 | s 135 | }) 136 | } 137 | 138 | fn aos_big_do_work_100k(bencher: &mut Bencher) { 139 | let vec = Big::aos_vec(100_000); 140 | bencher.iter(||{ 141 | let mut s = 0.0; 142 | for v in &vec { 143 | s += v.position.0 + v.velocity.0 * 0.1; 144 | } 145 | s 146 | }) 147 | } 148 | 149 | fn soa_big_do_work_10k(bencher: &mut Bencher) { 150 | let vec = Big::soa_vec(10_000); 151 | bencher.iter(||{ 152 | let mut s = 0.0; 153 | for (position, velocity) in vec.position.iter().zip(&vec.velocity) { 154 | s += position.0 + velocity.0; 155 | } 156 | s 157 | }) 158 | } 159 | 160 | fn soa_big_do_work_100k(bencher: &mut Bencher) { 161 | let vec = Big::soa_vec(100_000); 162 | bencher.iter(||{ 163 | let mut s = 0.0; 164 | for (position, velocity) in vec.position.iter().zip(&vec.velocity) { 165 | s += position.0 + velocity.0; 166 | } 167 | s 168 | }) 169 | } 170 | 171 | 172 | benchmark_group!(aos, 173 | aos_small_push, aos_big_push, aos_small_do_work_100k, aos_big_do_work_10k, 174 | aos_big_do_work_100k 175 | ); 176 | benchmark_group!(soa, 177 | soa_small_push, soa_big_push, soa_small_do_work_100k, soa_big_do_work_10k, 178 | soa_big_do_work_100k 179 | ); 180 | benchmark_main!(soa, aos); 181 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | use rustc_version::Version; 2 | 3 | fn main() { 4 | if rustc_version::version().unwrap() >= Version::parse("1.78.0").unwrap() { 5 | println!("cargo:rustc-cfg=rustc_is_at_least_1_78"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soa-derive-example" 3 | version = "0.0.0" 4 | authors = ["Luthaf "] 5 | repository = "https://github.com/lumol-org/soa-derive" 6 | license = "BSD" 7 | publish = false 8 | edition = "2021" 9 | 10 | [dependencies] 11 | soa_derive = { path = ".." } 12 | 13 | [lib] 14 | path = "lib.rs" 15 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /example/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate is an example for the [`soa_derive`] crate functionalities. All 2 | //! the code is generated by a single file: 3 | //! 4 | //! ```no_run 5 | //! extern crate soa_derive; 6 | //! # mod particle { 7 | //! #[macro_use] 8 | //! use soa_derive::StructOfArray; 9 | //! 10 | //! /// A basic Particle type 11 | //! #[derive(Debug, PartialEq, StructOfArray)] 12 | //! #[soa_derive(Debug, PartialEq)] 13 | //! pub struct Particle { 14 | //! /// Mass of the particle 15 | //! pub mass: f64, 16 | //! /// Position of the particle 17 | //! pub position: [f64; 3], 18 | //! /// Kind of the particle 19 | //! pub kind: usize, 20 | //! /// Name of the particle 21 | //! pub name: String, 22 | //! } 23 | //! # } 24 | //! ``` 25 | //! 26 | //! [`soa_derive`]: https://github.com/lumol-org/soa-derive/ 27 | 28 | // Deny most of allow by default lints, just to be sure we don't create warning in user code. 29 | // They are to be selectively allowed in the implementation 30 | #![deny(absolute_paths_not_starting_with_crate, anonymous_parameters, bare_trait_objects)] 31 | #![deny(missing_copy_implementations, missing_debug_implementations)] 32 | #![deny(missing_docs, trivial_casts, trivial_numeric_casts, unreachable_pub)] 33 | #![deny(unstable_features, unused_extern_crates, unused_import_braces, unused_labels)] 34 | #![deny(unused_lifetimes, unused_qualifications, unused_results, variant_size_differences)] 35 | 36 | // Other allow by default lints that need to stay allowed 37 | #![allow(unsafe_code, single_use_lifetimes, elided_lifetimes_in_paths)] 38 | 39 | #![deny(warnings)] 40 | 41 | #[macro_use] 42 | extern crate soa_derive; 43 | 44 | /// A basic Particle type 45 | #[derive(Debug, PartialEq, StructOfArray)] 46 | #[soa_derive(Debug, PartialEq)] 47 | pub struct Particle { 48 | /// Mass of the particle 49 | pub mass: f64, 50 | /// Position of the particle 51 | pub position: [f64; 3], 52 | /// Kind of the particle 53 | pub kind: usize, 54 | /// Name of the particle 55 | pub name: String, 56 | } 57 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | disable_all_formatting = true 2 | -------------------------------------------------------------------------------- /soa-derive-internal/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "soa_derive_internal" 3 | version = "0.13.4" 4 | edition = "2018" 5 | rust-version = "1.65" 6 | 7 | authors = ["Guillaume Fraux "] 8 | license = "MIT/Apache-2.0" 9 | 10 | readme = "../README.md" 11 | description = "Internal implementation crate for soa-derive" 12 | 13 | repository = "https://github.com/lumol-org/soa-derive" 14 | homepage = "https://github.com/lumol-org/soa-derive" 15 | documentation = "https://docs.rs/soa_derive/" 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | 21 | [dependencies] 22 | syn = {version = "2", features = ["derive", "extra-traits"]} 23 | quote = "1" 24 | proc-macro2 = "1" 25 | -------------------------------------------------------------------------------- /soa-derive-internal/src/generic.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::input::Input; 5 | use crate::names; 6 | 7 | 8 | pub fn derive_slice(input: &Input) -> TokenStream { 9 | let name = &input.name; 10 | let slice_name = names::slice_name(name); 11 | let ref_name = names::ref_name(&input.name); 12 | let ptr_name = names::ptr_name(&input.name); 13 | let iter_name = names::iter_name(name); 14 | 15 | let generated = quote! { 16 | impl<'a> ::soa_derive::SoASlice<#name> for #slice_name<'a> { 17 | type Ref<'t> = #ref_name<'t> where Self: 't, 'a: 't; 18 | type Slice<'t> = #slice_name<'t> where Self: 't, 'a: 't; 19 | type Iter<'t> = #iter_name<'t> where Self: 't, 'a: 't; 20 | type Ptr = #ptr_name; 21 | 22 | fn len(&self) -> usize { 23 | self.len() 24 | } 25 | 26 | fn is_empty(&self) -> bool { 27 | self.is_empty() 28 | } 29 | 30 | fn as_slice<'c>(&'c self) -> Self::Slice<'c> { 31 | self.reborrow::<'c>() 32 | } 33 | 34 | fn slice<'c, 'b: 'c>(&'c self, index: impl core::ops::RangeBounds) -> Self::Slice<'c> where Self: 'b { 35 | let start = match index.start_bound() { 36 | std::ops::Bound::Included(i) | std::ops::Bound::Excluded(i) => *i, 37 | std::ops::Bound::Unbounded => 0, 38 | }; 39 | let n = self.len(); 40 | let end = match index.end_bound() { 41 | std::ops::Bound::Included(i) => (*i + 1).min(n), 42 | std::ops::Bound::Excluded(i) => *i, 43 | std::ops::Bound::Unbounded => n, 44 | }; 45 | self.index(start..end) 46 | } 47 | 48 | fn get<'c>(&'c self, index: usize) -> Option> { 49 | self.get(index) 50 | } 51 | 52 | fn index<'c>(&'c self, index: usize) -> Self::Ref<'c> { 53 | self.index(index) 54 | } 55 | 56 | fn iter<'c>(&'c self) -> Self::Iter<'c> { 57 | self.iter() 58 | } 59 | 60 | fn as_ptr(&self) -> Self::Ptr { 61 | self.as_ptr() 62 | } 63 | } 64 | }; 65 | 66 | return generated 67 | } 68 | 69 | pub fn derive_slice_mut(input: &Input) -> TokenStream { 70 | let name = &input.name; 71 | let slice_name = names::slice_name(name); 72 | let slice_mut_name = names::slice_mut_name(&input.name); 73 | let ref_name = names::ref_name(&input.name); 74 | let ref_mut_name = names::ref_mut_name(&input.name); 75 | let ptr_name = names::ptr_name(&input.name); 76 | let ptr_mut_name = names::ptr_mut_name(&input.name); 77 | let iter_name = names::iter_name(name); 78 | let iter_mut_name = names::iter_mut_name(name); 79 | 80 | let generated = quote! { 81 | 82 | impl<'a> ::soa_derive::SoASliceMut<#name> for #slice_mut_name<'a> { 83 | type Ref<'t> = #ref_name<'t> where Self: 't; 84 | type Slice<'t> = #slice_name<'t> where Self: 't; 85 | type Iter<'t> = #iter_name<'t> where Self: 't; 86 | type Ptr = #ptr_name; 87 | 88 | type RefMut<'t> = #ref_mut_name<'t> where Self: 't; 89 | type SliceMut<'t> = #slice_mut_name<'t> where Self: 't; 90 | type IterMut<'t> = #iter_mut_name<'t> where Self: 't; 91 | type PtrMut = #ptr_mut_name; 92 | 93 | fn len(&self) -> usize { 94 | self.len() 95 | } 96 | 97 | fn is_empty(&self) -> bool { 98 | self.is_empty() 99 | } 100 | 101 | fn as_slice<'c>(&'c self) -> Self::Slice<'c> { 102 | self.as_slice() 103 | } 104 | 105 | fn slice<'c, 'b: 'c>(&'c self, index: impl core::ops::RangeBounds) -> Self::Slice<'c> where Self: 'b { 106 | let start = match index.start_bound() { 107 | std::ops::Bound::Included(i) | std::ops::Bound::Excluded(i) => *i, 108 | std::ops::Bound::Unbounded => 0, 109 | }; 110 | let n = self.len(); 111 | let end = match index.end_bound() { 112 | std::ops::Bound::Included(i) => (*i + 1).min(n), 113 | std::ops::Bound::Excluded(i) => *i, 114 | std::ops::Bound::Unbounded => n, 115 | }; 116 | self.index(start..end) 117 | } 118 | 119 | fn get<'c>(&'c self, index: usize) -> Option> { 120 | self.get(index) 121 | } 122 | 123 | fn index<'c>(&'c self, index: usize) -> Self::Ref<'c> { 124 | self.index(index) 125 | } 126 | 127 | fn iter<'c>(&'c self) -> Self::Iter<'c> { 128 | self.as_ref().into_iter() 129 | } 130 | 131 | fn as_mut_slice<'c: 'b, 'b>(&'c mut self) -> Self::SliceMut<'c> where Self: 'b { 132 | self.reborrow() 133 | } 134 | 135 | fn slice_mut<'c>(&'c mut self, index: impl core::ops::RangeBounds) -> Self::SliceMut<'c> { 136 | let start = match index.start_bound() { 137 | std::ops::Bound::Included(i) | std::ops::Bound::Excluded(i) => *i, 138 | std::ops::Bound::Unbounded => 0, 139 | }; 140 | let n = self.len(); 141 | let end = match index.end_bound() { 142 | std::ops::Bound::Included(i) => (*i + 1).min(n), 143 | std::ops::Bound::Excluded(i) => *i, 144 | std::ops::Bound::Unbounded => n, 145 | }; 146 | self.index_mut(start..end) 147 | } 148 | 149 | fn get_mut<'c>(&'c mut self, index: usize) -> Option> { 150 | self.get_mut(index) 151 | } 152 | 153 | fn index_mut<'c>(&'c mut self, index: usize) -> Self::RefMut<'c> { 154 | self.index_mut(index) 155 | } 156 | 157 | fn iter_mut<'c>(&'c mut self) -> Self::IterMut<'c> { 158 | self.iter_mut() 159 | } 160 | 161 | fn apply_index(&mut self, indices: &[usize]) { 162 | self.__private_apply_permutation(&mut ::soa_derive::Permutation::oneline(indices).inverse()); 163 | } 164 | 165 | fn as_ptr(&self) -> Self::Ptr { 166 | self.as_ptr() 167 | } 168 | 169 | fn as_mut_ptr(&mut self) -> Self::PtrMut { 170 | self.as_mut_ptr() 171 | } 172 | } 173 | }; 174 | 175 | return generated 176 | } 177 | 178 | pub fn derive_vec(input: &Input) -> TokenStream { 179 | let name = &input.name; 180 | let vec_name = names::vec_name(&input.name); 181 | let slice_name = names::slice_name(name); 182 | let slice_mut_name = names::slice_mut_name(&input.name); 183 | let ref_name = names::ref_name(&input.name); 184 | let ref_mut_name = names::ref_mut_name(&input.name); 185 | let ptr_name = names::ptr_name(&input.name); 186 | let ptr_mut_name = names::ptr_mut_name(&input.name); 187 | let iter_name = names::iter_name(name); 188 | let iter_mut_name = names::iter_mut_name(name); 189 | 190 | let generated = quote! { 191 | 192 | impl ::soa_derive::SoAVec<#name> for #vec_name { 193 | type Ref<'t> = #ref_name<'t>; 194 | type Slice<'t> = #slice_name<'t>; 195 | type Iter<'t> = #iter_name<'t>; 196 | type Ptr = #ptr_name; 197 | 198 | type RefMut<'t> = #ref_mut_name<'t>; 199 | type SliceMut<'t> = #slice_mut_name<'t>; 200 | type IterMut<'t> = #iter_mut_name<'t>; 201 | type PtrMut = #ptr_mut_name; 202 | 203 | fn len(&self) -> usize { 204 | self.len() 205 | } 206 | 207 | fn is_empty(&self) -> bool { 208 | self.is_empty() 209 | } 210 | 211 | fn as_slice<'c, 'a: 'c>(&'c self) -> Self::Slice<'c> where Self: 'a { 212 | self.as_slice() 213 | } 214 | 215 | fn slice<'c, 'a: 'c>(&'c self, index: impl core::ops::RangeBounds) -> Self::Slice<'c> where Self: 'a { 216 | let start = match index.start_bound() { 217 | std::ops::Bound::Included(i) | std::ops::Bound::Excluded(i) => *i, 218 | std::ops::Bound::Unbounded => 0, 219 | }; 220 | let n = self.len(); 221 | let end = match index.end_bound() { 222 | std::ops::Bound::Included(i) => (*i + 1).min(n), 223 | std::ops::Bound::Excluded(i) => *i, 224 | std::ops::Bound::Unbounded => n, 225 | }; 226 | self.index(start..end) 227 | } 228 | 229 | fn get<'c>(&'c self, index: usize) -> Option> { 230 | self.get(index) 231 | } 232 | 233 | fn index<'c>(&'c self, index: usize) -> Self::Ref<'c> { 234 | self.index(index) 235 | } 236 | 237 | fn iter<'c>(&'c self) -> Self::Iter<'c> { 238 | self.iter() 239 | } 240 | 241 | fn as_mut_slice<'c, 'a: 'c>(&'c mut self) -> Self::SliceMut<'c> where Self: 'a { 242 | self.as_mut_slice() 243 | } 244 | 245 | fn slice_mut<'c>(&'c mut self, index: impl core::ops::RangeBounds) -> Self::SliceMut<'c> { 246 | let start = match index.start_bound() { 247 | std::ops::Bound::Included(i) | std::ops::Bound::Excluded(i) => *i, 248 | std::ops::Bound::Unbounded => 0, 249 | }; 250 | let n = self.len(); 251 | let end = match index.end_bound() { 252 | std::ops::Bound::Included(i) => (*i + 1).min(n), 253 | std::ops::Bound::Excluded(i) => *i, 254 | std::ops::Bound::Unbounded => n, 255 | }; 256 | self.index_mut(start..end) 257 | } 258 | 259 | fn get_mut<'c>(&'c mut self, index: usize) -> Option> { 260 | self.get_mut(index) 261 | } 262 | 263 | fn index_mut<'c>(&'c mut self, index: usize) -> Self::RefMut<'c> { 264 | self.index_mut(index) 265 | } 266 | 267 | fn iter_mut<'c>(&'c mut self) -> Self::IterMut<'c> { 268 | self.iter_mut() 269 | } 270 | 271 | fn apply_index(&mut self, indices: &[usize]) { 272 | use ::soa_derive::SoASliceMut; 273 | self.as_mut_slice().apply_index(indices); 274 | } 275 | 276 | fn new() -> Self { 277 | Self::new() 278 | } 279 | 280 | fn with_capacity(capacity: usize) -> Self { 281 | Self::with_capacity(capacity) 282 | } 283 | 284 | fn capacity(&self) -> usize { 285 | self.capacity() 286 | } 287 | 288 | fn reserve(&mut self, additional: usize) { 289 | self.reserve(additional); 290 | } 291 | 292 | fn reserve_exact(&mut self, additional: usize) { 293 | self.reserve_exact(additional); 294 | } 295 | 296 | fn shrink_to_fit(&mut self) { 297 | self.shrink_to_fit(); 298 | } 299 | 300 | fn truncate(&mut self, len: usize) { 301 | self.truncate(len); 302 | } 303 | 304 | fn push(&mut self, value: #name) { 305 | self.push(value); 306 | } 307 | 308 | fn swap_remove(&mut self, index: usize) -> #name { 309 | self.swap_remove(index) 310 | } 311 | 312 | fn insert(&mut self, index: usize, element: #name) { 313 | self.insert(index, element); 314 | } 315 | 316 | fn replace(&mut self, index: usize, element: #name) -> #name { 317 | self.replace(index, element) 318 | } 319 | 320 | fn remove(&mut self, index: usize) -> #name { 321 | self.remove(index) 322 | } 323 | 324 | fn pop(&mut self) -> Option<#name> { 325 | self.pop() 326 | } 327 | 328 | fn append(&mut self, other: &mut Self) { 329 | self.append(other); 330 | } 331 | 332 | fn clear(&mut self) { 333 | self.clear(); 334 | } 335 | 336 | fn split_off(&mut self, at: usize) -> Self { 337 | self.split_off(at) 338 | } 339 | 340 | fn as_ptr(&self) -> Self::Ptr { 341 | self.as_ptr() 342 | } 343 | 344 | fn as_mut_ptr(&mut self) -> Self::PtrMut { 345 | self.as_mut_ptr() 346 | } 347 | } 348 | }; 349 | 350 | return generated 351 | } 352 | -------------------------------------------------------------------------------- /soa-derive-internal/src/index.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | 3 | use quote::quote; 4 | 5 | use crate::input::Input; 6 | use crate::names; 7 | 8 | pub fn derive(input: &Input) -> TokenStream { 9 | let vec_name = names::vec_name(&input.name); 10 | let slice_name = names::slice_name(&input.name); 11 | let slice_mut_name = names::slice_mut_name(&input.name); 12 | let ref_name = names::ref_name(&input.name); 13 | let ref_mut_name = names::ref_mut_name(&input.name); 14 | 15 | let fields_names = &input.fields.iter() 16 | .map(|field| field.ident.clone().unwrap()) 17 | .collect::>(); 18 | let first_field_name = &fields_names[0]; 19 | 20 | 21 | let get_unchecked = input.map_fields_nested_or( 22 | |ident, _| quote! { ::soa_derive::SoAIndex::get_unchecked(self.clone(), slice.#ident) }, 23 | |ident, _| quote! { slice.#ident.get_unchecked(self.clone()) }, 24 | ).collect::>(); 25 | 26 | let get_unchecked_mut = input.map_fields_nested_or( 27 | |ident, _| quote! { ::soa_derive::SoAIndexMut::get_unchecked_mut(self.clone(), slice.#ident) }, 28 | |ident, _| quote! { slice.#ident.get_unchecked_mut(self.clone()) }, 29 | ).collect::>(); 30 | 31 | let index = input.map_fields_nested_or( 32 | |ident, _| quote! { ::soa_derive::SoAIndex::index(self.clone(), slice.#ident) }, 33 | |ident, _| quote! { & slice.#ident[self.clone()] }, 34 | ).collect::>(); 35 | 36 | let index_mut = input.map_fields_nested_or( 37 | |ident, _| quote! { ::soa_derive::SoAIndexMut::index_mut(self.clone(), slice.#ident) }, 38 | |ident, _| quote! { &mut slice.#ident[self.clone()] }, 39 | ).collect::>(); 40 | 41 | quote!{ 42 | // usize 43 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for usize { 44 | type RefOutput = #ref_name<'a>; 45 | 46 | #[inline] 47 | fn get(self, soa: &'a #vec_name) -> Option { 48 | if self < soa.len() { 49 | Some(unsafe { ::soa_derive::SoAIndex::get_unchecked(self, soa) }) 50 | } else { 51 | None 52 | } 53 | } 54 | 55 | #[inline] 56 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 57 | ::soa_derive::SoAIndex::get_unchecked(self, soa.as_slice()) 58 | } 59 | 60 | #[inline] 61 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 62 | ::soa_derive::SoAIndex::index(self, soa.as_slice()) 63 | } 64 | } 65 | 66 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for usize { 67 | type MutOutput = #ref_mut_name<'a>; 68 | 69 | #[inline] 70 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 71 | if self < soa.len() { 72 | Some(unsafe { ::soa_derive::SoAIndexMut::get_unchecked_mut(self, soa) }) 73 | } else { 74 | None 75 | } 76 | } 77 | 78 | #[inline] 79 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 80 | ::soa_derive::SoAIndexMut::get_unchecked_mut(self, soa.as_mut_slice()) 81 | } 82 | 83 | #[inline] 84 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 85 | ::soa_derive::SoAIndexMut::index_mut(self, soa.as_mut_slice()) 86 | } 87 | } 88 | 89 | 90 | 91 | // Range 92 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::Range { 93 | type RefOutput = #slice_name<'a>; 94 | 95 | #[inline] 96 | fn get(self, soa: &'a #vec_name) -> Option { 97 | if self.start <= self.end && self.end <= soa.len() { 98 | unsafe { Some(::soa_derive::SoAIndex::get_unchecked(self, soa)) } 99 | } else { 100 | None 101 | } 102 | } 103 | 104 | #[inline] 105 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 106 | ::soa_derive::SoAIndex::get_unchecked(self, soa.as_slice()) 107 | } 108 | 109 | #[inline] 110 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 111 | ::soa_derive::SoAIndex::index(self, soa.as_slice()) 112 | } 113 | } 114 | 115 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::Range { 116 | type MutOutput = #slice_mut_name<'a>; 117 | 118 | #[inline] 119 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 120 | if self.start <= self.end && self.end <= soa.len() { 121 | unsafe { Some(::soa_derive::SoAIndexMut::get_unchecked_mut(self, soa)) } 122 | } else { 123 | None 124 | } 125 | } 126 | 127 | #[inline] 128 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 129 | ::soa_derive::SoAIndexMut::get_unchecked_mut(self, soa.as_mut_slice()) 130 | } 131 | 132 | #[inline] 133 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 134 | ::soa_derive::SoAIndexMut::index_mut(self, soa.as_mut_slice()) 135 | } 136 | } 137 | 138 | // RangeTo 139 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::RangeTo { 140 | type RefOutput = #slice_name<'a>; 141 | 142 | #[inline] 143 | fn get(self, soa: &'a #vec_name) -> Option { 144 | ::soa_derive::SoAIndex::get(0..self.end, soa) 145 | } 146 | 147 | #[inline] 148 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 149 | ::soa_derive::SoAIndex::get_unchecked(0..self.end, soa) 150 | } 151 | 152 | #[inline] 153 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 154 | ::soa_derive::SoAIndex::index(0..self.end, soa) 155 | } 156 | } 157 | 158 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::RangeTo { 159 | type MutOutput = #slice_mut_name<'a>; 160 | 161 | #[inline] 162 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 163 | ::soa_derive::SoAIndexMut::get_mut(0..self.end, soa) 164 | } 165 | 166 | #[inline] 167 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 168 | ::soa_derive::SoAIndexMut::get_unchecked_mut(0..self.end, soa) 169 | } 170 | 171 | #[inline] 172 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 173 | ::soa_derive::SoAIndexMut::index_mut(0..self.end, soa) 174 | } 175 | } 176 | 177 | // RangeFrom 178 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::RangeFrom { 179 | type RefOutput = #slice_name<'a>; 180 | 181 | #[inline] 182 | fn get(self, soa: &'a #vec_name) -> Option { 183 | ::soa_derive::SoAIndex::get(self.start..soa.len(), soa) 184 | } 185 | 186 | #[inline] 187 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 188 | ::soa_derive::SoAIndex::get_unchecked(self.start..soa.len(), soa) 189 | } 190 | 191 | #[inline] 192 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 193 | ::soa_derive::SoAIndex::index(self.start..soa.len(), soa) 194 | } 195 | } 196 | 197 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::RangeFrom { 198 | type MutOutput = #slice_mut_name<'a>; 199 | 200 | #[inline] 201 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 202 | ::soa_derive::SoAIndexMut::get_mut(self.start..soa.len(), soa) 203 | } 204 | 205 | #[inline] 206 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 207 | ::soa_derive::SoAIndexMut::get_unchecked_mut(self.start..soa.len(), soa) 208 | } 209 | 210 | #[inline] 211 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 212 | ::soa_derive::SoAIndexMut::index_mut(self.start..soa.len(), soa) 213 | } 214 | } 215 | 216 | // RangeFull 217 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::RangeFull { 218 | type RefOutput = #slice_name<'a>; 219 | 220 | #[inline] 221 | fn get(self, soa: &'a #vec_name) -> Option { 222 | Some(soa.as_slice()) 223 | } 224 | 225 | #[inline] 226 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 227 | soa.as_slice() 228 | } 229 | 230 | #[inline] 231 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 232 | soa.as_slice() 233 | } 234 | } 235 | 236 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::RangeFull { 237 | type MutOutput = #slice_mut_name<'a>; 238 | 239 | #[inline] 240 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 241 | Some(soa.as_mut_slice()) 242 | } 243 | 244 | #[inline] 245 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 246 | soa.as_mut_slice() 247 | } 248 | 249 | #[inline] 250 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 251 | soa.as_mut_slice() 252 | } 253 | } 254 | 255 | // RangeInclusive 256 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::RangeInclusive { 257 | type RefOutput = #slice_name<'a>; 258 | 259 | #[inline] 260 | fn get(self, soa: &'a #vec_name) -> Option { 261 | if *self.end() == usize::MAX { 262 | None 263 | } else { 264 | ::soa_derive::SoAIndex::get(*self.start()..self.end() + 1, soa) 265 | } 266 | } 267 | 268 | #[inline] 269 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 270 | ::soa_derive::SoAIndex::get_unchecked(*self.start()..self.end() + 1, soa) 271 | } 272 | 273 | #[inline] 274 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 275 | ::soa_derive::SoAIndex::index(*self.start()..self.end() + 1, soa) 276 | } 277 | } 278 | 279 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::RangeInclusive { 280 | type MutOutput = #slice_mut_name<'a>; 281 | 282 | #[inline] 283 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 284 | if *self.end() == usize::MAX { 285 | None 286 | } else { 287 | ::soa_derive::SoAIndexMut::get_mut(*self.start()..self.end() + 1, soa) 288 | } 289 | } 290 | 291 | #[inline] 292 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 293 | ::soa_derive::SoAIndexMut::get_unchecked_mut(*self.start()..self.end() + 1, soa) 294 | } 295 | 296 | #[inline] 297 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 298 | ::soa_derive::SoAIndexMut::index_mut(*self.start()..self.end() + 1, soa) 299 | } 300 | } 301 | 302 | // RangeToInclusive 303 | impl<'a> ::soa_derive::SoAIndex<&'a #vec_name> for ::std::ops::RangeToInclusive { 304 | type RefOutput = #slice_name<'a>; 305 | 306 | #[inline] 307 | fn get(self, soa: &'a #vec_name) -> Option { 308 | ::soa_derive::SoAIndex::get(0..=self.end, soa) 309 | } 310 | 311 | #[inline] 312 | unsafe fn get_unchecked(self, soa: &'a #vec_name) -> Self::RefOutput { 313 | ::soa_derive::SoAIndex::get_unchecked(0..=self.end, soa) 314 | } 315 | 316 | #[inline] 317 | fn index(self, soa: &'a #vec_name) -> Self::RefOutput { 318 | ::soa_derive::SoAIndex::index(0..=self.end, soa) 319 | } 320 | } 321 | 322 | impl<'a> ::soa_derive::SoAIndexMut<&'a mut #vec_name> for ::std::ops::RangeToInclusive { 323 | type MutOutput = #slice_mut_name<'a>; 324 | 325 | #[inline] 326 | fn get_mut(self, soa: &'a mut #vec_name) -> Option { 327 | ::soa_derive::SoAIndexMut::get_mut(0..=self.end, soa) 328 | } 329 | 330 | #[inline] 331 | unsafe fn get_unchecked_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 332 | ::soa_derive::SoAIndexMut::get_unchecked_mut(0..=self.end, soa) 333 | } 334 | 335 | #[inline] 336 | fn index_mut(self, soa: &'a mut #vec_name) -> Self::MutOutput { 337 | ::soa_derive::SoAIndexMut::index_mut(0..=self.end, soa) 338 | } 339 | } 340 | 341 | // usize 342 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for usize { 343 | type RefOutput = #ref_name<'a>; 344 | 345 | #[inline] 346 | fn get(self, slice: #slice_name<'a>) -> Option { 347 | if self < slice.#first_field_name.len() { 348 | Some(unsafe { ::soa_derive::SoAIndex::get_unchecked(self, slice) }) 349 | } else { 350 | None 351 | } 352 | } 353 | 354 | #[inline] 355 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 356 | #ref_name { 357 | #( #fields_names: #get_unchecked, )* 358 | } 359 | } 360 | 361 | #[inline] 362 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 363 | #ref_name { 364 | #( #fields_names: #index, )* 365 | } 366 | } 367 | } 368 | 369 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for usize { 370 | type MutOutput = #ref_mut_name<'a>; 371 | 372 | #[inline] 373 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 374 | if self < slice.len() { 375 | Some(unsafe { ::soa_derive::SoAIndexMut::get_unchecked_mut(self, slice) }) 376 | } else { 377 | None 378 | } 379 | } 380 | 381 | #[inline] 382 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 383 | #ref_mut_name { 384 | #( #fields_names: #get_unchecked_mut, )* 385 | } 386 | } 387 | 388 | #[inline] 389 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 390 | #ref_mut_name { 391 | #( #fields_names: #index_mut, )* 392 | } 393 | } 394 | } 395 | 396 | 397 | 398 | // Range 399 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::Range { 400 | type RefOutput = #slice_name<'a>; 401 | 402 | #[inline] 403 | fn get(self, slice: #slice_name<'a>) -> Option { 404 | if self.start <= self.end && self.end <= slice.#first_field_name.len() { 405 | unsafe { Some(::soa_derive::SoAIndex::get_unchecked(self, slice)) } 406 | } else { 407 | None 408 | } 409 | } 410 | 411 | #[inline] 412 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 413 | #slice_name { 414 | #( #fields_names: #get_unchecked, )* 415 | } 416 | } 417 | 418 | #[inline] 419 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 420 | #slice_name { 421 | #( #fields_names: #index, )* 422 | } 423 | } 424 | } 425 | 426 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::Range { 427 | type MutOutput = #slice_mut_name<'a>; 428 | 429 | #[inline] 430 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 431 | if self.start <= self.end && self.end <= slice.#first_field_name.len() { 432 | unsafe { Some(::soa_derive::SoAIndexMut::get_unchecked_mut(self, slice)) } 433 | } else { 434 | None 435 | } 436 | } 437 | 438 | #[inline] 439 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 440 | #slice_mut_name { 441 | #( #fields_names: #get_unchecked_mut, )* 442 | } 443 | } 444 | 445 | #[inline] 446 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 447 | #slice_mut_name { 448 | #( #fields_names: #index_mut, )* 449 | } 450 | } 451 | } 452 | 453 | 454 | 455 | // RangeTo 456 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::RangeTo { 457 | type RefOutput = #slice_name<'a>; 458 | 459 | #[inline] 460 | fn get(self, slice: #slice_name<'a>) -> Option { 461 | ::soa_derive::SoAIndex::get(0..self.end, slice) 462 | } 463 | 464 | #[inline] 465 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 466 | ::soa_derive::SoAIndex::get_unchecked(0..self.end, slice) 467 | } 468 | 469 | #[inline] 470 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 471 | ::soa_derive::SoAIndex::index(0..self.end, slice) 472 | } 473 | } 474 | 475 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::RangeTo { 476 | type MutOutput = #slice_mut_name<'a>; 477 | 478 | #[inline] 479 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 480 | ::soa_derive::SoAIndexMut::get_mut(0..self.end, slice) 481 | } 482 | 483 | #[inline] 484 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 485 | ::soa_derive::SoAIndexMut::get_unchecked_mut(0..self.end, slice) 486 | } 487 | 488 | #[inline] 489 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 490 | ::soa_derive::SoAIndexMut::index_mut(0..self.end, slice) 491 | } 492 | } 493 | 494 | 495 | // RangeFrom 496 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::RangeFrom { 497 | type RefOutput = #slice_name<'a>; 498 | 499 | #[inline] 500 | fn get(self, slice: #slice_name<'a>) -> Option { 501 | ::soa_derive::SoAIndex::get(self.start..slice.len(), slice) 502 | } 503 | 504 | #[inline] 505 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 506 | ::soa_derive::SoAIndex::get_unchecked(self.start..slice.len(), slice) 507 | } 508 | 509 | #[inline] 510 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 511 | ::soa_derive::SoAIndex::index(self.start..slice.len(), slice) 512 | } 513 | } 514 | 515 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::RangeFrom { 516 | type MutOutput = #slice_mut_name<'a>; 517 | 518 | #[inline] 519 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 520 | ::soa_derive::SoAIndexMut::get_mut(self.start..slice.len(), slice) 521 | } 522 | 523 | #[inline] 524 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 525 | ::soa_derive::SoAIndexMut::get_unchecked_mut(self.start..slice.len(), slice) 526 | } 527 | 528 | #[inline] 529 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 530 | ::soa_derive::SoAIndexMut::index_mut(self.start..slice.len(), slice) 531 | } 532 | } 533 | 534 | 535 | // RangeFull 536 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::RangeFull { 537 | type RefOutput = #slice_name<'a>; 538 | 539 | #[inline] 540 | fn get(self, slice: #slice_name<'a>) -> Option { 541 | Some(slice) 542 | } 543 | 544 | #[inline] 545 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 546 | slice 547 | } 548 | 549 | #[inline] 550 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 551 | slice 552 | } 553 | } 554 | 555 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::RangeFull { 556 | type MutOutput = #slice_mut_name<'a>; 557 | 558 | #[inline] 559 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 560 | Some(slice) 561 | } 562 | 563 | #[inline] 564 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 565 | slice 566 | } 567 | 568 | #[inline] 569 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 570 | slice 571 | } 572 | } 573 | 574 | 575 | // RangeInclusive 576 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::RangeInclusive { 577 | type RefOutput = #slice_name<'a>; 578 | 579 | #[inline] 580 | fn get(self, slice: #slice_name<'a>) -> Option { 581 | if *self.end() == usize::MAX { 582 | None 583 | } else { 584 | ::soa_derive::SoAIndex::get(*self.start()..self.end() + 1, slice) 585 | } 586 | } 587 | 588 | #[inline] 589 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 590 | ::soa_derive::SoAIndex::get_unchecked(*self.start()..self.end() + 1, slice) 591 | } 592 | 593 | #[inline] 594 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 595 | ::soa_derive::SoAIndex::index(*self.start()..self.end() + 1, slice) 596 | } 597 | } 598 | 599 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::RangeInclusive { 600 | type MutOutput = #slice_mut_name<'a>; 601 | 602 | #[inline] 603 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 604 | if *self.end() == usize::MAX { 605 | None 606 | } else { 607 | ::soa_derive::SoAIndexMut::get_mut(*self.start()..self.end() + 1, slice) 608 | } 609 | } 610 | 611 | #[inline] 612 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 613 | ::soa_derive::SoAIndexMut::get_unchecked_mut(*self.start()..self.end() + 1, slice) 614 | } 615 | 616 | #[inline] 617 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 618 | ::soa_derive::SoAIndexMut::index_mut(*self.start()..self.end() + 1, slice) 619 | } 620 | } 621 | 622 | 623 | // RangeToInclusive 624 | impl<'a> ::soa_derive::SoAIndex<#slice_name<'a>> for ::std::ops::RangeToInclusive { 625 | type RefOutput = #slice_name<'a>; 626 | 627 | #[inline] 628 | fn get(self, slice: #slice_name<'a>) -> Option { 629 | ::soa_derive::SoAIndex::get(0..=self.end, slice) 630 | } 631 | 632 | #[inline] 633 | unsafe fn get_unchecked(self, slice: #slice_name<'a>) -> Self::RefOutput { 634 | ::soa_derive::SoAIndex::get_unchecked(0..=self.end, slice) 635 | } 636 | 637 | #[inline] 638 | fn index(self, slice: #slice_name<'a>) -> Self::RefOutput { 639 | ::soa_derive::SoAIndex::index(0..=self.end, slice) 640 | } 641 | } 642 | 643 | impl<'a> ::soa_derive::SoAIndexMut<#slice_mut_name<'a>> for ::std::ops::RangeToInclusive { 644 | type MutOutput = #slice_mut_name<'a>; 645 | 646 | #[inline] 647 | fn get_mut(self, slice: #slice_mut_name<'a>) -> Option { 648 | ::soa_derive::SoAIndexMut::get_mut(0..=self.end, slice) 649 | } 650 | 651 | #[inline] 652 | unsafe fn get_unchecked_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 653 | ::soa_derive::SoAIndexMut::get_unchecked_mut(0..=self.end, slice) 654 | } 655 | 656 | #[inline] 657 | fn index_mut(self, slice: #slice_mut_name<'a>) -> Self::MutOutput { 658 | ::soa_derive::SoAIndexMut::index_mut(0..=self.end, slice) 659 | } 660 | } 661 | } 662 | } 663 | -------------------------------------------------------------------------------- /soa-derive-internal/src/input.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::quote; 3 | 4 | use syn::punctuated::Punctuated; 5 | use syn::{Attribute, Data, DeriveInput, Field, Path, Token, Visibility}; 6 | use syn::{Meta, MetaList}; 7 | 8 | /// Representing the struct we are deriving 9 | pub struct Input { 10 | /// The input struct name 11 | pub name: syn::Ident, 12 | /// The list of fields in the struct 13 | pub fields: Vec, 14 | /// Is field marked with `#[nested_soa]` 15 | pub field_is_nested: Vec, 16 | /// The struct overall visibility 17 | pub visibility: Visibility, 18 | /// Additional attributes requested with `#[soa_attr(...)]` or 19 | /// `#[soa_derive()]` 20 | pub attrs: ExtraAttributes, 21 | } 22 | 23 | pub struct ExtraAttributes { 24 | // did the user explicitly asked us to derive clone? 25 | pub derive_clone: bool, 26 | 27 | pub vec: Vec, 28 | pub slice: Vec, 29 | pub slice_mut: Vec, 30 | pub ref_: Vec, 31 | pub ref_mut: Vec, 32 | pub ptr: Vec, 33 | pub ptr_mut: Vec, 34 | } 35 | 36 | impl ExtraAttributes { 37 | fn new() -> ExtraAttributes { 38 | ExtraAttributes { 39 | derive_clone: false, 40 | vec: Vec::new(), 41 | slice: Vec::new(), 42 | slice_mut: Vec::new(), 43 | ref_: Vec::new(), 44 | ref_mut: Vec::new(), 45 | ptr: Vec::new(), 46 | ptr_mut: Vec::new(), 47 | } 48 | } 49 | 50 | /// Add a single trait from `#[soa_derive]` 51 | fn add_derive(&mut self, ident: &proc_macro2::Ident) { 52 | let derive_only_vec = |ident| { 53 | static EXCEPTIONS: &[&str] = &["Clone", "Deserialize", "Serialize"]; 54 | for exception in EXCEPTIONS { 55 | if ident == exception { 56 | return true; 57 | } 58 | } 59 | return false; 60 | }; 61 | 62 | let derive = Meta::List(MetaList { 63 | path: Path::from(syn::Ident::new("derive", Span::call_site())), 64 | delimiter: syn::MacroDelimiter::Paren(syn::token::Paren(Span::call_site())), 65 | tokens: quote!{ #ident }, 66 | }); 67 | 68 | if !derive_only_vec(ident) { 69 | self.slice.push(derive.clone()); 70 | self.slice_mut.push(derive.clone()); 71 | self.ref_.push(derive.clone()); 72 | self.ref_mut.push(derive.clone()); 73 | self.ptr.push(derive.clone()); 74 | self.ptr_mut.push(derive.clone()); 75 | } 76 | 77 | // always add this derive to the Vec struct 78 | self.vec.push(derive); 79 | 80 | if ident == "Clone" { 81 | self.derive_clone = true; 82 | } 83 | } 84 | } 85 | 86 | fn contains_nested_soa(attrs: &[Attribute]) -> bool { 87 | for attr in attrs { 88 | if attr.path().is_ident("nested_soa") { 89 | return true; 90 | } 91 | } 92 | return false; 93 | } 94 | 95 | impl Input { 96 | pub fn new(input: DeriveInput) -> Input { 97 | let mut fields = Vec::new(); 98 | let mut field_is_nested = Vec::new(); 99 | match input.data { 100 | Data::Struct(s) => { 101 | for field in s.fields.iter().cloned() { 102 | fields.push(field.clone()); 103 | field_is_nested.push(contains_nested_soa(&field.attrs)); 104 | } 105 | } 106 | _ => panic!("#[derive(StructOfArray)] only supports struct"), 107 | }; 108 | 109 | assert!(!fields.is_empty(), "#[derive(StructOfArray)] only supports struct with fields"); 110 | 111 | let mut extra_attrs = ExtraAttributes::new(); 112 | 113 | for attr in input.attrs { 114 | if attr.path().is_ident("soa_derive") { 115 | attr.parse_nested_meta(|meta| { 116 | match meta.path.get_ident() { 117 | Some(ident) => { 118 | assert!(ident != "Copy", "can not derive Copy for SoA vectors"); 119 | if ident != "Default" { 120 | // ignore as Default is already derived for SoA vectors, slices and mut slices 121 | extra_attrs.add_derive(ident); 122 | } 123 | } 124 | None => { 125 | panic!( 126 | "expected #[soa_derive(Traits, To, Derive)], got #[{}]", 127 | quote!(attr) 128 | ); 129 | } 130 | } 131 | Ok(()) 132 | }).expect("failed to parse soa_derive"); 133 | } 134 | 135 | if attr.path().is_ident("soa_attr") { 136 | let nested = attr.parse_args_with(Punctuated::::parse_terminated) 137 | .expect("expected attribute like #[soa_attr(, )]"); 138 | assert!(nested.len() == 2, "expected attribute like #[soa_attr(, )]"); 139 | 140 | let soa_type = nested.first().expect("should have 2 elements"); 141 | let attr = nested.last().expect("should have 2 elements").clone(); 142 | 143 | match soa_type.path().get_ident() { 144 | Some(ident) => { 145 | if ident == "Vec" { 146 | extra_attrs.vec.push(attr); 147 | } else if ident == "Slice" { 148 | extra_attrs.slice.push(attr); 149 | } else if ident == "SliceMut" { 150 | extra_attrs.slice_mut.push(attr); 151 | } else if ident == "Ref" { 152 | extra_attrs.ref_.push(attr); 153 | } else if ident == "RefMut" { 154 | extra_attrs.ref_mut.push(attr); 155 | } else if ident == "Ptr" { 156 | extra_attrs.ptr.push(attr); 157 | } else if ident == "PtrMut" { 158 | extra_attrs.ptr_mut.push(attr); 159 | } else { 160 | panic!("expected one of the SoA type, got {}", quote!(#soa_type)); 161 | } 162 | } 163 | None => panic!("expected one of the SoA type, got {}", quote!(#soa_type)) 164 | } 165 | } 166 | } 167 | 168 | Input { 169 | name: input.ident, 170 | fields: fields, 171 | visibility: input.vis, 172 | attrs: extra_attrs, 173 | field_is_nested, 174 | } 175 | } 176 | 177 | /// Map over all fields in the struct, calling the first function if the 178 | /// field is a nested struct of array, the second function otherwise 179 | pub(crate) fn map_fields_nested_or<'a, A, B>(&'a self, nested: A, not_nested: B) -> impl TokenStreamIterator + 'a 180 | where A: Fn(&syn::Ident, &syn::Type) -> TokenStream + 'a, 181 | B: Fn(&syn::Ident, &syn::Type) -> TokenStream + 'a, 182 | { 183 | self.fields.iter().zip(self.field_is_nested.iter()).map(move |(field, &is_nested)| { 184 | if is_nested { 185 | nested(field.ident.as_ref().expect("missing ident"), &field.ty) 186 | } else { 187 | not_nested(field.ident.as_ref().expect("missing ident"), &field.ty) 188 | } 189 | }) 190 | } 191 | } 192 | 193 | pub(crate) trait TokenStreamIterator: Iterator { 194 | fn concat_by(self, f: impl Fn(proc_macro2::TokenStream, proc_macro2::TokenStream) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream; 195 | } 196 | 197 | impl> TokenStreamIterator for T { 198 | fn concat_by(mut self, f: impl Fn(proc_macro2::TokenStream, proc_macro2::TokenStream) -> proc_macro2::TokenStream) -> proc_macro2::TokenStream { 199 | match self.next() { 200 | Some(first) => { 201 | self.fold(first, |current, next| { 202 | f(current, next) 203 | }) 204 | }, 205 | None => quote!{}, 206 | } 207 | } 208 | } 209 | 210 | #[cfg(test)] 211 | mod tests { 212 | use super::*; 213 | 214 | #[test] 215 | fn concat_by() { 216 | let token_streams = vec![quote!{a}, quote!{b}, quote!{c}]; 217 | assert_eq!(token_streams.into_iter().concat_by(|current, next| { 218 | quote!{(#current, #next)} 219 | }).to_string(), "((a , b) , c)"); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /soa-derive-internal/src/iter.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::input::{Input, TokenStreamIterator}; 5 | use crate::names; 6 | 7 | pub fn derive(input: &Input) -> TokenStream { 8 | let name = &input.name; 9 | let visibility = &input.visibility; 10 | let vec_name = names::vec_name(&input.name); 11 | let slice_name = names::slice_name(name); 12 | let slice_mut_name = names::slice_mut_name(&input.name); 13 | let ref_name = names::ref_name(&input.name); 14 | let ref_mut_name = names::ref_mut_name(&input.name); 15 | let iter_name = names::iter_name(&input.name); 16 | let iter_mut_name = names::iter_mut_name(&input.name); 17 | 18 | let doc_url = format!("[`{0}`](struct.{0}.html)", name); 19 | let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name); 20 | let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name); 21 | 22 | let fields_names = &input.fields.iter() 23 | .map(|field| field.ident.clone().unwrap()) 24 | .collect::>(); 25 | 26 | let fields_types = &input.fields.iter() 27 | .map(|field| field.ty.clone()) 28 | .collect::>(); 29 | 30 | let iter_type = input.map_fields_nested_or( 31 | |_, field_type| quote! { <#field_type as soa_derive::SoAIter<'a>>::Iter }, 32 | |_, field_type| quote! { ::std::slice::Iter<'a, #field_type> }, 33 | ).concat_by( 34 | |seq, next| { quote! { ::std::iter::Zip<#seq, #next> } } 35 | ); 36 | 37 | let iter_mut_type = input.map_fields_nested_or( 38 | |_, field_type| quote! { <#field_type as soa_derive::SoAIter<'a>>::IterMut }, 39 | |_, field_type| quote! { ::std::slice::IterMut<'a, #field_type> }, 40 | ).concat_by( 41 | |seq, next| { quote! { ::std::iter::Zip<#seq, #next> } } 42 | ); 43 | 44 | let create_into_iter = input.map_fields_nested_or( 45 | |ident, _| quote! { self.#ident.into_iter() }, 46 | |ident, _| quote! { self.#ident.iter() }, 47 | ).concat_by( 48 | |seq, next| { quote! { #seq.zip(#next) } } 49 | ); 50 | 51 | let create_mut_into_iter = input.map_fields_nested_or( 52 | |ident, _| quote! { self.#ident.into_iter() }, 53 | |ident, _| quote! { self.#ident.iter_mut() }, 54 | ).concat_by( 55 | |seq, next| { quote! { #seq.zip(#next) } } 56 | ); 57 | 58 | let iter_pat = fields_names.iter().fold(None, |seq, ident| { 59 | if let Some(seq) = seq { 60 | Some(quote! { (#seq, #ident) }) 61 | } else { 62 | Some(quote!{ #ident }) 63 | } 64 | }).expect("should be Some"); 65 | 66 | let create_iter = fields_names.iter().fold(None, |seq, ident| { 67 | if let Some(seq) = seq { 68 | Some(quote! { #seq.zip(self.#ident.iter()) }) 69 | } else { 70 | Some(quote! { self.#ident.iter() }) 71 | } 72 | }).expect("should be Some"); 73 | 74 | let create_iter_mut = fields_names.iter().fold(None, |seq, ident| { 75 | if let Some(seq) = seq { 76 | Some(quote! { #seq.zip(self.#ident.iter_mut()) }) 77 | } else { 78 | Some(quote! { self.#ident.iter_mut() }) 79 | } 80 | }).expect("should be Some"); 81 | 82 | let generated = quote! { 83 | /// Iterator over 84 | #[doc = #doc_url] 85 | #[allow(missing_debug_implementations)] 86 | #visibility struct #iter_name<'a>(#iter_type); 87 | 88 | impl<'a> Iterator for #iter_name<'a> { 89 | type Item = #ref_name<'a>; 90 | 91 | #[inline] 92 | fn next(&mut self) -> Option<#ref_name<'a>> { 93 | self.0.next().and_then(|#iter_pat| 94 | Some(#ref_name{ 95 | #(#fields_names,)* 96 | }) 97 | ) 98 | } 99 | 100 | #[inline] 101 | fn size_hint(&self) -> (usize, Option) { 102 | self.0.size_hint() 103 | } 104 | } 105 | 106 | impl<'a> DoubleEndedIterator for #iter_name<'a> { 107 | #[inline] 108 | fn next_back(&mut self) -> Option<#ref_name<'a>> { 109 | self.0.next_back().and_then(|#iter_pat| 110 | Some(#ref_name{ 111 | #(#fields_names,)* 112 | }) 113 | ) 114 | } 115 | } 116 | 117 | impl<'a> ExactSizeIterator for #iter_name<'a> { 118 | fn len(&self) -> usize { 119 | self.0.len() 120 | } 121 | } 122 | 123 | impl #vec_name { 124 | /// Get an iterator over the 125 | #[doc = #ref_doc_url] 126 | /// in this vector 127 | pub fn iter(&self) -> #iter_name { 128 | self.as_slice().into_iter() 129 | } 130 | } 131 | 132 | impl<'a> #slice_name<'a> { 133 | /// Get an iterator over the 134 | #[doc = #ref_doc_url] 135 | /// in this slice. 136 | pub fn iter(&self) -> #iter_name { 137 | #iter_name(#create_iter) 138 | } 139 | 140 | /// Get an iterator over the 141 | #[doc = #ref_doc_url] 142 | /// in this slice. 143 | pub fn into_iter(self) -> #iter_name<'a> { 144 | #iter_name(#create_into_iter) 145 | } 146 | } 147 | 148 | /// Mutable iterator over 149 | #[doc = #doc_url] 150 | #[allow(missing_debug_implementations)] 151 | #visibility struct #iter_mut_name<'a>(#iter_mut_type); 152 | 153 | impl<'a> Iterator for #iter_mut_name<'a> { 154 | type Item = #ref_mut_name<'a>; 155 | 156 | #[inline] 157 | fn next(&mut self) -> Option<#ref_mut_name<'a>> { 158 | self.0.next().and_then(|#iter_pat| 159 | Some(#ref_mut_name{ 160 | #(#fields_names,)* 161 | }) 162 | ) 163 | } 164 | 165 | #[inline] 166 | fn size_hint(&self) -> (usize, Option) { 167 | self.0.size_hint() 168 | } 169 | } 170 | 171 | impl<'a> DoubleEndedIterator for #iter_mut_name<'a> { 172 | #[inline] 173 | fn next_back(&mut self) -> Option<#ref_mut_name<'a>> { 174 | self.0.next_back().and_then(|#iter_pat| 175 | Some(#ref_mut_name{ 176 | #(#fields_names,)* 177 | }) 178 | ) 179 | } 180 | } 181 | impl<'a> ExactSizeIterator for #iter_mut_name<'a> { 182 | fn len(&self) -> usize { 183 | self.0.len() 184 | } 185 | } 186 | 187 | impl #vec_name { 188 | /// Get a mutable iterator over the 189 | #[doc = #ref_mut_doc_url] 190 | /// in this vector 191 | pub fn iter_mut(&mut self) -> #iter_mut_name { 192 | self.as_mut_slice().into_iter() 193 | } 194 | } 195 | 196 | impl<'a> #slice_mut_name<'a> { 197 | /// Get an iterator over the 198 | #[doc = #ref_doc_url] 199 | /// in this vector 200 | pub fn iter(&mut self) -> #iter_name { 201 | self.as_ref().into_iter() 202 | } 203 | 204 | /// Get a mutable iterator over the 205 | #[doc = #ref_mut_doc_url] 206 | /// in this vector 207 | pub fn iter_mut(&mut self) -> #iter_mut_name { 208 | #iter_mut_name(#create_iter_mut) 209 | } 210 | 211 | /// Get a mutable iterator over the 212 | #[doc = #ref_mut_doc_url] 213 | /// in this vector 214 | pub fn into_iter(self) -> #iter_mut_name<'a> { 215 | #iter_mut_name(#create_mut_into_iter) 216 | } 217 | } 218 | 219 | impl<'a> soa_derive::SoAIter<'a> for #name { 220 | type Ref = #ref_name<'a>; 221 | type RefMut = #ref_mut_name<'a>; 222 | type Iter = #iter_name<'a>; 223 | type IterMut = #iter_mut_name<'a>; 224 | } 225 | 226 | impl<'a> IntoIterator for #slice_name<'a> { 227 | type Item = #ref_name<'a>; 228 | type IntoIter = #iter_name<'a>; 229 | 230 | fn into_iter(self) -> Self::IntoIter { 231 | #iter_name(#create_into_iter) 232 | } 233 | } 234 | 235 | 236 | impl std::iter::FromIterator<#name> for #vec_name { 237 | fn from_iter>(iter: T) -> Self { 238 | let mut result = #vec_name::new(); 239 | for element in iter { 240 | result.push(element); 241 | } 242 | result 243 | } 244 | } 245 | 246 | impl<'a, 'b> IntoIterator for &'a #slice_name<'b> { 247 | type Item = #ref_name<'a>; 248 | type IntoIter = #iter_name<'a>; 249 | 250 | fn into_iter(self) -> Self::IntoIter { 251 | #iter_name(#create_into_iter) 252 | } 253 | } 254 | 255 | impl<'a> IntoIterator for &'a #vec_name { 256 | type Item = #ref_name<'a>; 257 | type IntoIter = #iter_name<'a>; 258 | 259 | fn into_iter(self) -> Self::IntoIter { 260 | self.as_slice().into_iter() 261 | } 262 | } 263 | 264 | impl<'a> IntoIterator for #slice_mut_name<'a> { 265 | type Item = #ref_mut_name<'a>; 266 | type IntoIter = #iter_mut_name<'a>; 267 | 268 | fn into_iter(self) -> Self::IntoIter { 269 | #iter_mut_name(#create_mut_into_iter) 270 | } 271 | } 272 | 273 | impl<'a> IntoIterator for &'a mut #vec_name { 274 | type Item = #ref_mut_name<'a>; 275 | type IntoIter = #iter_mut_name<'a>; 276 | 277 | fn into_iter(self) -> Self::IntoIter { 278 | self.as_mut_slice().into_iter() 279 | } 280 | } 281 | 282 | impl Extend<#name> for #vec_name { 283 | fn extend>(&mut self, iter: I) { 284 | for item in iter { 285 | self.push(item) 286 | } 287 | } 288 | } 289 | 290 | impl<'a> Extend<#ref_name<'a>> for #vec_name 291 | // only expose if all fields are Clone 292 | // https://github.com/rust-lang/rust/issues/48214#issuecomment-1150463333 293 | where #( for<'b> #fields_types: Clone, )* 294 | { 295 | fn extend>>(&mut self, iter: I) { 296 | >::extend(self, iter.into_iter().map(|item| item.to_owned())) 297 | } 298 | } 299 | 300 | impl<'a> ::soa_derive::IntoSoAIter<'a, #name> for #slice_name<'a> {} 301 | }; 302 | 303 | return generated; 304 | } 305 | -------------------------------------------------------------------------------- /soa-derive-internal/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(clippy::all, clippy::pedantic)] 2 | 3 | #![allow(clippy::needless_return, clippy::redundant_field_names)] 4 | #![allow(clippy::use_self, clippy::too_many_lines, clippy::missing_panics_doc)] 5 | #![allow(clippy::uninlined_format_args)] 6 | 7 | extern crate proc_macro; 8 | 9 | use proc_macro2::TokenStream; 10 | use quote::TokenStreamExt; 11 | 12 | mod index; 13 | #[macro_use] 14 | mod input; 15 | mod iter; 16 | mod ptr; 17 | mod refs; 18 | mod slice; 19 | mod vec; 20 | mod generic; 21 | 22 | pub(crate) mod names; 23 | 24 | #[proc_macro_derive(StructOfArray, attributes(soa_derive, soa_attr, nested_soa))] 25 | pub fn soa_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 26 | let ast = syn::parse(input).expect("Failed to parse derive macro for StructOfArray"); 27 | let input = input::Input::new(ast); 28 | 29 | let mut generated = TokenStream::new(); 30 | generated.append_all(vec::derive(&input)); 31 | generated.append_all(refs::derive(&input)); 32 | generated.append_all(ptr::derive(&input)); 33 | generated.append_all(slice::derive(&input)); 34 | generated.append_all(slice::derive_mut(&input)); 35 | generated.append_all(index::derive(&input)); 36 | generated.append_all(iter::derive(&input)); 37 | generated.append_all(derive_trait(&input)); 38 | 39 | generated.append_all(generic::derive_slice(&input)); 40 | generated.append_all(generic::derive_slice_mut(&input)); 41 | generated.append_all(generic::derive_vec(&input)); 42 | generated.into() 43 | } 44 | 45 | use crate::input::Input; 46 | use quote::quote; 47 | 48 | fn derive_trait(input: &Input) -> TokenStream { 49 | let name = &input.name; 50 | let vec_name = names::vec_name(name); 51 | 52 | quote! { 53 | impl soa_derive::StructOfArray for #name { 54 | type Type = #vec_name; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /soa-derive-internal/src/names.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::Span; 2 | use quote::ToTokens; 3 | use syn::Ident; 4 | 5 | /// Get the ident for the `Vec` type associated with `name` 6 | pub fn vec_name(name: impl ToTokens) -> Ident { 7 | Ident::new(&format!("{}Vec", name.to_token_stream()), Span::call_site()) 8 | } 9 | 10 | /// Get the ident for the slice type associated with `name` 11 | pub fn slice_name(name: impl ToTokens) -> Ident { 12 | Ident::new(&format!("{}Slice", name.to_token_stream()), Span::call_site()) 13 | } 14 | 15 | /// Get the ident for the mutable slice type associated with `name` 16 | pub fn slice_mut_name(name: impl ToTokens) -> Ident { 17 | Ident::new(&format!("{}SliceMut", name.to_token_stream()), Span::call_site()) 18 | } 19 | 20 | /// Get the ident for the reference type associated with `name` 21 | pub fn ref_name(name: impl ToTokens) -> Ident { 22 | Ident::new(&format!("{}Ref", name.to_token_stream()), Span::call_site()) 23 | } 24 | 25 | /// Get the ident for the mutable reference type associated with `name` 26 | pub fn ref_mut_name(name: impl ToTokens) -> Ident { 27 | Ident::new(&format!("{}RefMut", name.to_token_stream()), Span::call_site()) 28 | } 29 | 30 | /// Get the ident for the iterator type associated with `name` 31 | pub fn iter_name(name: impl ToTokens) -> Ident { 32 | Ident::new(&format!("{}Iter", name.to_token_stream()), Span::call_site()) 33 | } 34 | 35 | /// Get the ident for the mutable iterator type associated with `name` 36 | pub fn iter_mut_name(name: impl ToTokens) -> Ident { 37 | Ident::new(&format!("{}IterMut", name.to_token_stream()), Span::call_site()) 38 | } 39 | 40 | /// Get the ident for the pointer type associated with `name` 41 | pub fn ptr_name(name: impl ToTokens) -> Ident { 42 | Ident::new(&format!("{}Ptr", name.to_token_stream()), Span::call_site()) 43 | } 44 | 45 | /// Get the ident for the mutable pointer type associated with `name` 46 | pub fn ptr_mut_name(name: impl ToTokens) -> Ident { 47 | Ident::new(&format!("{}PtrMut", name.to_token_stream()), Span::call_site()) 48 | } 49 | -------------------------------------------------------------------------------- /soa-derive-internal/src/ptr.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | 4 | use crate::input::Input; 5 | use crate::names; 6 | 7 | pub fn derive(input: &Input) -> TokenStream { 8 | let name = &input.name; 9 | let visibility = &input.visibility; 10 | let attrs = &input.attrs.ptr; 11 | let mut_attrs = &input.attrs.ptr_mut; 12 | let vec_name = names::vec_name(&input.name); 13 | let ptr_name = names::ptr_name(&input.name); 14 | let ptr_mut_name = names::ptr_mut_name(&input.name); 15 | let ref_name = names::ref_name(&input.name); 16 | let ref_mut_name = names::ref_mut_name(&input.name); 17 | 18 | let doc_url = format!("[`{0}`](struct.{0}.html)", name); 19 | let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name); 20 | let ptr_doc_url = format!("[`{0}`](struct.{0}.html)", ptr_name); 21 | let ptr_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ptr_mut_name); 22 | let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name); 23 | let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name); 24 | 25 | let fields_names = &input.fields.iter() 26 | .map(|field| field.ident.clone().unwrap()) 27 | .collect::>(); 28 | 29 | let ptr_fields_types = input.map_fields_nested_or( 30 | |_, field_type| { 31 | let field_ptr_type = names::ptr_name(field_type); 32 | quote! { #field_ptr_type } 33 | }, 34 | |_, field_type| quote! { *const #field_type }, 35 | ).collect::>(); 36 | 37 | let ptr_mut_fields_types = input.map_fields_nested_or( 38 | |_, field_type| { 39 | let field_ptr_type = names::ptr_mut_name(field_type); 40 | quote! { #field_ptr_type } 41 | }, 42 | |_, field_type| quote! { *mut #field_type }, 43 | ).collect::>(); 44 | 45 | let as_ptr = input.map_fields_nested_or( 46 | |ident, _| quote! { self.#ident.as_ptr() }, 47 | |ident, _| quote! { self.#ident as *const _ }, 48 | ).collect::>(); 49 | 50 | let as_mut_ptr = input.map_fields_nested_or( 51 | |ident, _| quote! { self.#ident.as_mut_ptr() }, 52 | |ident, _| quote! { self.#ident as *mut _ }, 53 | ).collect::>(); 54 | 55 | quote! { 56 | /// An analog of a pointer to 57 | #[doc = #doc_url] 58 | /// with struct of array layout. 59 | #(#[#attrs])* 60 | #[derive(Copy, Clone)] 61 | #visibility struct #ptr_name { 62 | #( 63 | /// pointer to the ` 64 | #[doc = stringify!(#fields_names)] 65 | ///` field of a single 66 | #[doc = #doc_url] 67 | /// inside a 68 | #[doc = #vec_doc_url] 69 | pub #fields_names: #ptr_fields_types, 70 | )* 71 | } 72 | 73 | /// An analog of a mutable pointer to 74 | #[doc = #doc_url] 75 | /// with struct of array layout. 76 | #(#[#mut_attrs])* 77 | #[derive(Copy, Clone)] 78 | #visibility struct #ptr_mut_name { 79 | #( 80 | /// pointer to the ` 81 | #[doc = stringify!(#fields_names)] 82 | ///` field of a single 83 | #[doc = #doc_url] 84 | /// inside a 85 | #[doc = #vec_doc_url] 86 | pub #fields_names: #ptr_mut_fields_types, 87 | )* 88 | } 89 | 90 | #[allow(dead_code)] 91 | impl #ptr_name { 92 | /// Convert a 93 | #[doc = #ptr_doc_url] 94 | /// to a 95 | #[doc = #ptr_mut_doc_url] 96 | /// ; *i.e.* do a `*const T as *mut T` transformation. 97 | #visibility fn as_mut_ptr(&self) -> #ptr_mut_name { 98 | #ptr_mut_name { 99 | #( #fields_names: #as_mut_ptr, )* 100 | } 101 | } 102 | 103 | /// Similar to [`*const T::is_null()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.is_null). 104 | pub fn is_null(self) -> bool { 105 | false #( || self.#fields_names.is_null())* 106 | } 107 | 108 | /// Similar to [`*const T::as_ref()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref), 109 | /// with the same safety caveats. 110 | pub unsafe fn as_ref<'a>(self) -> Option<#ref_name<'a>> { 111 | if self.is_null() { 112 | None 113 | } else { 114 | Some(#ref_name { 115 | #(#fields_names: self.#fields_names.as_ref().expect("should not be null"), )* 116 | }) 117 | } 118 | } 119 | 120 | /// Similar to [`*const T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset), 121 | /// with the same safety caveats. 122 | pub unsafe fn offset(self, count: isize) -> #ptr_name { 123 | #ptr_name { 124 | #(#fields_names: self.#fields_names.offset(count), )* 125 | } 126 | } 127 | 128 | /// Similar to [`*const T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset). 129 | pub fn wrapping_offset(self, count: isize) -> #ptr_name { 130 | #ptr_name { 131 | #(#fields_names: self.#fields_names.wrapping_offset(count), )* 132 | } 133 | } 134 | 135 | /// Similar to [`*const T::add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.add), 136 | /// with the same safety caveats. 137 | pub unsafe fn add(self, count: usize) -> #ptr_name { 138 | #ptr_name { 139 | #(#fields_names: self.#fields_names.add(count), )* 140 | } 141 | } 142 | 143 | /// Similar to [`*const T::sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.sub), 144 | /// with the same safety caveats. 145 | pub unsafe fn sub(self, count: usize) -> #ptr_name { 146 | #ptr_name { 147 | #(#fields_names: self.#fields_names.sub(count), )* 148 | } 149 | } 150 | 151 | /// Similar to [`*const T::wrapping_add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add). 152 | pub fn wrapping_add(self, count: usize) -> #ptr_name { 153 | #ptr_name { 154 | #(#fields_names: self.#fields_names.wrapping_add(count), )* 155 | } 156 | } 157 | 158 | /// Similar to [`*const T::wrapping_sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_sub). 159 | pub fn wrapping_sub(self, count: usize) -> #ptr_name { 160 | #ptr_name { 161 | #(#fields_names: self.#fields_names.wrapping_sub(count), )* 162 | } 163 | } 164 | 165 | /// Similar to [`*const T::read()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read), 166 | /// with the same safety caveats. 167 | pub unsafe fn read(self) -> #name { 168 | #name { 169 | #(#fields_names: self.#fields_names.read(), )* 170 | } 171 | } 172 | 173 | /// Similar to [`*const T::read_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_volatile), 174 | /// with the same safety caveats. 175 | pub unsafe fn read_volatile(self) -> #name { 176 | #name { 177 | #(#fields_names: self.#fields_names.read_volatile(), )* 178 | } 179 | } 180 | 181 | /// Similar to [`*const T::read_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_unaligned), 182 | /// with the same safety caveats. 183 | pub unsafe fn read_unaligned(self) -> #name { 184 | #name { 185 | #(#fields_names: self.#fields_names.read_unaligned(), )* 186 | } 187 | } 188 | } 189 | 190 | impl ::soa_derive::SoAPointers for #name { 191 | type Ptr = #ptr_name; 192 | type MutPtr = #ptr_mut_name; 193 | } 194 | 195 | #[allow(dead_code)] 196 | #[allow(clippy::forget_non_drop)] 197 | impl #ptr_mut_name { 198 | /// Convert a 199 | #[doc = #ptr_mut_doc_url] 200 | /// to a 201 | #[doc = #ptr_doc_url] 202 | /// ; *i.e.* do a `*mut T as *const T` transformation 203 | #visibility fn as_ptr(&self) -> #ptr_name { 204 | #ptr_name { 205 | #( #fields_names: #as_ptr, )* 206 | } 207 | } 208 | 209 | /// Similar to [`*mut T::is_null()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.is_null). 210 | pub fn is_null(self) -> bool { 211 | false #( || self.#fields_names.is_null())* 212 | } 213 | 214 | /// Similar to [`*mut T::as_ref()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_ref), 215 | /// with the same safety caveats. 216 | pub unsafe fn as_ref<'a>(self) -> Option<#ref_name<'a>> { 217 | if self.is_null() { 218 | None 219 | } else { 220 | Some(#ref_name { 221 | #(#fields_names: self.#fields_names.as_ref().expect("should not be null"), )* 222 | }) 223 | } 224 | } 225 | 226 | /// Similar to [`*mut T::as_mut()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.as_mut), 227 | /// with the same safety caveats. 228 | pub unsafe fn as_mut<'a>(self) -> Option<#ref_mut_name<'a>> { 229 | if self.is_null() { 230 | None 231 | } else { 232 | Some(#ref_mut_name { 233 | #(#fields_names: self.#fields_names.as_mut().expect("should not be null"), )* 234 | }) 235 | } 236 | } 237 | 238 | /// Similar to [`*mut T::offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset), 239 | /// with the same safety caveats. 240 | pub unsafe fn offset(self, count: isize) -> #ptr_mut_name { 241 | #ptr_mut_name { 242 | #(#fields_names: self.#fields_names.offset(count), )* 243 | } 244 | } 245 | 246 | /// Similar to [`*mut T::wrapping_offset()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_offset) 247 | pub fn wrapping_offset(self, count: isize) -> #ptr_mut_name { 248 | #ptr_mut_name { 249 | #(#fields_names: self.#fields_names.wrapping_offset(count), )* 250 | } 251 | } 252 | 253 | /// Similar to [`*mut T::add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.add), 254 | /// with the same safety caveats. 255 | pub unsafe fn add(self, count: usize) -> #ptr_mut_name { 256 | #ptr_mut_name { 257 | #(#fields_names: self.#fields_names.add(count), )* 258 | } 259 | } 260 | 261 | /// Similar to [`*mut T::sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.sub), 262 | /// with the same safety caveats. 263 | pub unsafe fn sub(self, count: usize) -> #ptr_mut_name { 264 | #ptr_mut_name { 265 | #(#fields_names: self.#fields_names.sub(count), )* 266 | } 267 | } 268 | 269 | /// Similar to [`*mut T::wrapping_add()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_add), 270 | /// with the same safety caveats. 271 | pub fn wrapping_add(self, count: usize) -> #ptr_mut_name { 272 | #ptr_mut_name { 273 | #(#fields_names: self.#fields_names.wrapping_add(count), )* 274 | } 275 | } 276 | 277 | /// Similar to [`*mut T::wrapping_sub()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.wrapping_sub), 278 | /// with the same safety caveats. 279 | pub fn wrapping_sub(self, count: usize) -> #ptr_mut_name { 280 | #ptr_mut_name { 281 | #(#fields_names: self.#fields_names.wrapping_sub(count), )* 282 | } 283 | } 284 | 285 | /// Similar to [`*mut T::read()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read), 286 | /// with the same safety caveats. 287 | pub unsafe fn read(self) -> #name { 288 | #name { 289 | #(#fields_names: self.#fields_names.read(), )* 290 | } 291 | } 292 | 293 | /// Similar to [`*mut T::read_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_volatile), 294 | /// with the same safety caveats. 295 | pub unsafe fn read_volatile(self) -> #name { 296 | #name { 297 | #(#fields_names: self.#fields_names.read_volatile(), )* 298 | } 299 | } 300 | 301 | /// Similar to [`*mut T::read_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.read_unaligned), 302 | /// with the same safety caveats. 303 | pub unsafe fn read_unaligned(self) -> #name { 304 | #name { 305 | #(#fields_names: self.#fields_names.read_unaligned(), )* 306 | } 307 | } 308 | 309 | /// Similar to [`*mut T::write()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write), 310 | /// with the same safety caveats. 311 | #[allow(clippy::forget_non_drop)] 312 | pub unsafe fn write(self, val: #name) { 313 | unsafe { 314 | #(self.#fields_names.write(::std::ptr::read(&val.#fields_names));)* 315 | } 316 | // if val implements Drop, we don't want to run it here, only 317 | // when the vec itself will be dropped 318 | ::std::mem::forget(val); 319 | } 320 | 321 | /// Similar to [`*mut T::write_volatile()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write_volatile), 322 | /// with the same safety caveats. 323 | #[allow(clippy::forget_non_drop)] 324 | pub unsafe fn write_volatile(self, val: #name) { 325 | unsafe { 326 | #(self.#fields_names.write_volatile(::std::ptr::read(&val.#fields_names));)* 327 | } 328 | // if val implements Drop, we don't want to run it here, only 329 | // when the vec itself will be dropped 330 | ::std::mem::forget(val); 331 | } 332 | 333 | /// Similar to [`*mut T::write_unaligned()`](https://doc.rust-lang.org/std/primitive.pointer.html#method.write_unaligned), 334 | /// with the same safety caveats. 335 | #[allow(clippy::forget_non_drop)] 336 | pub unsafe fn write_unaligned(self, val: #name) { 337 | unsafe { 338 | #(self.#fields_names.write_unaligned(::std::ptr::read(&val.#fields_names));)* 339 | } 340 | // if val implements Drop, we don't want to run it here, only 341 | // when the vec itself will be dropped 342 | ::std::mem::forget(val); 343 | } 344 | } 345 | 346 | #[allow(dead_code)] 347 | impl<'a> #ref_name<'a> { 348 | /// Convert a 349 | #[doc = #ref_doc_url] 350 | /// to a 351 | #[doc = #ptr_doc_url] 352 | /// ; *i.e.* do a `&T as *const T` transformation 353 | #visibility fn as_ptr(&self) -> #ptr_name { 354 | #ptr_name { 355 | #( #fields_names: #as_ptr, )* 356 | } 357 | } 358 | } 359 | 360 | #[allow(dead_code)] 361 | impl<'a> #ref_mut_name<'a> { 362 | /// Convert a 363 | #[doc = #ref_mut_doc_url] 364 | /// to a 365 | #[doc = #ptr_doc_url] 366 | /// ; *i.e.* do a `&mut T as *const T` transformation 367 | #visibility fn as_ptr(&self) -> #ptr_name { 368 | #ptr_name { 369 | #( #fields_names: #as_ptr, )* 370 | } 371 | } 372 | 373 | /// Convert a 374 | #[doc = #ref_mut_doc_url] 375 | /// to a 376 | #[doc = #ptr_mut_doc_url] 377 | /// ; *i.e.* do a `&mut T as *mut T` transformation 378 | #visibility fn as_mut_ptr(&mut self) -> #ptr_mut_name { 379 | #ptr_mut_name { 380 | #( #fields_names: #as_mut_ptr, )* 381 | } 382 | } 383 | } 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /soa-derive-internal/src/refs.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::quote; 3 | 4 | use crate::input::Input; 5 | use crate::names; 6 | 7 | pub fn derive(input: &Input) -> TokenStream { 8 | let name = &input.name; 9 | let visibility = &input.visibility; 10 | let attrs = &input.attrs.ref_; 11 | let mut_attrs = &input.attrs.ref_mut; 12 | let vec_name = names::vec_name(&input.name); 13 | let ref_name = names::ref_name(&input.name); 14 | let ref_mut_name = names::ref_mut_name(&input.name); 15 | 16 | let fields_types = &input.fields.iter() 17 | .map(|field| field.ty.clone()) 18 | .collect::>(); 19 | 20 | let doc_url = format!("[`{0}`](struct.{0}.html)", name); 21 | let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name); 22 | let ref_doc_url = format!("[`{0}`](struct.{0}.html)", ref_name); 23 | let ref_mut_doc_url = format!("[`{0}`](struct.{0}.html)", ref_mut_name); 24 | 25 | let fields_names = &input.fields.iter() 26 | .map(|field| field.ident.clone().unwrap()) 27 | .collect::>(); 28 | 29 | let fields_names_hygienic = input.fields.iter() 30 | .enumerate() 31 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_{}", i), Span::call_site())) 32 | .collect::>(); 33 | 34 | let ref_fields_types = input.map_fields_nested_or( 35 | |_, field_type| { 36 | let field_ptr_type = names::ref_name(field_type); 37 | quote! { #field_ptr_type<'a> } 38 | }, 39 | |_, field_type| quote! { &'a #field_type }, 40 | ).collect::>(); 41 | 42 | let ref_mut_fields_types = input.map_fields_nested_or( 43 | |_, field_type| { 44 | let field_ptr_type = names::ref_mut_name(field_type); 45 | quote! { #field_ptr_type<'a> } 46 | }, 47 | |_, field_type| quote! { &'a mut #field_type }, 48 | ).collect::>(); 49 | 50 | let as_ref = input.map_fields_nested_or( 51 | |ident, _| quote! { self.#ident.as_ref() }, 52 | |ident, _| quote! { &self.#ident }, 53 | ).collect::>(); 54 | 55 | let as_mut = input.map_fields_nested_or( 56 | |ident, _| quote! { self.#ident.as_mut() }, 57 | |ident, _| quote! { &mut self.#ident }, 58 | ).collect::>(); 59 | 60 | let to_owned = input.map_fields_nested_or( 61 | |ident, _| quote! { self.#ident.to_owned() }, 62 | |ident, _| quote! { self.#ident.clone() }, 63 | ).collect::>(); 64 | 65 | let ref_replace = input.map_fields_nested_or( 66 | |ident, _| quote! { self.#ident.replace(field) }, 67 | |ident, _| quote! { ::std::mem::replace(&mut *self.#ident, field) }, 68 | ).collect::>(); 69 | 70 | quote! { 71 | /// A reference to a 72 | #[doc = #doc_url] 73 | /// with struct of array layout. 74 | #(#[#attrs])* 75 | #[derive(Copy, Clone)] 76 | #visibility struct #ref_name<'a> { 77 | #( 78 | /// reference to the ` 79 | #[doc = stringify!(#fields_names)] 80 | ///` field of a single 81 | #[doc = #doc_url] 82 | /// inside a 83 | #[doc = #vec_doc_url] 84 | pub #fields_names: #ref_fields_types, 85 | )* 86 | } 87 | 88 | /// A mutable reference to a 89 | #[doc = #doc_url] 90 | /// with struct of array layout. 91 | #(#[#mut_attrs])* 92 | #visibility struct #ref_mut_name<'a> { 93 | #( 94 | /// reference to the ` 95 | #[doc = stringify!(#fields_names)] 96 | ///` field of a single 97 | #[doc = #doc_url] 98 | /// inside a 99 | #[doc = #vec_doc_url] 100 | pub #fields_names: #ref_mut_fields_types, 101 | )* 102 | } 103 | 104 | #[allow(dead_code)] 105 | impl #name { 106 | /// Create a 107 | #[doc = #ref_doc_url] 108 | /// from a borrowed 109 | #[doc = #doc_url] 110 | /// . 111 | #visibility fn as_ref(&self) -> #ref_name { 112 | #ref_name { 113 | #( #fields_names: #as_ref, )* 114 | } 115 | } 116 | 117 | /// Create a 118 | #[doc = #ref_mut_doc_url] 119 | /// from a mutably borrowed 120 | #[doc = #doc_url] 121 | /// . 122 | #visibility fn as_mut(&mut self) -> #ref_mut_name { 123 | #ref_mut_name { 124 | #( #fields_names: #as_mut, )* 125 | } 126 | } 127 | } 128 | 129 | impl<'a> #ref_name<'a> { 130 | /// Convert a reference to 131 | #[doc = #doc_url] 132 | /// into an owned value. This is only available if all fields 133 | /// implement `Clone`. 134 | pub fn to_owned(&self) -> #name 135 | // only expose to_owned if all fields are Clone 136 | // https://github.com/rust-lang/rust/issues/48214#issuecomment-1150463333 137 | where #( for<'b> #fields_types: Clone, )* 138 | { 139 | #name { 140 | #( #fields_names: #to_owned, )* 141 | } 142 | } 143 | } 144 | 145 | impl<'a> From<#ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* { 146 | fn from(value: #ref_name<'a>) -> #name { 147 | value.to_owned() 148 | } 149 | } 150 | 151 | impl<'a> From<&'a #ref_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* { 152 | fn from(value: &'a #ref_name<'a>) -> #name { 153 | value.to_owned() 154 | } 155 | } 156 | 157 | impl<'a> #ref_mut_name<'a> { 158 | /// Convert a mutable reference to 159 | #[doc = #doc_url] 160 | /// into an owned value. This is only available if all fields 161 | /// implement `Clone`. 162 | pub fn to_owned(&self) -> #name 163 | // only expose to_owned if all fields are Clone 164 | // https://github.com/rust-lang/rust/issues/48214#issuecomment-1150463333 165 | where #( for<'b> #fields_types: Clone, )* 166 | { 167 | #name { 168 | #( #fields_names: #to_owned, )* 169 | } 170 | } 171 | 172 | /// Similar to [`std::mem::replace()`](https://doc.rust-lang.org/std/mem/fn.replace.html). 173 | #[allow(clippy::forget_non_drop)] 174 | pub fn replace(&mut self, val: #name) -> #name { 175 | #( 176 | let field = unsafe { ::std::ptr::read(&val.#fields_names) }; 177 | let #fields_names_hygienic = #ref_replace; 178 | )* 179 | // if val implements Drop, we don't want to run it here, only 180 | // when the vec itself will be dropped 181 | ::std::mem::forget(val); 182 | 183 | #name{#(#fields_names: #fields_names_hygienic),*} 184 | } 185 | } 186 | 187 | impl<'a> From<#ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* { 188 | fn from(value: #ref_mut_name<'a>) -> #name { 189 | value.to_owned() 190 | } 191 | } 192 | 193 | impl<'a> From<&'a #ref_mut_name<'a>> for #name where #( for<'b> #fields_types: Clone, )* { 194 | fn from(value: &'a #ref_mut_name<'a>) -> #name { 195 | value.to_owned() 196 | } 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /soa-derive-internal/src/slice.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use syn::Ident; 3 | use quote::TokenStreamExt; 4 | use quote::quote; 5 | 6 | use crate::input::Input; 7 | use crate::names; 8 | 9 | pub fn derive(input: &Input) -> TokenStream { 10 | let name = &input.name; 11 | let visibility = &input.visibility; 12 | let slice_name = names::slice_name(&input.name); 13 | let attrs = &input.attrs.slice; 14 | let vec_name = names::vec_name(&input.name); 15 | let ref_name = names::ref_name(&input.name); 16 | let ptr_name = names::ptr_name(&input.name); 17 | 18 | let slice_name_str = format!("[{}]", input.name); 19 | let doc_url = format!("[`{0}`](struct.{0}.html)", input.name); 20 | let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name); 21 | 22 | let fields_names = &input.fields.iter() 23 | .map(|field| field.ident.as_ref().unwrap()) 24 | .collect::>(); 25 | 26 | let first_field = &fields_names[0]; 27 | 28 | let fields_names_hygienic_1 = input.fields.iter() 29 | .enumerate() 30 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_1_{}", i), Span::call_site())) 31 | .collect::>(); 32 | let fields_names_hygienic_2 = input.fields.iter() 33 | .enumerate() 34 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_2_{}", i), Span::call_site())) 35 | .collect::>(); 36 | 37 | let slice_fields_types = input.map_fields_nested_or( 38 | |_, field_type| { 39 | let slice_type = names::slice_name(field_type); 40 | quote! { #slice_type<'a> } 41 | }, 42 | |_, field_type| quote! { &'a [#field_type] }, 43 | ).collect::>(); 44 | 45 | let slice_reborrow = input.map_fields_nested_or( 46 | |ident, _| quote! { self.#ident.reborrow() }, 47 | |ident, _| quote! { &self.#ident }, 48 | ).collect::>(); 49 | 50 | let slice_from_raw_parts = input.map_fields_nested_or( 51 | |ident, field_type| { 52 | let slice_type = names::slice_name(field_type); 53 | quote! { #slice_type::from_raw_parts(data.#ident, len) } 54 | }, 55 | |ident, _| quote! { ::std::slice::from_raw_parts(data.#ident, len) }, 56 | ).collect::>(); 57 | 58 | let mut generated = quote! { 59 | /// A slice of 60 | #[doc = #doc_url] 61 | /// inside a 62 | #[doc = #vec_doc_url] 63 | /// . 64 | #[allow(dead_code)] 65 | #[derive(Copy, Clone)] 66 | #(#[#attrs])* 67 | #[derive(Default)] 68 | #visibility struct #slice_name<'a> { 69 | #( 70 | /// slice of ` 71 | #[doc = stringify!(#fields_names)] 72 | ///` inside a 73 | #[doc = #vec_doc_url] 74 | pub #fields_names: #slice_fields_types, 75 | )* 76 | } 77 | 78 | #[allow(dead_code)] 79 | impl<'a> #slice_name<'a> { 80 | /// Similar to [`& 81 | #[doc = #slice_name_str] 82 | /// ::len()`](https://doc.rust-lang.org/std/primitive.slice.html#method.len), 83 | /// the length of all fields should be the same. 84 | pub fn len(&self) -> usize { 85 | let len = self.#first_field.len(); 86 | #(debug_assert_eq!(self.#fields_names.len(), len);)* 87 | len 88 | } 89 | 90 | /// Similar to [`& 91 | #[doc = #slice_name_str] 92 | /// ::is_empty()`](https://doc.rust-lang.org/std/primitive.slice.html#method.is_empty), 93 | /// the length of all fields should be the same. 94 | pub fn is_empty(&self) -> bool { 95 | let empty = self.#first_field.is_empty(); 96 | #(debug_assert_eq!(self.#fields_names.is_empty(), empty);)* 97 | empty 98 | } 99 | 100 | /// Similar to [`& 101 | #[doc = #slice_name_str] 102 | /// ::first()`](https://doc.rust-lang.org/std/primitive.slice.html#method.first). 103 | pub fn first(&self) -> Option<#ref_name<'a>> { 104 | if self.is_empty() { 105 | None 106 | } else { 107 | #( 108 | let #fields_names_hygienic_1 = self.#fields_names.first().unwrap(); 109 | )* 110 | Some(#ref_name{#(#fields_names: #fields_names_hygienic_1),*}) 111 | } 112 | } 113 | 114 | /// Similar to [`& 115 | #[doc = #slice_name_str] 116 | /// ::split_first()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_first). 117 | pub fn split_first(&self) -> Option<(#ref_name<'a>, #slice_name<'a>)> { 118 | if self.is_empty() { 119 | None 120 | } else { 121 | #( 122 | let (#fields_names_hygienic_1, #fields_names_hygienic_2) = self.#fields_names.split_first().unwrap(); 123 | )* 124 | let ref_ = #ref_name{#(#fields_names: #fields_names_hygienic_1),*}; 125 | let slice = #slice_name{#(#fields_names: #fields_names_hygienic_2),*}; 126 | Some((ref_, slice)) 127 | } 128 | } 129 | 130 | /// Similar to [`& 131 | #[doc = #slice_name_str] 132 | /// ::last()`](https://doc.rust-lang.org/std/primitive.slice.html#method.last). 133 | pub fn last(&self) -> Option<#ref_name<'a>> { 134 | if self.is_empty() { 135 | None 136 | } else { 137 | #( 138 | let #fields_names_hygienic_1 = self.#fields_names.last().unwrap(); 139 | )* 140 | Some(#ref_name{#(#fields_names: #fields_names_hygienic_1),*}) 141 | } 142 | } 143 | 144 | /// Similar to [`& 145 | #[doc = #slice_name_str] 146 | /// ::split_last()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_last). 147 | pub fn split_last(&self) -> Option<(#ref_name<'a>, #slice_name<'a>)> { 148 | if self.is_empty() { 149 | None 150 | } else { 151 | #( 152 | let (#fields_names_hygienic_1, #fields_names_hygienic_2) = self.#fields_names.split_last().unwrap(); 153 | )* 154 | let ref_ = #ref_name{#(#fields_names: #fields_names_hygienic_1),*}; 155 | let slice = #slice_name{#(#fields_names: #fields_names_hygienic_2),*}; 156 | Some((ref_, slice)) 157 | } 158 | } 159 | 160 | /// Similar to [`& 161 | #[doc = #slice_name_str] 162 | /// ::split_at()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_at). 163 | pub fn split_at(&self, mid: usize) -> (#slice_name<'a>, #slice_name<'a>) { 164 | #( 165 | let (#fields_names_hygienic_1, #fields_names_hygienic_2) = self.#fields_names.split_at(mid); 166 | )* 167 | let left = #slice_name{#(#fields_names: #fields_names_hygienic_1),*}; 168 | let right = #slice_name{#(#fields_names: #fields_names_hygienic_2),*}; 169 | (left, right) 170 | } 171 | 172 | /// Similar to [`& 173 | #[doc = #slice_name_str] 174 | /// ::get()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get). 175 | pub fn get<'b, I>(&'b self, index: I) -> Option 176 | where 177 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 178 | 'a: 'b 179 | { 180 | let slice: #slice_name<'b> = self.reborrow(); 181 | index.get(slice) 182 | } 183 | 184 | /// Similar to [`& 185 | #[doc = #slice_name_str] 186 | /// ::get_unchecked()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked). 187 | pub unsafe fn get_unchecked<'b, I>(&'b self, index: I) -> I::RefOutput 188 | where 189 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 190 | 'a: 'b 191 | { 192 | let slice: #slice_name<'b> = self.reborrow(); 193 | index.get_unchecked(slice) 194 | } 195 | 196 | /// Similar to the 197 | /// [`std::ops::Index`](https://doc.rust-lang.org/std/ops/trait.Index.html) 198 | /// trait for `& 199 | #[doc = #slice_name_str] 200 | ///` . 201 | /// This is required because we cannot implement `std::ops::Index` directly since it requires returning a reference. 202 | pub fn index<'b, I>(&'b self, index: I) -> I::RefOutput 203 | where 204 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 205 | 'a: 'b 206 | { 207 | let slice: #slice_name<'b> = self.reborrow(); 208 | index.index(slice) 209 | } 210 | 211 | /// Reborrows the slices in a narrower lifetime 212 | pub fn reborrow<'b>(&'b self) -> #slice_name<'b> 213 | where 214 | 'a: 'b 215 | { 216 | #slice_name { 217 | #( #fields_names: #slice_reborrow, )* 218 | } 219 | } 220 | 221 | /// Similar to [`& 222 | #[doc = #slice_name_str] 223 | /// ::as_ptr()`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_ptr). 224 | pub fn as_ptr(&self) -> #ptr_name { 225 | #ptr_name { 226 | #(#fields_names: self.#fields_names.as_ptr(),)* 227 | } 228 | } 229 | 230 | /// Similar to [`std::slice::from_raw_parts()`](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html). 231 | pub unsafe fn from_raw_parts<'b>(data: #ptr_name, len: usize) -> #slice_name<'b> { 232 | #slice_name { 233 | #( #fields_names: #slice_from_raw_parts, )* 234 | } 235 | } 236 | } 237 | 238 | }; 239 | 240 | if input.attrs.derive_clone { 241 | generated.append_all(quote!{ 242 | #[allow(dead_code)] 243 | impl<'a> #slice_name<'a> { 244 | /// Similar to [`& 245 | #[doc = #slice_name_str] 246 | /// ::to_vec()`](https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec). 247 | pub fn to_vec(&self) -> #vec_name { 248 | #vec_name { 249 | #(#fields_names: self.#fields_names.to_vec(),)* 250 | } 251 | } 252 | } 253 | }); 254 | 255 | { 256 | generated.append_all(quote! { 257 | impl<'a> ::soa_derive::ToSoAVec<#name> for #slice_name<'a> { 258 | type SoAVecType = #vec_name; 259 | 260 | fn to_vec(&self) -> Self::SoAVecType { 261 | self.to_vec() 262 | } 263 | } 264 | }); 265 | } 266 | } 267 | 268 | return generated; 269 | } 270 | 271 | pub fn derive_mut(input: &Input) -> TokenStream { 272 | let name = &input.name; 273 | let visibility = &input.visibility; 274 | let slice_name = names::slice_name(&input.name); 275 | let slice_mut_name = names::slice_mut_name(&input.name); 276 | let vec_name = names::vec_name(&input.name); 277 | let attrs = &input.attrs.slice_mut; 278 | let ref_name = names::ref_name(&input.name); 279 | let ref_mut_name = names::ref_mut_name(&input.name); 280 | let ptr_name = names::ptr_name(&input.name); 281 | let ptr_mut_name = names::ptr_mut_name(&input.name); 282 | 283 | let slice_name_str = format!("[{}]", input.name); 284 | let doc_url = format!("[`{0}`](struct.{0}.html)", input.name); 285 | let slice_doc_url = format!("[`{0}`](struct.{0}.html)", slice_name); 286 | let slice_mut_doc_url = format!("[`{0}`](struct.{0}.html)", slice_mut_name); 287 | let vec_doc_url = format!("[`{0}`](struct.{0}.html)", vec_name); 288 | 289 | let fields_names = &input.fields.iter() 290 | .map(|field| field.ident.clone().unwrap()) 291 | .collect::>(); 292 | 293 | let first_field = &fields_names[0]; 294 | let fields_names_hygienic_1 = &input.fields.iter() 295 | .enumerate() 296 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_slice_1_{}", i), Span::call_site())) 297 | .collect::>(); 298 | let fields_names_hygienic_2 = &input.fields.iter() 299 | .enumerate() 300 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_slice_2_{}", i), Span::call_site())) 301 | .collect::>(); 302 | 303 | let slice_mut_fields_types = input.map_fields_nested_or( 304 | |_, field_type| { 305 | let slice_type = names::slice_mut_name(field_type); 306 | quote! { #slice_type<'a> } 307 | }, 308 | |_, field_type| quote! { &'a mut [#field_type] }, 309 | ).collect::>(); 310 | 311 | let slice_as_ref = input.map_fields_nested_or( 312 | |ident, _| quote! { self.#ident.as_ref() }, 313 | |ident, _| quote! { self.#ident }, 314 | ).collect::>(); 315 | 316 | let slice_as_slice = input.map_fields_nested_or( 317 | |ident, _| quote! { self.#ident.as_slice() }, 318 | |ident, _| quote! { &self.#ident }, 319 | ).collect::>(); 320 | 321 | let slice_reborrow = input.map_fields_nested_or( 322 | |ident, _| quote! { self.#ident.reborrow() }, 323 | |ident, _| quote! { &mut self.#ident }, 324 | ).collect::>(); 325 | 326 | let slice_from_raw_parts_mut = input.map_fields_nested_or( 327 | |ident, field_type| { 328 | let slice_type = names::slice_mut_name(field_type); 329 | quote! { #slice_type::from_raw_parts_mut(data.#ident, len) } 330 | }, 331 | |ident, _| quote! {::std::slice::from_raw_parts_mut(data.#ident, len) }, 332 | ).collect::>(); 333 | 334 | let mut nested_ord = input.map_fields_nested_or( 335 | |_, field_type| { 336 | let field_ref_type = names::ref_name(field_type); 337 | quote! { for<'b> #field_ref_type<'b>: Ord } 338 | }, 339 | |_, _| quote! {}, 340 | ).filter(|stream| !stream.is_empty()).collect::>(); 341 | nested_ord.push(quote! { for<'b> #ref_name<'b>: Ord }); 342 | 343 | let apply_permutation = input.map_fields_nested_or( 344 | |ident, _| quote! { self.#ident.__private_apply_permutation(permutation) }, 345 | |ident, _| quote! { permutation.apply_slice_in_place(&mut self.#ident) }, 346 | ).collect::>(); 347 | 348 | let mut generated = quote! { 349 | /// A mutable slice of 350 | #[doc = #doc_url] 351 | /// inside a 352 | #[doc = #vec_doc_url] 353 | /// . 354 | #[allow(dead_code)] 355 | #(#[#attrs])* 356 | #[derive(Default)] 357 | #visibility struct #slice_mut_name<'a> { 358 | #( 359 | /// slice of ` 360 | #[doc = stringify!(#fields_names)] 361 | ///` inside a 362 | #[doc = #vec_doc_url] 363 | pub #fields_names: #slice_mut_fields_types, 364 | )* 365 | } 366 | 367 | #[allow(dead_code)] 368 | impl<'a> #slice_mut_name<'a> { 369 | /// Convert a 370 | #[doc = #slice_mut_doc_url] 371 | /// to a 372 | #[doc = #slice_doc_url] 373 | /// in order to be able to use the methods on the non mutable 374 | /// version of the slices. 375 | pub fn as_ref(&self) -> #slice_name { 376 | #slice_name { 377 | #( #fields_names: #slice_as_ref, )* 378 | } 379 | } 380 | 381 | /// Similar to [`& 382 | #[doc = #slice_name_str] 383 | /// ::len()`](https://doc.rust-lang.org/std/primitive.slice.html#method.len), 384 | /// the length of all fields should be the same. 385 | pub fn len(&self) -> usize { 386 | let len = self.#first_field.len(); 387 | #(debug_assert_eq!(self.#fields_names.len(), len);)* 388 | len 389 | } 390 | 391 | /// Similar to [`& 392 | #[doc = #slice_name_str] 393 | /// ::is_empty()`](https://doc.rust-lang.org/std/primitive.slice.html#method.is_empty), 394 | /// the length of all fields should be the same. 395 | pub fn is_empty(&self) -> bool { 396 | let empty = self.#first_field.is_empty(); 397 | #(debug_assert_eq!(self.#fields_names.is_empty(), empty);)* 398 | empty 399 | } 400 | 401 | /// Similar to [`&mut 402 | #[doc = #slice_name_str] 403 | /// ::first_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.first_mut). 404 | pub fn first_mut(&mut self) -> Option<#ref_mut_name> { 405 | if self.is_empty() { 406 | None 407 | } else { 408 | #( 409 | let #fields_names = self.#fields_names.first_mut().unwrap(); 410 | )* 411 | Some(#ref_mut_name{#(#fields_names: #fields_names),*}) 412 | } 413 | } 414 | 415 | /// Similar to [`&mut 416 | #[doc = #slice_name_str] 417 | ///::split_first_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_first_mut). 418 | /// 419 | /// The main difference is that this function consumes the slice. 420 | /// You should use [`Self::reborrow()`] first if you want the 421 | /// returned values to have a shorter lifetime. 422 | pub fn split_first_mut(mut self) -> Option<(#ref_mut_name<'a>, #slice_mut_name<'a>)> { 423 | if self.is_empty() { 424 | None 425 | } else { 426 | #( 427 | let (#fields_names, #fields_names_hygienic_1) = self.#fields_names.split_first_mut().unwrap(); 428 | )* 429 | let ref_ = #ref_mut_name{#(#fields_names: #fields_names),*}; 430 | let slice = #slice_mut_name{#(#fields_names: #fields_names_hygienic_1),*}; 431 | Some((ref_, slice)) 432 | } 433 | } 434 | 435 | /// Similar to [`&mut 436 | #[doc = #slice_name_str] 437 | /// ::last_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.last_mut). 438 | pub fn last_mut(&mut self) -> Option<#ref_mut_name> { 439 | if self.is_empty() { 440 | None 441 | } else { 442 | #( 443 | let #fields_names = self.#fields_names.last_mut().unwrap(); 444 | )* 445 | Some(#ref_mut_name{#(#fields_names: #fields_names),*}) 446 | } 447 | } 448 | 449 | /// Similar to [`&mut 450 | #[doc = #slice_name_str] 451 | /// ::last_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.last_mut). 452 | /// 453 | /// The main difference is that this function consumes the slice. 454 | /// You should use [`Self::reborrow()`] first if you want the 455 | /// returned values to have a shorter lifetime. 456 | pub fn split_last_mut(mut self) -> Option<(#ref_mut_name<'a>, #slice_mut_name<'a>)> { 457 | if self.is_empty() { 458 | None 459 | } else { 460 | #( 461 | let (#fields_names, #fields_names_hygienic_1) = self.#fields_names.split_last_mut().unwrap(); 462 | )* 463 | let ref_ = #ref_mut_name{#(#fields_names: #fields_names),*}; 464 | let slice = #slice_mut_name{#(#fields_names: #fields_names_hygienic_1),*}; 465 | Some((ref_, slice)) 466 | } 467 | } 468 | 469 | /// Similar to [`&mut 470 | #[doc = #slice_name_str] 471 | /// ::split_at_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.split_at_mut). 472 | /// 473 | /// The main difference is that this function consumes the slice. 474 | /// You should use [`Self::reborrow()`] first if you want the 475 | /// returned values to have a shorter lifetime. 476 | pub fn split_at_mut(mut self, mid: usize) -> (#slice_mut_name<'a>, #slice_mut_name<'a>) { 477 | #( 478 | let (#fields_names_hygienic_1, #fields_names_hygienic_2) = self.#fields_names.split_at_mut(mid); 479 | )* 480 | let left = #slice_mut_name{#(#fields_names: #fields_names_hygienic_1),*}; 481 | let right = #slice_mut_name{#(#fields_names: #fields_names_hygienic_2),*}; 482 | (left, right) 483 | } 484 | 485 | /// Similar to [`&mut 486 | #[doc = #slice_name_str] 487 | /// ::swap()`](https://doc.rust-lang.org/std/primitive.slice.html#method.swap). 488 | pub fn swap(&mut self, a: usize, b: usize) { 489 | #( 490 | self.#fields_names.swap(a, b); 491 | )* 492 | } 493 | 494 | /// Similar to [`& 495 | #[doc = #slice_name_str] 496 | /// ::get()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get). 497 | pub fn get<'b, I>(&'b self, index: I) -> Option 498 | where 499 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 500 | 'a: 'b 501 | { 502 | let slice: #slice_name<'b> = self.as_slice(); 503 | index.get(slice) 504 | } 505 | 506 | /// Similar to [`& 507 | #[doc = #slice_name_str] 508 | /// ::get_unchecked()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked). 509 | pub unsafe fn get_unchecked<'b, I>(&'b self, index: I) -> I::RefOutput 510 | where 511 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 512 | 'a: 'b 513 | { 514 | let slice: #slice_name<'b> = self.as_slice(); 515 | index.get_unchecked(slice) 516 | } 517 | 518 | 519 | /// Similar to the 520 | /// [`std::ops::Index`](https://doc.rust-lang.org/std/ops/trait.Index.html) 521 | /// trait for `& 522 | #[doc = #slice_name_str] 523 | ///` . 524 | /// This is required because we cannot implement that trait. 525 | pub fn index<'b, I>(&'b self, index: I) -> I::RefOutput 526 | where 527 | I: ::soa_derive::SoAIndex<#slice_name<'b>>, 528 | 'a: 'b 529 | { 530 | let slice: #slice_name<'b> = self.as_slice(); 531 | index.index(slice) 532 | } 533 | 534 | /// Similar to [`&mut 535 | #[doc = #slice_name_str] 536 | /// ::get_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_mut). 537 | pub fn get_mut<'b, I>(&'b mut self, index: I) -> Option 538 | where 539 | I: ::soa_derive::SoAIndexMut<#slice_mut_name<'b>>, 540 | 'a: 'b 541 | { 542 | let slice: #slice_mut_name<'b> = self.reborrow(); 543 | index.get_mut(slice) 544 | } 545 | 546 | /// Similar to [`&mut 547 | #[doc = #slice_name_str] 548 | /// ::get_unchecked_mut()`](https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked_mut). 549 | pub unsafe fn get_unchecked_mut<'b, I>(&'b mut self, index: I) -> I::MutOutput 550 | where 551 | I: ::soa_derive::SoAIndexMut<#slice_mut_name<'b>>, 552 | 'a: 'b 553 | { 554 | let slice: #slice_mut_name<'b> = self.reborrow(); 555 | index.get_unchecked_mut(slice) 556 | } 557 | 558 | /// Similar to the 559 | /// [`std::ops::IndexMut`](https://doc.rust-lang.org/std/ops/trait.IndexMut.html) 560 | /// trait for `&mut 561 | #[doc = #slice_name_str] 562 | ///` . 563 | /// This is required because we cannot implement `std::ops::IndexMut` directly since it requires returning a mutable reference. 564 | pub fn index_mut<'b, I>(&'b mut self, index: I) -> I::MutOutput 565 | where 566 | I: ::soa_derive::SoAIndexMut<#slice_mut_name<'b>>, 567 | 'a: 'b 568 | { 569 | let slice: #slice_mut_name<'b> = self.reborrow(); 570 | index.index_mut(slice) 571 | } 572 | 573 | /// Returns a non-mutable slice from this mutable slice. 574 | pub fn as_slice<'b>(&'b self) -> #slice_name<'b> 575 | where 576 | 'a: 'b 577 | { 578 | #slice_name { 579 | #( #fields_names: #slice_as_slice, )* 580 | } 581 | } 582 | 583 | /// Reborrows the slices in a narrower lifetime 584 | pub fn reborrow<'b>(&'b mut self) -> #slice_mut_name<'b> 585 | where 586 | 'a: 'b 587 | { 588 | #slice_mut_name { 589 | #( #fields_names: #slice_reborrow, )* 590 | } 591 | } 592 | 593 | /// Similar to [`& 594 | #[doc = #slice_name_str] 595 | /// ::as_ptr()`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_ptr). 596 | pub fn as_ptr(&self) -> #ptr_name { 597 | #ptr_name { 598 | #(#fields_names: self.#fields_names.as_ptr(),)* 599 | } 600 | } 601 | 602 | /// Similar to [`&mut 603 | #[doc = #slice_name_str] 604 | /// ::as_mut_ptr()`](https://doc.rust-lang.org/std/primitive.slice.html#method.as_mut_ptr). 605 | pub fn as_mut_ptr(&mut self) -> #ptr_mut_name { 606 | #ptr_mut_name { 607 | #(#fields_names: self.#fields_names.as_mut_ptr(),)* 608 | } 609 | } 610 | 611 | /// Similar to [`std::slice::from_raw_parts_mut()`](https://doc.rust-lang.org/std/slice/fn.from_raw_parts_mut.html). 612 | pub unsafe fn from_raw_parts_mut<'b>(data: #ptr_mut_name, len: usize) -> #slice_mut_name<'b> { 613 | #slice_mut_name { 614 | #( #fields_names: #slice_from_raw_parts_mut, )* 615 | } 616 | } 617 | 618 | #[doc(hidden)] 619 | /// This is `pub` due to there will be compile-error if `#[nested_soa]` is used. 620 | /// Do not use this method directly. 621 | pub fn __private_apply_permutation(&mut self, permutation: &mut soa_derive::Permutation) { 622 | #( #apply_permutation; )* 623 | } 624 | 625 | /// Similar to [`&mut 626 | #[doc = #slice_name_str] 627 | /// ::sort_by()`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by). 628 | pub fn sort_by(&mut self, mut f: F) 629 | where 630 | F: FnMut(#ref_name, #ref_name) -> std::cmp::Ordering, 631 | { 632 | use soa_derive::Permutation; 633 | 634 | let mut permutation: Vec = (0..self.len()).collect(); 635 | permutation.sort_by(|j, k| f(self.index(*j), self.index(*k))); 636 | 637 | let mut permutation = Permutation::oneline(permutation).inverse(); 638 | self.__private_apply_permutation(&mut permutation); 639 | } 640 | 641 | /// Similar to [`&mut 642 | #[doc = #slice_name_str] 643 | /// ::sort_by_key()`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by_key). 644 | pub fn sort_by_key(&mut self, mut f: F) 645 | where 646 | F: FnMut(#ref_name) -> K, 647 | K: Ord, 648 | { 649 | use soa_derive::Permutation; 650 | 651 | let mut permutation: Vec = (0..self.len()).collect(); 652 | permutation.sort_by_key(|i| f(self.index(*i))); 653 | 654 | let mut permutation = Permutation::oneline(permutation).inverse(); 655 | self.__private_apply_permutation(&mut permutation); 656 | } 657 | } 658 | 659 | #[allow(dead_code)] 660 | impl<'a> #slice_mut_name<'a> 661 | where 662 | #( #nested_ord, )* 663 | { 664 | /// Similar to [`&mut 665 | #[doc = #slice_name_str] 666 | /// ::sort()`](https://doc.rust-lang.org/std/primitive.slice.html#method.sort). 667 | pub fn sort(&mut self) { 668 | use soa_derive::Permutation; 669 | 670 | let mut permutation: Vec = (0..self.len()).collect(); 671 | permutation.sort_by_key(|i| self.index(*i)); 672 | 673 | let mut permutation = Permutation::oneline(permutation).inverse(); 674 | self.__private_apply_permutation(&mut permutation); 675 | } 676 | } 677 | }; 678 | 679 | if input.attrs.derive_clone { 680 | generated.append_all(quote!{ 681 | #[allow(dead_code)] 682 | impl<'a> #slice_mut_name<'a> { 683 | /// Similar to [`& 684 | #[doc = #slice_name_str] 685 | /// ::to_vec()`](https://doc.rust-lang.org/std/primitive.slice.html#method.to_vec). 686 | pub fn to_vec(&self) -> #vec_name { 687 | #vec_name { 688 | #(#fields_names: self.#fields_names.to_vec(),)* 689 | } 690 | } 691 | } 692 | }); 693 | 694 | { 695 | generated.append_all(quote! { 696 | impl<'a> ::soa_derive::ToSoAVec<#name> for #slice_mut_name<'a> { 697 | type SoAVecType = #vec_name; 698 | 699 | fn to_vec(&self) -> Self::SoAVecType { 700 | self.to_vec() 701 | } 702 | } 703 | }); 704 | } 705 | } 706 | 707 | return generated; 708 | } 709 | -------------------------------------------------------------------------------- /soa-derive-internal/src/vec.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Span, TokenStream}; 2 | use quote::TokenStreamExt; 3 | use quote::quote; 4 | 5 | use crate::input::Input; 6 | use crate::names; 7 | 8 | pub fn derive(input: &Input) -> TokenStream { 9 | let name = &input.name; 10 | let vec_name_str = format!("Vec<{}>", name); 11 | let attrs = &input.attrs.vec; 12 | let visibility = &input.visibility; 13 | let vec_name = names::vec_name(&input.name); 14 | let slice_name = names::slice_name(name); 15 | let slice_mut_name = names::slice_mut_name(&input.name); 16 | let ref_name = names::ref_name(&input.name); 17 | let ref_mut_name = names::ref_mut_name(&input.name); 18 | let ptr_name = names::ptr_name(&input.name); 19 | let ptr_mut_name = names::ptr_mut_name(&input.name); 20 | 21 | let doc_url = format!("[`{0}`](struct.{0}.html)", input.name); 22 | 23 | let fields_names = &input.fields.iter() 24 | .map(|field| field.ident.as_ref().unwrap()) 25 | .collect::>(); 26 | 27 | let fields_names_hygienic = input.fields.iter() 28 | .enumerate() 29 | .map(|(i, _)| Ident::new(&format!("___soa_derive_private_{}", i), Span::call_site())) 30 | .collect::>(); 31 | 32 | let first_field = &fields_names[0]; 33 | 34 | let vec_fields_types = input.map_fields_nested_or( 35 | |_, field_type| { 36 | let vec_type = names::vec_name(field_type); 37 | quote! { #vec_type } 38 | }, 39 | |_, field_type| quote! { Vec<#field_type> }, 40 | ).collect::>(); 41 | 42 | let vec_with_capacity = input.map_fields_nested_or( 43 | |_, field_type| quote! { <#field_type as StructOfArray>::Type::with_capacity(capacity) }, 44 | |_, _| quote! { Vec::with_capacity(capacity) }, 45 | ).collect::>(); 46 | 47 | let vec_slice = input.map_fields_nested_or( 48 | |ident, _| quote! { self.#ident.slice(range.clone()) }, 49 | |ident, _| quote! { &self.#ident[range.clone()] }, 50 | ).collect::>(); 51 | 52 | let vec_slice_mut = input.map_fields_nested_or( 53 | |ident, _| quote! { self.#ident.slice_mut(range.clone()) }, 54 | |ident, _| quote! { &mut self.#ident[range.clone()] }, 55 | ).collect::>(); 56 | 57 | let vec_from_raw_parts = input.map_fields_nested_or( 58 | |ident, field_type| { 59 | let vec_type = names::vec_name(field_type); 60 | quote! { #vec_type::from_raw_parts(data.#ident, len, capacity) } 61 | }, 62 | |ident, _| quote! { Vec::from_raw_parts(data.#ident, len, capacity) }, 63 | ).collect::>(); 64 | 65 | let vec_replace = input.map_fields_nested_or( 66 | |ident, _| quote! { self.#ident.replace(index, field) }, 67 | |ident, _| quote! { ::std::mem::replace(&mut self.#ident[index], field) }, 68 | ).collect::>(); 69 | 70 | let mut generated = quote! { 71 | /// An analog to ` 72 | #[doc = #vec_name_str] 73 | /// ` with Struct of Array (SoA) layout 74 | #[allow(dead_code)] 75 | #(#[#attrs])* 76 | #[derive(Default)] 77 | #visibility struct #vec_name { 78 | #( 79 | /// a vector of ` 80 | #[doc = stringify!(#fields_names)] 81 | ///` from a 82 | #[doc = #doc_url] 83 | pub #fields_names: #vec_fields_types, 84 | )* 85 | } 86 | 87 | #[allow(dead_code)] 88 | #[allow(clippy::forget_non_drop)] 89 | impl #vec_name { 90 | /// Similar to [` 91 | #[doc = #vec_name_str] 92 | /// ::new()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.new) 93 | pub fn new() -> #vec_name { 94 | Default::default() 95 | } 96 | 97 | /// Similar to [` 98 | #[doc = #vec_name_str] 99 | /// ::with_capacity()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.with_capacity), 100 | /// initializing all fields with the given `capacity`. 101 | pub fn with_capacity(capacity: usize) -> #vec_name { 102 | #vec_name { 103 | #( #fields_names: #vec_with_capacity, )* 104 | } 105 | } 106 | 107 | /// Similar to [` 108 | #[doc = #vec_name_str] 109 | /// ::capacity()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.capacity), 110 | /// the capacity of all fields should be the same. 111 | pub fn capacity(&self) -> usize { 112 | let capacity = self.#first_field.capacity(); 113 | #(debug_assert_eq!(self.#fields_names.capacity(), capacity);)* 114 | capacity 115 | } 116 | 117 | /// Similar to [` 118 | #[doc = #vec_name_str] 119 | /// ::reserve()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.reserve), 120 | /// reserving the same `additional` space for all fields. 121 | pub fn reserve(&mut self, additional: usize) { 122 | #(self.#fields_names.reserve(additional);)* 123 | } 124 | 125 | /// Similar to [` 126 | #[doc = #vec_name_str] 127 | /// ::reserve_exact()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.reserve_exact) 128 | /// reserving the same `additional` space for all fields. 129 | pub fn reserve_exact(&mut self, additional: usize) { 130 | #(self.#fields_names.reserve_exact(additional);)* 131 | } 132 | 133 | /// Similar to [` 134 | #[doc = #vec_name_str] 135 | /// ::shrink_to_fit()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.shrink_to_fit) 136 | /// shrinking all fields. 137 | pub fn shrink_to_fit(&mut self) { 138 | #(self.#fields_names.shrink_to_fit();)* 139 | } 140 | 141 | /// Similar to [` 142 | #[doc = #vec_name_str] 143 | /// ::truncate()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.truncate) 144 | /// truncating all fields. 145 | pub fn truncate(&mut self, len: usize) { 146 | #(self.#fields_names.truncate(len);)* 147 | } 148 | 149 | /// Similar to [` 150 | #[doc = #vec_name_str] 151 | /// ::push()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.push). 152 | #[allow(clippy::forget_non_drop)] 153 | pub fn push(&mut self, value: #name) { 154 | // We need to use ptr read/write instead of moving out of the 155 | // fields in case the value struct implements Drop. 156 | unsafe { 157 | #(self.#fields_names.push(::std::ptr::read(&value.#fields_names));)* 158 | } 159 | // if value implements Drop, we don't want to run it here, only 160 | // when the vec itself will be dropped. 161 | ::std::mem::forget(value); 162 | } 163 | 164 | /// Similar to [` 165 | #[doc = #vec_name_str] 166 | /// ::len()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.len), 167 | /// all the fields should have the same length. 168 | pub fn len(&self) -> usize { 169 | let len = self.#first_field.len(); 170 | #(debug_assert_eq!(self.#fields_names.len(), len);)* 171 | len 172 | } 173 | 174 | /// Similar to [` 175 | #[doc = #vec_name_str] 176 | /// ::is_empty()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.is_empty), 177 | /// all the fields should have the same length. 178 | pub fn is_empty(&self) -> bool { 179 | let empty = self.#first_field.is_empty(); 180 | #(debug_assert_eq!(self.#fields_names.is_empty(), empty);)* 181 | empty 182 | } 183 | 184 | /// Similar to [` 185 | #[doc = #vec_name_str] 186 | /// ::swap_remove()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.swap_remove). 187 | pub fn swap_remove(&mut self, index: usize) -> #name { 188 | #( 189 | let #fields_names_hygienic = self.#fields_names.swap_remove(index); 190 | )* 191 | #name{#(#fields_names: #fields_names_hygienic),*} 192 | } 193 | 194 | /// Similar to [` 195 | #[doc = #vec_name_str] 196 | /// ::insert()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.insert). 197 | #[allow(clippy::forget_non_drop)] 198 | pub fn insert(&mut self, index: usize, element: #name) { 199 | if index > self.len() { 200 | panic!("index out of bounds: the len is {} but the index is {}", self.len(), index); 201 | } 202 | 203 | // similar to push, we can not use move and have to rely on ptr 204 | // read/write 205 | unsafe { 206 | #(self.#fields_names.insert(index, ::std::ptr::read(&element.#fields_names));)* 207 | } 208 | // if value implements Drop, we don't want to run it here, only 209 | // when the vec itself will be dropped. 210 | ::std::mem::forget(element); 211 | } 212 | 213 | /// Similar to [`std::mem::replace()`](https://doc.rust-lang.org/std/mem/fn.replace.html). 214 | #[allow(clippy::forget_non_drop)] 215 | pub fn replace(&mut self, index: usize, element: #name) -> #name { 216 | if index > self.len() { 217 | panic!("index out of bounds: the len is {} but the index is {}", self.len(), index); 218 | } 219 | 220 | // similar to push, we can not use move and have to rely on ptr 221 | // read/write 222 | #( 223 | let field = unsafe { ::std::ptr::read(&element.#fields_names) }; 224 | let #fields_names_hygienic = #vec_replace; 225 | )* 226 | // if value implements Drop, we don't want to run it here, only 227 | // when the vec itself will be dropped. 228 | ::std::mem::forget(element); 229 | 230 | #name{#(#fields_names: #fields_names_hygienic),*} 231 | } 232 | 233 | /// Similar to [` 234 | #[doc = #vec_name_str] 235 | /// ::remove()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.remove). 236 | pub fn remove(&mut self, index: usize) -> #name { 237 | #( 238 | let #fields_names_hygienic = self.#fields_names.remove(index); 239 | )* 240 | #name{#(#fields_names: #fields_names_hygienic),*} 241 | } 242 | 243 | /// Similar to [` 244 | #[doc = #vec_name_str] 245 | /// ::pop()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.pop). 246 | pub fn pop(&mut self) -> Option<#name> { 247 | if self.is_empty() { 248 | None 249 | } else { 250 | #( 251 | let #fields_names_hygienic = self.#fields_names.pop().unwrap(); 252 | )* 253 | Some(#name{#(#fields_names: #fields_names_hygienic),*}) 254 | } 255 | } 256 | 257 | /// Similar to [` 258 | #[doc = #vec_name_str] 259 | /// ::append()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.append). 260 | pub fn append(&mut self, other: &mut #vec_name) { 261 | #( 262 | self.#fields_names.append(&mut other.#fields_names); 263 | )* 264 | } 265 | 266 | /// Similar to [` 267 | #[doc = #vec_name_str] 268 | /// ::clear()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.clear). 269 | pub fn clear(&mut self) { 270 | #(self.#fields_names.clear();)* 271 | } 272 | 273 | /// Similar to [` 274 | #[doc = #vec_name_str] 275 | /// ::split_off()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.split_off). 276 | pub fn split_off(&mut self, at: usize) -> #vec_name { 277 | #vec_name { 278 | #(#fields_names: self.#fields_names.split_off(at), )* 279 | } 280 | } 281 | 282 | /// Similar to [` 283 | #[doc = #vec_name_str] 284 | /// ::as_slice()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_slice). 285 | pub fn as_slice(&self) -> #slice_name { 286 | #slice_name { 287 | #(#fields_names: self.#fields_names.as_slice(), )* 288 | } 289 | } 290 | 291 | /// Similar to [` 292 | #[doc = #vec_name_str] 293 | /// ::as_mut_slice()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_mut_slice). 294 | pub fn as_mut_slice(&mut self) -> #slice_mut_name { 295 | #slice_mut_name { 296 | #(#fields_names: self.#fields_names.as_mut_slice(), )* 297 | } 298 | } 299 | 300 | /// Create a slice of this vector matching the given `range`. This 301 | /// is analogous to `Index>`. 302 | pub fn slice(&self, range: ::std::ops::Range) -> #slice_name { 303 | #slice_name { 304 | #( #fields_names: #vec_slice, )* 305 | } 306 | } 307 | 308 | /// Create a mutable slice of this vector matching the given 309 | /// `range`. This is analogous to `IndexMut>`. 310 | pub fn slice_mut(&mut self, range: ::std::ops::Range) -> #slice_mut_name { 311 | #slice_mut_name { 312 | #( #fields_names: #vec_slice_mut, )* 313 | } 314 | } 315 | 316 | /// Similar to [` 317 | #[doc = #vec_name_str] 318 | /// ::retain()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain). 319 | pub fn retain(&mut self, mut f: F) where F: FnMut(#ref_name) -> bool { 320 | let len = self.len(); 321 | let mut del = 0; 322 | 323 | { 324 | let mut slice = self.as_mut_slice(); 325 | for i in 0..len { 326 | if !f(slice.get(i).unwrap()) { 327 | del += 1; 328 | } else if del > 0 { 329 | slice.swap(i - del, i); 330 | } 331 | } 332 | } 333 | if del > 0 { 334 | self.truncate(len - del); 335 | } 336 | } 337 | 338 | /// Similar to [` 339 | #[doc = #vec_name_str] 340 | /// ::retain_mut()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.retain_mut). 341 | pub fn retain_mut(&mut self, mut f: F) where F: FnMut(#ref_mut_name) -> bool { 342 | let len = self.len(); 343 | let mut del = 0; 344 | 345 | { 346 | let mut slice = self.as_mut_slice(); 347 | for i in 0..len { 348 | if !f(slice.get_mut(i).unwrap()) { 349 | del += 1; 350 | } else if del > 0 { 351 | slice.swap(i - del, i); 352 | } 353 | } 354 | } 355 | if del > 0 { 356 | self.truncate(len - del); 357 | } 358 | } 359 | 360 | /// Similar to [` 361 | #[doc = #vec_name_str] 362 | /// ::get()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get). 363 | pub fn get<'a, I>(&'a self, index: I) -> Option 364 | where 365 | I: ::soa_derive::SoAIndex<&'a #vec_name> 366 | { 367 | index.get(self) 368 | } 369 | 370 | /// Similar to [` 371 | #[doc = #vec_name_str] 372 | /// ::get_unchecked()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked). 373 | pub unsafe fn get_unchecked<'a, I>(&'a self, index: I) -> I::RefOutput 374 | where 375 | I: ::soa_derive::SoAIndex<&'a #vec_name> 376 | { 377 | index.get_unchecked(self) 378 | } 379 | 380 | /// Similar to [` 381 | #[doc = #vec_name_str] 382 | /// ::index()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.index). 383 | pub fn index<'a, I>(&'a self, index: I) -> I::RefOutput 384 | where 385 | I: ::soa_derive::SoAIndex<&'a #vec_name> 386 | { 387 | index.index(self) 388 | } 389 | 390 | /// Similar to [` 391 | #[doc = #vec_name_str] 392 | /// ::get_mut()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_mut). 393 | pub fn get_mut<'a, I>(&'a mut self, index: I) -> Option 394 | where 395 | I: ::soa_derive::SoAIndexMut<&'a mut #vec_name> 396 | { 397 | index.get_mut(self) 398 | } 399 | 400 | /// Similar to [` 401 | #[doc = #vec_name_str] 402 | /// ::get_unchecked_mut()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.get_unchecked_mut). 403 | pub unsafe fn get_unchecked_mut<'a, I>(&'a mut self, index: I) -> I::MutOutput 404 | where 405 | I: ::soa_derive::SoAIndexMut<&'a mut #vec_name> 406 | { 407 | index.get_unchecked_mut(self) 408 | } 409 | 410 | /// Similar to [` 411 | #[doc = #vec_name_str] 412 | /// ::index_mut()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.index_mut). 413 | pub fn index_mut<'a, I>(&'a mut self, index: I) -> I::MutOutput 414 | where 415 | I: ::soa_derive::SoAIndexMut<&'a mut #vec_name> 416 | { 417 | index.index_mut(self) 418 | } 419 | 420 | /// Similar to [` 421 | #[doc = #vec_name_str] 422 | /// ::as_ptr()`](https://doc.rust-lang.org/std/struct.Vec.html#method.as_ptr). 423 | pub fn as_ptr(&self) -> #ptr_name { 424 | #ptr_name { 425 | #(#fields_names: self.#fields_names.as_ptr(),)* 426 | } 427 | } 428 | 429 | /// Similar to [` 430 | #[doc = #vec_name_str] 431 | /// ::as_mut_ptr()`](https://doc.rust-lang.org/std/struct.Vec.html#method.as_mut_ptr). 432 | pub fn as_mut_ptr(&mut self) -> #ptr_mut_name { 433 | #ptr_mut_name { 434 | #(#fields_names: self.#fields_names.as_mut_ptr(),)* 435 | } 436 | } 437 | 438 | /// Similar to [` 439 | #[doc = #vec_name_str] 440 | /// ::from_raw_parts()`](https://doc.rust-lang.org/std/struct.Vec.html#method.from_raw_parts). 441 | pub unsafe fn from_raw_parts(data: #ptr_mut_name, len: usize, capacity: usize) -> #vec_name { 442 | #vec_name { 443 | #( #fields_names: #vec_from_raw_parts, )* 444 | } 445 | } 446 | } 447 | 448 | #[allow(clippy::drop_non_drop)] 449 | impl Drop for #vec_name { 450 | fn drop(&mut self) { 451 | while let Some(value) = self.pop() { 452 | ::std::mem::drop(value); 453 | } 454 | } 455 | } 456 | }; 457 | 458 | if input.attrs.derive_clone { 459 | generated.append_all(quote!{ 460 | #[allow(dead_code)] 461 | impl #vec_name { 462 | /// Similar to [` 463 | #[doc = #vec_name_str] 464 | /// ::resize()`](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.resize). 465 | pub fn resize(&mut self, new_len: usize, value: #name) { 466 | #( 467 | self.#fields_names.resize(new_len, value.#fields_names); 468 | )* 469 | } 470 | } 471 | 472 | impl ::soa_derive::SoAAppendVec<#name> for #vec_name { 473 | fn extend_from_slice(&mut self, other: Self::Slice<'_>) { 474 | #( 475 | self.#fields_names.extend_from_slice(other.#fields_names); 476 | )* 477 | } 478 | } 479 | }); 480 | } 481 | 482 | return generated; 483 | } 484 | -------------------------------------------------------------------------------- /tests/extreme.rs: -------------------------------------------------------------------------------- 1 | #![deny(warnings)] 2 | #![allow(clippy::float_cmp)] 3 | 4 | use soa_derive::StructOfArray; 5 | 6 | // This test checks that the derive code works even in some extreme cases 7 | 8 | #[derive(StructOfArray)] 9 | struct Private { 10 | inner: f64, 11 | } 12 | 13 | #[test] 14 | fn private() { 15 | let p = Private {inner: 42.0}; 16 | assert_eq!(p.inner, 42.0); 17 | } 18 | 19 | pub struct Empty; 20 | 21 | #[derive(StructOfArray)] 22 | pub struct NoTraits { 23 | inner: Empty, 24 | } 25 | 26 | #[derive(StructOfArray)] 27 | pub struct VeryBig { 28 | x: f64, 29 | y: f64, 30 | z: f64, 31 | vx: f64, 32 | vy: f64, 33 | vz: f64, 34 | name: String, 35 | } 36 | 37 | // strange names used by variables inside the implementation. 38 | // This checks for hygiene in code generation 39 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 40 | pub struct BadNames { 41 | pub index: String, 42 | pub at: String, 43 | pub other: String, 44 | pub len: String, 45 | pub size: String, 46 | pub cap: String, 47 | pub capacity: String, 48 | pub buf: String, 49 | } 50 | 51 | // Raw identifiers 52 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 53 | pub struct RawIdent { 54 | pub r#for: String, 55 | pub r#in: String, 56 | } 57 | -------------------------------------------------------------------------------- /tests/generic.rs: -------------------------------------------------------------------------------- 1 | #![allow(unexpected_cfgs)] 2 | #![cfg(rustc_is_at_least_1_78)] 3 | 4 | mod particles; 5 | use std::marker::PhantomData; 6 | use std::fmt::Debug; 7 | 8 | use particles::ParticleVec; 9 | use soa_derive::{SoAVec, SoASlice, StructOfArray}; 10 | 11 | use self::particles::Particle; 12 | 13 | fn may_iter>(vec: &V) -> V::Iter<'_> { 14 | vec.iter() 15 | } 16 | 17 | fn may_push>(vec: &mut V, val: T) { 18 | vec.push(val) 19 | } 20 | 21 | fn may_sort_generic>(vec: &mut V) where for<'t> V::Ref<'t> : PartialOrd { 22 | let mut indices: Vec<_> = (0..vec.len()).collect(); 23 | 24 | indices.sort_by(|j, k| { 25 | let a = vec.index(*j); 26 | let b = vec.index(*k); 27 | a.partial_cmp(&b).unwrap() 28 | }); 29 | 30 | vec.apply_index(&indices); 31 | } 32 | 33 | 34 | fn may_closure_sort, F>(vec: &mut V, mut f: F) where F: FnMut(V::Ref<'_>, V::Ref<'_>) -> std::cmp::Ordering { 35 | let mut indices: Vec<_> = (0..vec.len()).collect(); 36 | 37 | indices.sort_by(|j, k| { 38 | let a = vec.index(*j); 39 | let b = vec.index(*k); 40 | f(a, b) 41 | }); 42 | 43 | vec.apply_index(&indices); 44 | } 45 | 46 | 47 | #[test] 48 | fn test_generic_type_behavior() { 49 | let mut x = ParticleVec::new(); 50 | x.push(Particle::new("foo".into(), 100.0)); 51 | let y: Vec<_> = may_iter::(&x).collect(); 52 | assert_eq!(x.len(), y.len()); 53 | assert_eq!(x.first().as_ref(), y.first()); 54 | drop(y); 55 | 56 | let z = Particle::new("bar".into(), 1000.0); 57 | may_push(&mut x, z); 58 | assert_eq!(x.len(), 2); 59 | 60 | may_sort_generic(&mut x); 61 | assert_eq!(x.first().unwrap().name, "bar"); 62 | 63 | x.sort_by(|a, b| a.mass.total_cmp(b.mass).reverse()); 64 | // may_sort(&mut x); 65 | let a = x.index(0); 66 | let b = x.index(1); 67 | assert!(a.mass > b.mass); 68 | 69 | may_closure_sort(&mut x, |a, b| a.mass.total_cmp(b.mass)); 70 | 71 | let a = x.index(0); 72 | let b = x.index(1); 73 | assert!(a.mass < b.mass); 74 | } 75 | 76 | 77 | #[derive(Debug, Clone)] 78 | struct VecWrap> { 79 | data: V, 80 | marker: PhantomData 81 | } 82 | 83 | impl> VecWrap { 84 | fn new() -> Self { 85 | Self { 86 | data: V::new(), 87 | marker: PhantomData 88 | } 89 | } 90 | 91 | fn push(&mut self, value: T) { 92 | self.data.push(value); 93 | } 94 | 95 | fn iter(&self) -> V::Iter<'_> { 96 | self.data.iter() 97 | } 98 | 99 | fn view(&self) -> V::Slice<'_> { 100 | self.data.as_slice() 101 | } 102 | 103 | fn first(&self) -> Option> { 104 | self.data.first() 105 | } 106 | 107 | fn sort_by(&mut self, f: F) where F: FnMut(V::Ref<'_>, V::Ref<'_>) -> std::cmp::Ordering { 108 | self.data.sort_by(f); 109 | } 110 | } 111 | 112 | #[test] 113 | fn test_wrapped() { 114 | let mut this: VecWrap = VecWrap::new(); 115 | let x = Particle::new("foo".into(), 100.0); 116 | this.push(x); 117 | let x = Particle::new("bar".into(), 1000.0); 118 | this.push(x); 119 | let x = Particle::new("baz".into(), 10.0); 120 | this.push(x); 121 | 122 | assert_eq!(this.iter().count(), 3); 123 | assert_eq!(this.view().len(), 3); 124 | } 125 | 126 | fn iter_max_generic<'a, T: StructOfArray, V: SoASlice + 'a>(vec: &'a V) -> Option> 127 | where 128 | V::Ref<'a>: PartialOrd + Debug 129 | { 130 | let x= vec.iter().reduce(|a, b| { 131 | if a.partial_cmp(&b).unwrap().is_ge() { 132 | a 133 | } else { 134 | b 135 | } 136 | }); 137 | x 138 | } 139 | 140 | fn iter_max_generic_iter<'a, T: StructOfArray, V: SoAVec>(it: V::Iter<'a>) -> Option> 141 | where 142 | V::Ref<'a>: PartialOrd 143 | { 144 | it.reduce(|a: V::Ref<'_>, b: V::Ref<'_>| { 145 | if a.partial_cmp(&b).unwrap().is_ge() { 146 | a 147 | } else { 148 | b 149 | } 150 | }) 151 | } 152 | 153 | fn slice_ref_len>(vec: &V) -> usize { 154 | let view = vec.as_slice(); 155 | let n = view.iter().count(); 156 | assert_eq!(view.into_iter().count(), n); 157 | n 158 | } 159 | 160 | 161 | #[test] 162 | fn test_ops() { 163 | let mut vec = ParticleVec::new(); 164 | vec.push(Particle::new("foo".into(), 100.0)); 165 | vec.push(Particle::new("bar".into(), 1000.0)); 166 | vec.push(Particle::new("baz".into(), 50.0)); 167 | // let x = iter_max_generic(&view); 168 | // eprintln!("{x:?}"); 169 | let y = iter_max_generic_iter::(vec.iter()); 170 | eprintln!("{y:?}"); 171 | let k = slice_ref_len(&vec); 172 | assert_eq!(k, 3); 173 | 174 | let mut view = vec.as_mut_slice(); 175 | view.iter_mut().for_each(|f| { 176 | *f.mass *= 2.0; 177 | }); 178 | 179 | let view = vec.as_slice(); 180 | let z = iter_max_generic(&view).unwrap(); 181 | assert_eq!(z.name, "foo"); 182 | 183 | let n = view.iter().count(); 184 | assert!(n > 0); 185 | 186 | let mut vec = VecWrap::::new(); 187 | vec.push(Particle::new("foo".into(), 100.0)); 188 | vec.push(Particle::new("bar".into(), 1000.0)); 189 | vec.push(Particle::new("baz".into(), 50.0)); 190 | vec.sort_by(|a, b| a.mass.total_cmp(b.mass)); 191 | 192 | assert_eq!(vec.first().unwrap().name, "baz"); 193 | } 194 | -------------------------------------------------------------------------------- /tests/index.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | mod particles; 4 | use self::particles::{Particle, ParticleVec, ParticleRef}; 5 | 6 | 7 | /// Helper function to assert that two iterators (one of SoA and another of AoS) are equal. 8 | fn eq_its<'a, I1, I2>(i1: I1, i2: I2) 9 | where 10 | I1: Iterator>, 11 | I2: Iterator, 12 | { 13 | for (p1, p2) in i1.zip(i2) { 14 | assert_eq!(p1.name, &p2.name); 15 | assert_eq!(*p1.mass, p2.mass); 16 | } 17 | } 18 | 19 | #[test] 20 | fn index_vec_with_usize() { 21 | let mut aos = Vec::new(); 22 | let mut soa = ParticleVec::new(); 23 | 24 | let particle = Particle::new(String::from("Na"), 56.0); 25 | aos.push(particle.clone()); 26 | soa.push(particle.clone()); 27 | 28 | // SoAIndex 29 | assert_eq!(soa.get(0).unwrap().name, &aos.first().unwrap().name); 30 | assert_eq!(soa.get(0).unwrap().mass, &aos.first().unwrap().mass); 31 | assert_eq!(aos.get(1), None); 32 | assert_eq!(soa.get(1), None); 33 | 34 | unsafe { 35 | assert_eq!(soa.get_unchecked(0).name, &aos.get_unchecked(0).name); 36 | assert_eq!(soa.get_unchecked(0).mass, &aos.get_unchecked(0).mass); 37 | } 38 | 39 | assert_eq!(soa.index(0).name, &aos[0].name); 40 | assert_eq!(soa.index(0).mass, &aos[0].mass); 41 | 42 | 43 | // SoaIndexMut 44 | assert_eq!(soa.get_mut(0).unwrap().name, &aos.get_mut(0).unwrap().name); 45 | assert_eq!(soa.get_mut(0).unwrap().mass, &aos.get_mut(0).unwrap().mass); 46 | assert_eq!(aos.get_mut(1), None); 47 | assert_eq!(soa.get_mut(1), None); 48 | 49 | unsafe { 50 | assert_eq!(soa.get_unchecked_mut(0).name, &aos.get_unchecked_mut(0).name); 51 | assert_eq!(soa.get_unchecked_mut(0).mass, &aos.get_unchecked_mut(0).mass); 52 | } 53 | 54 | assert_eq!(soa.index_mut(0).name, &aos[0].name); 55 | assert_eq!(soa.index_mut(0).mass, &aos[0].mass); 56 | 57 | 58 | *soa.index_mut(0).mass -= 1.; 59 | assert_eq!(soa.get(0).map(|p| *p.mass), Some(particle.mass - 1.)); 60 | 61 | *soa.get_mut(0).unwrap().mass += 1.; 62 | assert_eq!(soa.get(0).map(|p| *p.mass), Some(particle.mass)); 63 | } 64 | 65 | #[test] 66 | fn index_vec_with_ranges() { 67 | let particles = [ 68 | Particle::new(String::from("Cl"), 1.0), 69 | Particle::new(String::from("Na"), 2.0), 70 | Particle::new(String::from("Br"), 3.0), 71 | Particle::new(String::from("Zn"), 4.0), 72 | ]; 73 | 74 | let mut soa = ParticleVec::new(); 75 | for particle in particles.iter() { 76 | soa.push(particle.clone()); 77 | } 78 | 79 | eq_its(soa.iter(), particles.iter()); 80 | 81 | // All tests from here are the same only changing the range 82 | 83 | let range = 0..1; 84 | eq_its(soa.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 85 | unsafe { eq_its(soa.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 86 | eq_its(soa.index(range.clone()).iter(), particles[range.clone()].iter()); 87 | eq_its(soa.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 88 | unsafe { eq_its(soa.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 89 | eq_its(soa.index_mut(range.clone()).iter(), particles[range].iter()); 90 | 91 | let range = ..3; 92 | eq_its(soa.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 93 | unsafe { eq_its(soa.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 94 | eq_its(soa.index(range).iter(), particles[range].iter()); 95 | eq_its(soa.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 96 | unsafe { eq_its(soa.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 97 | eq_its(soa.index_mut(range).iter(), particles[range].iter()); 98 | 99 | let range = 1..; 100 | eq_its(soa.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 101 | unsafe { eq_its(soa.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 102 | eq_its(soa.index(range.clone()).iter(), particles[range.clone()].iter()); 103 | eq_its(soa.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 104 | unsafe { eq_its(soa.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 105 | eq_its(soa.index_mut(range.clone()).iter(), particles[range].iter()); 106 | 107 | let range = ..; 108 | eq_its(soa.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 109 | unsafe { eq_its(soa.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 110 | eq_its(soa.index(range).iter(), particles[range].iter()); 111 | eq_its(soa.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 112 | unsafe { eq_its(soa.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 113 | eq_its(soa.index_mut(range).iter(), particles[range].iter()); 114 | 115 | let range = 0..=1; 116 | eq_its(soa.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 117 | unsafe { eq_its(soa.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 118 | eq_its(soa.index(range.clone()).iter(), particles[range.clone()].iter()); 119 | eq_its(soa.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 120 | unsafe { eq_its(soa.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 121 | eq_its(soa.index_mut(range.clone()).iter(), particles[range].iter()); 122 | 123 | let range = ..=2; 124 | eq_its(soa.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 125 | unsafe { eq_its(soa.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 126 | eq_its(soa.index(range).iter(), particles[range].iter()); 127 | eq_its(soa.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 128 | unsafe { eq_its(soa.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 129 | eq_its(soa.index_mut(range).iter(), particles[range].iter()); 130 | } 131 | 132 | #[test] 133 | fn index_slice_with_usize() { 134 | let mut aos = Vec::new(); 135 | let mut soa = ParticleVec::new(); 136 | 137 | let particle = Particle::new(String::from("Na"), 56.0); 138 | aos.push(particle.clone()); 139 | soa.push(particle.clone()); 140 | 141 | // SoAIndex 142 | let aos_slice = aos.as_slice(); 143 | let soa_slice = soa.as_slice(); 144 | 145 | assert_eq!(soa_slice.get(0).unwrap().name, &aos_slice.first().unwrap().name); 146 | assert_eq!(soa_slice.get(0).unwrap().mass, &aos_slice.first().unwrap().mass); 147 | assert_eq!(aos_slice.get(1), None); 148 | assert_eq!(soa_slice.get(1), None); 149 | 150 | unsafe { 151 | assert_eq!(soa_slice.get_unchecked(0).name, &aos_slice.get_unchecked(0).name); 152 | assert_eq!(soa_slice.get_unchecked(0).mass, &aos_slice.get_unchecked(0).mass); 153 | } 154 | 155 | assert_eq!(soa_slice.index(0).name, &aos_slice[0].name); 156 | assert_eq!(soa_slice.index(0).mass, &aos_slice[0].mass); 157 | 158 | 159 | // SoaIndexMut 160 | let aos_mut_slice = aos.as_mut_slice(); 161 | let mut soa_mut_slice = soa.as_mut_slice(); 162 | assert_eq!(soa_mut_slice.get_mut(0).unwrap().name, &aos_mut_slice.get_mut(0).unwrap().name); 163 | assert_eq!(soa_mut_slice.get_mut(0).unwrap().mass, &aos_mut_slice.get_mut(0).unwrap().mass); 164 | assert_eq!(soa_mut_slice.get_mut(0).unwrap().mass, &aos_mut_slice.get_mut(0).unwrap().mass); 165 | assert_eq!(aos_mut_slice.get_mut(1), None); 166 | assert_eq!(soa_mut_slice.get_mut(1), None); 167 | 168 | unsafe { 169 | assert_eq!(soa_mut_slice.get_unchecked_mut(0).name, &aos_mut_slice.get_unchecked_mut(0).name); 170 | assert_eq!(soa_mut_slice.get_unchecked_mut(0).mass, &aos_mut_slice.get_unchecked_mut(0).mass); 171 | } 172 | 173 | assert_eq!(soa_mut_slice.index_mut(0).name, &aos_mut_slice[0].name); 174 | assert_eq!(soa_mut_slice.index_mut(0).mass, &aos_mut_slice[0].mass); 175 | 176 | 177 | *soa_mut_slice.index_mut(0).mass -= 1.; 178 | assert_eq!(soa_mut_slice.get(0).map(|p| *p.mass), Some(particle.mass - 1.)); 179 | 180 | *soa_mut_slice.get_mut(0).unwrap().mass += 1.; 181 | assert_eq!(soa_mut_slice.get(0).map(|p| *p.mass), Some(particle.mass)); 182 | } 183 | 184 | #[test] 185 | fn index_slice_with_ranges() { 186 | let particles = [ 187 | Particle::new(String::from("Cl"), 1.0), 188 | Particle::new(String::from("Na"), 2.0), 189 | Particle::new(String::from("Br"), 3.0), 190 | Particle::new(String::from("Zn"), 4.0), 191 | ]; 192 | 193 | let mut soa = ParticleVec::new(); 194 | for particle in particles.iter() { 195 | soa.push(particle.clone()); 196 | } 197 | 198 | eq_its(soa.iter(), particles.iter()); 199 | 200 | let mut soa_mut_slice = soa.as_mut_slice(); 201 | // All tests from here are the same only changing the range 202 | 203 | let range = 0..1; 204 | eq_its(soa_mut_slice.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 205 | unsafe { eq_its(soa_mut_slice.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 206 | eq_its(soa_mut_slice.index(range.clone()).iter(), particles[range.clone()].iter()); 207 | eq_its(soa_mut_slice.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 208 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 209 | eq_its(soa_mut_slice.index_mut(range.clone()).iter(), particles[range].iter()); 210 | 211 | let range = ..3; 212 | eq_its(soa_mut_slice.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 213 | unsafe { eq_its(soa_mut_slice.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 214 | eq_its(soa_mut_slice.index(range).iter(), particles[range].iter()); 215 | eq_its(soa_mut_slice.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 216 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 217 | eq_its(soa_mut_slice.index_mut(range).iter(), particles[range].iter()); 218 | 219 | let range = 1..; 220 | eq_its(soa_mut_slice.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 221 | unsafe { eq_its(soa_mut_slice.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 222 | eq_its(soa_mut_slice.index(range.clone()).iter(), particles[range.clone()].iter()); 223 | eq_its(soa_mut_slice.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 224 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 225 | eq_its(soa_mut_slice.index_mut(range.clone()).iter(), particles[range].iter()); 226 | 227 | let range = ..; 228 | eq_its(soa_mut_slice.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 229 | unsafe { eq_its(soa_mut_slice.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 230 | eq_its(soa_mut_slice.index(range).iter(), particles[range].iter()); 231 | eq_its(soa_mut_slice.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 232 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 233 | eq_its(soa_mut_slice.index_mut(range).iter(), particles[range].iter()); 234 | 235 | let range = 0..=1; 236 | eq_its(soa_mut_slice.get(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 237 | unsafe { eq_its(soa_mut_slice.get_unchecked(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 238 | eq_its(soa_mut_slice.index(range.clone()).iter(), particles[range.clone()].iter()); 239 | eq_its(soa_mut_slice.get_mut(range.clone()).unwrap().iter(), particles.get(range.clone()).unwrap().iter()); 240 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range.clone()).iter(), particles.get_unchecked(range.clone()).iter()); } 241 | eq_its(soa_mut_slice.index_mut(range.clone()).iter(), particles[range].iter()); 242 | 243 | let range = ..=2; 244 | eq_its(soa_mut_slice.get(range).unwrap().iter(), particles.get(range).unwrap().iter()); 245 | unsafe { eq_its(soa_mut_slice.get_unchecked(range).iter(), particles.get_unchecked(range).iter()); } 246 | eq_its(soa_mut_slice.index(range).iter(), particles[range].iter()); 247 | eq_its(soa_mut_slice.get_mut(range).unwrap().iter(), particles.get(range).unwrap().iter()); 248 | unsafe { eq_its(soa_mut_slice.get_unchecked_mut(range).iter(), particles.get_unchecked(range).iter()); } 249 | eq_its(soa_mut_slice.index_mut(range).iter(), particles[range].iter()); 250 | } 251 | -------------------------------------------------------------------------------- /tests/iter.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | mod particles; 4 | use self::particles::*; 5 | 6 | #[test] 7 | fn iter() { 8 | let mut particles = ParticleVec::new(); 9 | particles.push(Particle::new(String::from("Na"), 56.0)); 10 | particles.push(Particle::new(String::from("Cl"), 56.0)); 11 | particles.push(Particle::new(String::from("Zn"), 56.0)); 12 | 13 | let mut iter = particles.iter(); 14 | assert_eq!(iter.next().unwrap().name, "Na"); 15 | assert_eq!(iter.next().unwrap().name, "Cl"); 16 | assert_eq!(iter.next().unwrap().name, "Zn"); 17 | 18 | assert!(iter.next().is_none()); 19 | 20 | let slice = particles.as_slice(); 21 | let mut iter = slice.iter(); 22 | assert_eq!(iter.next().unwrap().name, "Na"); 23 | assert_eq!(iter.next().unwrap().name, "Cl"); 24 | assert_eq!(iter.next().unwrap().name, "Zn"); 25 | 26 | assert!(iter.next().is_none()); 27 | } 28 | 29 | #[test] 30 | fn iter_mut() { 31 | let mut particles = ParticleVec::new(); 32 | particles.push(Particle::new(String::from("Na"), 0.0)); 33 | particles.push(Particle::new(String::from("Cl"), 0.0)); 34 | particles.push(Particle::new(String::from("Zn"), 0.0)); 35 | 36 | for particle in particles.iter_mut() { 37 | *particle.mass += 1.0; 38 | } 39 | assert_eq!(*particles.index(0).mass, 1.0); 40 | assert_eq!(*particles.index(1).mass, 1.0); 41 | assert_eq!(*particles.index(2).mass, 1.0); 42 | 43 | { 44 | let mut slice = particles.as_mut_slice(); 45 | for particle in slice.iter_mut() { 46 | *particle.mass += 1.0; 47 | } 48 | } 49 | 50 | assert_eq!(*particles.index(0).mass, 2.0); 51 | assert_eq!(*particles.index(1).mass, 2.0); 52 | assert_eq!(*particles.index(2).mass, 2.0); 53 | } 54 | 55 | #[test] 56 | fn from_iter() { 57 | let vec_with_particles = vec![ 58 | Particle::new(String::from("Na"), 0.0), 59 | Particle::new(String::from("Cl"), 0.0), 60 | Particle::new(String::from("Zn"), 0.0), 61 | ]; 62 | 63 | let particles_from_iter: ParticleVec = vec_with_particles.into_iter().collect(); 64 | 65 | let mut particles = ParticleVec::new(); 66 | particles.push(Particle::new(String::from("Na"), 0.0)); 67 | particles.push(Particle::new(String::from("Cl"), 0.0)); 68 | particles.push(Particle::new(String::from("Zn"), 0.0)); 69 | 70 | assert_eq!(particles, particles_from_iter) 71 | } 72 | 73 | #[test] 74 | fn extend() { 75 | let vec_with_particles = vec![ 76 | Particle::new(String::from("Na"), 0.0), 77 | Particle::new(String::from("Cl"), 0.0), 78 | Particle::new(String::from("Zn"), 0.0), 79 | ]; 80 | 81 | let particles_from_iter: ParticleVec = vec_with_particles.clone().into_iter().collect(); 82 | 83 | let mut particles = ParticleVec::new(); 84 | Extend::::extend(&mut particles, vec_with_particles); 85 | 86 | assert_eq!(particles, particles_from_iter); 87 | 88 | let mut particles = ParticleVec::new(); 89 | Extend::::extend(&mut particles, particles_from_iter.iter()); 90 | assert_eq!(particles, particles_from_iter); 91 | 92 | let mut particles = ParticleVec::new(); 93 | particles.extend(&particles_from_iter); 94 | assert_eq!(particles, particles_from_iter); 95 | } 96 | -------------------------------------------------------------------------------- /tests/nested_soa.rs: -------------------------------------------------------------------------------- 1 | use soa_derive::StructOfArray; 2 | 3 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 4 | pub struct Point { 5 | x: f32, 6 | y: f32, 7 | } 8 | 9 | mod other_mod { 10 | use soa_derive::StructOfArray; 11 | 12 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 13 | #[soa_derive(Debug, Clone, PartialEq)] 14 | pub struct Color { 15 | pub r: u8, 16 | pub g: u8, 17 | pub b: u8, 18 | pub a: u8, 19 | } 20 | } 21 | 22 | use other_mod::*; 23 | 24 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 25 | #[soa_derive(Debug, Clone, PartialEq)] 26 | pub struct Particle { 27 | pub point: Point, 28 | #[nested_soa] 29 | pub color: Color, 30 | pub mass: f32, 31 | } 32 | 33 | #[test] 34 | fn nested_soa() { 35 | let mut particle_vec = ParticleVec::new(); 36 | particle_vec.push(Particle { 37 | point: Point { x: 1.0, y: 2.0 }, 38 | color: Color { r: 255, g: 0, b: 0, a: 255 }, 39 | mass: 1.0, 40 | }); 41 | particle_vec.push(Particle { 42 | point: Point { x: 2.0, y: 3.0 }, 43 | color: Color { r: 128, g: 255, b: 100, a: 23 }, 44 | mass: 2.0, 45 | }); 46 | assert_eq!(particle_vec.point[0], Point { 47 | x: 1.0, y: 2.0 48 | }); 49 | assert_eq!(particle_vec.color.r[0], 255); 50 | assert_eq!(particle_vec.color.g[0], 0); 51 | assert_eq!(particle_vec.color.b[0], 0); 52 | assert_eq!(particle_vec.color.a[0], 255); 53 | assert_eq!(particle_vec.point[1], Point { 54 | x: 2.0, y: 3.0 55 | }); 56 | assert_eq!(particle_vec.color.r[1], 128); 57 | assert_eq!(particle_vec.color.g[1], 255); 58 | assert_eq!(particle_vec.color.b[1], 100); 59 | assert_eq!(particle_vec.color.a[1], 23); 60 | assert_eq!(particle_vec.color, ColorVec { 61 | r: vec![255, 128], 62 | g: vec![0, 255], 63 | b: vec![0, 100], 64 | a: vec![255, 23], 65 | }); 66 | } 67 | -------------------------------------------------------------------------------- /tests/particles/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use soa_derive::*; 3 | use itertools::Itertools; 4 | 5 | #[derive(Debug, Clone, PartialOrd, PartialEq, StructOfArray)] 6 | #[soa_derive(Debug, Clone, PartialOrd, PartialEq)] 7 | pub struct Particle { 8 | pub name: String, 9 | pub mass: f64, 10 | } 11 | 12 | impl Particle { 13 | pub fn new(name: String, mass: f64) -> Self { 14 | Particle { 15 | name, 16 | mass, 17 | } 18 | } 19 | } 20 | 21 | impl ParticleVec { 22 | pub fn extend(&mut self, other: &ParticleVec) { 23 | self.name.extend_from_slice(&other.name); 24 | self.mass.extend_from_slice(&other.mass); 25 | } 26 | } 27 | 28 | #[test] 29 | fn use_iterator_tools_get() { 30 | let vec = ParticleVec::new(); 31 | let particle = vec.iter().get(..0).find_or_first(|_|true); 32 | assert_eq!(particle, None); 33 | } 34 | -------------------------------------------------------------------------------- /tests/ptr.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | use std::sync::atomic::{AtomicUsize, Ordering}; 4 | 5 | mod particles; 6 | 7 | use self::particles::{Particle, ParticleVec, ParticleSlice, ParticleSliceMut}; 8 | 9 | #[test] 10 | fn const_ref() { 11 | let particle = Particle::new(String::from("Cl"), 0.0); 12 | 13 | let reference = particle.as_ref(); 14 | let ptr = reference.as_ptr(); 15 | 16 | unsafe { 17 | assert_eq!(ptr.as_ref().unwrap().name, "Cl"); 18 | assert_eq!(*ptr.as_ref().unwrap().mass, 0.0); 19 | } 20 | } 21 | 22 | #[test] 23 | fn mut_ref() { 24 | let mut particle = Particle::new(String::from("Cl"), 0.0); 25 | let mut reference = particle.as_mut(); 26 | 27 | let ptr = reference.as_mut_ptr(); 28 | 29 | unsafe { 30 | *ptr.as_mut().unwrap().name = String::from("Fe"); 31 | *ptr.as_mut().unwrap().mass = 42.0; 32 | } 33 | 34 | let ptr = reference.as_ptr(); 35 | 36 | unsafe { 37 | assert_eq!(ptr.as_ref().unwrap().name, "Fe"); 38 | assert_eq!(*ptr.as_ref().unwrap().mass, 42.0); 39 | } 40 | } 41 | 42 | #[test] 43 | fn slice() { 44 | let mut particles = ParticleVec::new(); 45 | 46 | particles.push(Particle::new(String::from("Na"), 1.0)); 47 | particles.push(Particle::new(String::from("Zn"), 2.0)); 48 | particles.push(Particle::new(String::from("Fe"), 3.0)); 49 | 50 | let slice = particles.as_slice(); 51 | let ptr = slice.as_ptr(); 52 | 53 | unsafe { 54 | assert_eq!(ptr.as_ref().unwrap().name, "Na"); 55 | assert_eq!(*ptr.as_ref().unwrap().mass, 1.0); 56 | } 57 | 58 | unsafe { 59 | let slice = ParticleSlice::from_raw_parts(ptr, 2); 60 | assert_eq!(slice.len(), 2); 61 | assert_eq!(slice.name[0], "Na"); 62 | assert_eq!(slice.name[1], "Zn"); 63 | 64 | assert_eq!(slice.mass[0], 1.0); 65 | assert_eq!(slice.mass[1], 2.0); 66 | } 67 | } 68 | 69 | #[test] 70 | fn slice_mut() { 71 | let mut particles = ParticleVec::new(); 72 | 73 | particles.push(Particle::new(String::from("Na"), 1.0)); 74 | particles.push(Particle::new(String::from("Zn"), 2.0)); 75 | particles.push(Particle::new(String::from("Fe"), 3.0)); 76 | 77 | let mut slice = particles.as_mut_slice(); 78 | let ptr = slice.as_mut_ptr(); 79 | 80 | unsafe { 81 | *ptr.as_mut().unwrap().name = String::from("Fe"); 82 | *ptr.as_mut().unwrap().mass = 42.0; 83 | } 84 | 85 | assert_eq!(slice.name[0], "Fe"); 86 | assert_eq!(slice.mass[0], 42.0); 87 | 88 | unsafe { 89 | let slice = ParticleSliceMut::from_raw_parts_mut(slice.as_mut_ptr(), 2); 90 | 91 | for mass in slice.mass { 92 | *mass = -1.0; 93 | } 94 | } 95 | 96 | assert_eq!(slice.mass[0], -1.0); 97 | assert_eq!(slice.mass[1], -1.0); 98 | assert_eq!(slice.mass[2], 3.0); 99 | } 100 | 101 | #[test] 102 | fn vec() { 103 | let mut particles = ParticleVec::new(); 104 | 105 | particles.push(Particle::new(String::from("Na"), 1.0)); 106 | particles.push(Particle::new(String::from("Zn"), 2.0)); 107 | particles.push(Particle::new(String::from("Fe"), 3.0)); 108 | 109 | let len = particles.len(); 110 | let capacity = particles.capacity(); 111 | let ptr = particles.as_mut_ptr(); 112 | 113 | std::mem::forget(particles); 114 | 115 | unsafe { 116 | *ptr.as_mut().unwrap().name = String::from("Fe"); 117 | *ptr.as_mut().unwrap().mass = 42.0; 118 | } 119 | 120 | let particles = unsafe { 121 | ParticleVec::from_raw_parts(ptr, len, capacity) 122 | }; 123 | 124 | assert_eq!(particles.len(), len); 125 | assert_eq!(particles.capacity(), capacity); 126 | 127 | assert_eq!(particles.name[0], "Fe"); 128 | assert_eq!(particles.mass[0], 42.0); 129 | 130 | assert_eq!(particles.name[1], "Zn"); 131 | assert_eq!(particles.mass[1], 2.0); 132 | 133 | assert_eq!(particles.name[2], "Fe"); 134 | assert_eq!(particles.mass[2], 3.0); 135 | } 136 | 137 | #[derive(Clone, soa_derive::StructOfArray)] 138 | #[soa_derive(Clone)] 139 | struct CountOnDrop { 140 | data: usize, 141 | } 142 | 143 | static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0); 144 | 145 | impl Drop for CountOnDrop { 146 | fn drop(&mut self) { 147 | DROP_COUNTER.fetch_add(1, Ordering::SeqCst); 148 | } 149 | } 150 | 151 | #[test] 152 | fn write() { 153 | { 154 | let mut vec = CountOnDropVec::new(); 155 | vec.push(CountOnDrop { data: 0 }); 156 | 157 | let ptr = vec.as_mut_ptr(); 158 | unsafe { 159 | // this does not drop the already existing value in the vec 160 | ptr.write(CountOnDrop { data: 4 }); 161 | } 162 | 163 | assert_eq!(vec.data[0], 4); 164 | } 165 | 166 | assert_eq!(DROP_COUNTER.load(Ordering::SeqCst), 1); 167 | } 168 | -------------------------------------------------------------------------------- /tests/replace.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | mod particles; 4 | use self::particles::{Particle, ParticleVec}; 5 | 6 | #[test] 7 | fn replace_element() { 8 | let mut soa = ParticleVec::new(); 9 | soa.push(Particle::new(String::from("Na"), 22.990)); 10 | soa.push(Particle::new(String::from("Zn"), 65.380)); 11 | soa.push(Particle::new(String::from("Cl"), 35.453)); 12 | 13 | let particle = soa.replace(1, Particle::new(String::from("Br"), 79.904)); 14 | assert_eq!(particle.name, "Zn"); 15 | assert_eq!(particle.mass, 65.380); 16 | 17 | assert_eq!(soa.name[1], "Br"); 18 | assert_eq!(soa.mass[1], 79.904); 19 | } 20 | 21 | #[test] 22 | fn replace_mutable_reference() { 23 | let mut soa = ParticleVec::new(); 24 | soa.push(Particle::new(String::from("Na"), 22.990)); 25 | soa.push(Particle::new(String::from("Zn"), 65.380)); 26 | soa.push(Particle::new(String::from("Cl"), 35.453)); 27 | 28 | let particle = soa.index_mut(1).replace(Particle::new(String::from("Br"), 79.904)); 29 | assert_eq!(particle.name, "Zn"); 30 | assert_eq!(particle.mass, 65.380); 31 | 32 | assert_eq!(soa.name[1], "Br"); 33 | assert_eq!(soa.mass[1], 79.904); 34 | } 35 | -------------------------------------------------------------------------------- /tests/serde.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use soa_derive::StructOfArray; 3 | 4 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 5 | #[soa_derive(Debug, Clone, PartialEq, Serialize, Deserialize)] 6 | pub struct Particle { 7 | pub name: String, 8 | pub mass: f64, 9 | } 10 | 11 | impl Particle { 12 | pub fn new(name: String, mass: f64) -> Self { 13 | Particle { 14 | name, 15 | mass, 16 | } 17 | } 18 | } 19 | 20 | #[test] 21 | fn serde_test() -> Result<(), serde_json::Error> { 22 | let mut soa = ParticleVec::new(); 23 | soa.push(Particle::new(String::from("Na"), 56.0)); 24 | soa.push(Particle::new(String::from("Cl"), 35.0)); 25 | 26 | let json = serde_json::to_string(&soa)?; 27 | assert_eq!(json, r#"{"name":["Na","Cl"],"mass":[56.0,35.0]}"#); 28 | let soa2: ParticleVec = serde_json::from_str(&json)?; 29 | assert_eq!(soa, soa2); 30 | Ok(()) 31 | } 32 | -------------------------------------------------------------------------------- /tests/slice.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | mod particles; 4 | use self::particles::{Particle, ParticleVec}; 5 | 6 | #[test] 7 | fn len() { 8 | let mut particles = ParticleVec::new(); 9 | 10 | { 11 | let slice = particles.as_slice(); 12 | assert!(slice.is_empty()); 13 | assert_eq!(slice.len(), 0); 14 | } 15 | 16 | particles.push(Particle::new(String::from("Na"), 56.0)); 17 | particles.push(Particle::new(String::from("Na"), 56.0)); 18 | particles.push(Particle::new(String::from("Na"), 56.0)); 19 | 20 | let slice = particles.as_slice(); 21 | assert_eq!(slice.len(), 3); 22 | } 23 | 24 | #[test] 25 | fn first_last() { 26 | let mut particles = ParticleVec::new(); 27 | 28 | { 29 | let slice = particles.as_slice(); 30 | assert_eq!(slice.first(), None); 31 | assert_eq!(slice.last(), None); 32 | } 33 | 34 | particles.push(Particle::new(String::from("Na"), 0.0)); 35 | particles.push(Particle::new(String::from("Zn"), 0.0)); 36 | particles.push(Particle::new(String::from("Cl"), 0.0)); 37 | 38 | let slice = particles.as_slice(); 39 | assert_eq!(slice.first().unwrap().name, "Na"); 40 | assert_eq!(slice.last().unwrap().name, "Cl"); 41 | } 42 | 43 | #[test] 44 | fn split() { 45 | let mut particles = ParticleVec::new(); 46 | { 47 | let slice = particles.as_slice(); 48 | assert_eq!(slice.split_first(), None); 49 | assert_eq!(slice.split_last(), None); 50 | } 51 | particles.push(Particle::new(String::from("Cl"), 0.0)); 52 | particles.push(Particle::new(String::from("Na"), 0.0)); 53 | particles.push(Particle::new(String::from("Br"), 0.0)); 54 | particles.push(Particle::new(String::from("Zn"), 0.0)); 55 | 56 | let slice = particles.as_slice(); 57 | let (first, end) = slice.split_first().unwrap(); 58 | assert_eq!(first.name, "Cl"); 59 | assert_eq!(end.len(), 3); 60 | 61 | let (last, start) = slice.split_last().unwrap(); 62 | assert_eq!(last.name, "Zn"); 63 | assert_eq!(start.len(), 3); 64 | 65 | let (start, end) = slice.split_at(1); 66 | assert_eq!(start.len(), 1); 67 | assert_eq!(start.name[0], "Cl"); 68 | assert_eq!(end.len(), 3); 69 | assert_eq!(end.name[0], "Na"); 70 | } 71 | 72 | #[test] 73 | fn refs() { 74 | let mut particle = Particle::new(String::from("Cl"), 0.0); 75 | assert_eq!(particle.as_ref().name, "Cl"); 76 | { 77 | let mut_ref = particle.as_mut(); 78 | *mut_ref.mass = 42.0; 79 | } 80 | assert_eq!(particle.mass, 42.0); 81 | } 82 | 83 | #[test] 84 | fn get() { 85 | let mut particles = ParticleVec::new(); 86 | assert_eq!(particles.as_slice().get(0), None); 87 | 88 | particles.push(Particle::new(String::from("Cl"), 0.0)); 89 | particles.push(Particle::new(String::from("Na"), 0.0)); 90 | particles.push(Particle::new(String::from("Br"), 0.0)); 91 | particles.push(Particle::new(String::from("Zn"), 0.0)); 92 | 93 | assert_eq!(particles.as_slice().get(0).unwrap().name, "Cl"); 94 | assert_eq!(particles.as_slice().get(10), None); 95 | 96 | unsafe { 97 | assert_eq!(particles.as_slice().get_unchecked(0).name, "Cl"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /tests/slice_mut.rs: -------------------------------------------------------------------------------- 1 | mod particles; 2 | use self::particles::{Particle, ParticleVec}; 3 | 4 | #[test] 5 | fn len() { 6 | let mut particles = ParticleVec::new(); 7 | 8 | { 9 | let slice = particles.as_mut_slice(); 10 | assert!(slice.is_empty()); 11 | assert_eq!(slice.len(), 0); 12 | } 13 | 14 | particles.push(Particle::new(String::from("Na"), 56.0)); 15 | particles.push(Particle::new(String::from("Na"), 56.0)); 16 | particles.push(Particle::new(String::from("Na"), 56.0)); 17 | 18 | let slice = particles.as_mut_slice(); 19 | assert_eq!(slice.len(), 3); 20 | } 21 | 22 | #[test] 23 | fn first_last() { 24 | let mut particles = ParticleVec::new(); 25 | 26 | { 27 | let mut slice = particles.as_mut_slice(); 28 | assert_eq!(slice.first_mut(), None); 29 | assert_eq!(slice.last_mut(), None); 30 | } 31 | 32 | particles.push(Particle::new(String::from("Na"), 0.0)); 33 | particles.push(Particle::new(String::from("Zn"), 0.0)); 34 | particles.push(Particle::new(String::from("Cl"), 0.0)); 35 | 36 | let mut slice = particles.as_mut_slice(); 37 | assert_eq!(slice.first_mut().unwrap().name, "Na"); 38 | assert_eq!(slice.last_mut().unwrap().name, "Cl"); 39 | } 40 | 41 | #[test] 42 | fn split() { 43 | let mut particles = ParticleVec::new(); 44 | { 45 | let mut slice = particles.as_mut_slice(); 46 | assert_eq!(slice.reborrow().split_first_mut(), None); 47 | assert_eq!(slice.reborrow().split_last_mut(), None); 48 | } 49 | particles.push(Particle::new(String::from("Cl"), 0.0)); 50 | particles.push(Particle::new(String::from("Na"), 0.0)); 51 | particles.push(Particle::new(String::from("Br"), 0.0)); 52 | particles.push(Particle::new(String::from("Zn"), 0.0)); 53 | 54 | let mut slice = particles.as_mut_slice(); 55 | { 56 | let (first, end) = slice.reborrow().split_first_mut().unwrap(); 57 | assert_eq!(first.name, "Cl"); 58 | assert_eq!(end.len(), 3); 59 | } 60 | 61 | { 62 | let (last, start) = slice.reborrow().split_last_mut().unwrap(); 63 | assert_eq!(last.name, "Zn"); 64 | assert_eq!(start.len(), 3); 65 | } 66 | 67 | { 68 | let (start, end) = slice.reborrow().split_at_mut(1); 69 | assert_eq!(start.len(), 1); 70 | assert_eq!(start.name[0], "Cl"); 71 | assert_eq!(end.len(), 3); 72 | assert_eq!(end.name[0], "Na"); 73 | } 74 | } 75 | 76 | #[test] 77 | fn get() { 78 | let mut particles = ParticleVec::new(); 79 | assert_eq!(particles.as_mut_slice().get(0), None); 80 | assert_eq!(particles.as_mut_slice().get_mut(0), None); 81 | 82 | particles.push(Particle::new(String::from("Cl"), 0.0)); 83 | particles.push(Particle::new(String::from("Na"), 0.0)); 84 | particles.push(Particle::new(String::from("Br"), 0.0)); 85 | particles.push(Particle::new(String::from("Zn"), 0.0)); 86 | 87 | assert_eq!(particles.as_mut_slice().get(0).unwrap().name, "Cl"); 88 | assert_eq!(particles.as_mut_slice().get_mut(1).unwrap().name, "Na"); 89 | assert_eq!(particles.as_mut_slice().get(10), None); 90 | assert_eq!(particles.as_mut_slice().get_mut(42), None); 91 | 92 | unsafe { 93 | assert_eq!(particles.as_mut_slice().get_unchecked(0).name, "Cl"); 94 | assert_eq!(particles.as_mut_slice().get_unchecked_mut(2).name, "Br"); 95 | } 96 | } 97 | 98 | #[test] 99 | fn split_non_mut() { 100 | let mut particles = ParticleVec::new(); 101 | { 102 | let mut_slice = particles.as_mut_slice(); 103 | let slice = mut_slice.as_ref(); 104 | assert_eq!(slice.split_first(), None); 105 | assert_eq!(slice.split_last(), None); 106 | } 107 | particles.push(Particle::new(String::from("Cl"), 0.0)); 108 | particles.push(Particle::new(String::from("Na"), 0.0)); 109 | particles.push(Particle::new(String::from("Br"), 0.0)); 110 | particles.push(Particle::new(String::from("Zn"), 0.0)); 111 | 112 | let mut_slice = particles.as_mut_slice(); 113 | let slice = mut_slice.as_ref(); 114 | 115 | let (first, end) = slice.split_first().unwrap(); 116 | assert_eq!(first.name, "Cl"); 117 | assert_eq!(end.len(), 3); 118 | 119 | let (last, start) = slice.split_last().unwrap(); 120 | assert_eq!(last.name, "Zn"); 121 | assert_eq!(start.len(), 3); 122 | 123 | let (start, end) = slice.split_at(1); 124 | assert_eq!(start.len(), 1); 125 | assert_eq!(start.name[0], "Cl"); 126 | assert_eq!(end.len(), 3); 127 | assert_eq!(end.name[0], "Na"); 128 | } 129 | 130 | #[test] 131 | fn sort() { 132 | let mut particles = ParticleVec::new(); 133 | particles.push(Particle::new(String::from("Na3"), 168.0)); 134 | particles.push(Particle::new(String::from("Na"), 56.0)); 135 | particles.push(Particle::new(String::from("Na4"), 224.0)); 136 | particles.push(Particle::new(String::from("Na2"), 112.0)); 137 | 138 | let mut slice = particles.as_mut_slice(); 139 | 140 | slice.sort_by(|j, k| { j.partial_cmp(&k).unwrap() }); 141 | 142 | let mut ordered_particles = ParticleVec::new(); 143 | ordered_particles.push(Particle::new(String::from("Na"), 56.0)); 144 | ordered_particles.push(Particle::new(String::from("Na2"), 112.0)); 145 | ordered_particles.push(Particle::new(String::from("Na3"), 168.0)); 146 | ordered_particles.push(Particle::new(String::from("Na4"), 224.0)); 147 | 148 | assert_eq!(particles, ordered_particles); 149 | } 150 | -------------------------------------------------------------------------------- /tests/soa_attr.rs: -------------------------------------------------------------------------------- 1 | use soa_derive::StructOfArray; 2 | 3 | #[derive(Debug, Clone, PartialEq, StructOfArray)] 4 | #[soa_attr(Vec, cfg_attr(test, derive(PartialEq, Debug)))] 5 | pub struct Particle { 6 | pub name: String, 7 | pub mass: f64, 8 | } 9 | 10 | impl Particle { 11 | pub fn new(name: String, mass: f64) -> Self { 12 | Particle { name, mass } 13 | } 14 | } 15 | 16 | #[test] 17 | fn eq_test() { 18 | let particles0 = ParticleVec { 19 | name: vec![String::from("foo"), String::from("bar")], 20 | mass: vec![1.0, 2.0], 21 | }; 22 | let particles1 = ParticleVec { 23 | name: vec![String::from("foo"), String::from("bar")], 24 | mass: vec![1.0, 2.0], 25 | }; 26 | assert_eq!(particles0, particles1); 27 | } 28 | -------------------------------------------------------------------------------- /tests/vec.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | use std::cell::Cell; 4 | use std::rc::Rc; 5 | 6 | mod particles; 7 | use self::particles::{Particle, ParticleVec}; 8 | use soa_derive::StructOfArray; 9 | 10 | #[test] 11 | fn ty() { 12 | let _: ::Type = ParticleVec::new(); 13 | } 14 | 15 | #[test] 16 | fn push() { 17 | let mut particles = ParticleVec::new(); 18 | particles.push(Particle::new(String::from("Na"), 56.0)); 19 | 20 | assert_eq!(particles.name[0], "Na"); 21 | assert_eq!(particles.mass[0], 56.0); 22 | } 23 | 24 | #[test] 25 | fn len() { 26 | let mut particles = ParticleVec::new(); 27 | assert_eq!(particles.len(), 0); 28 | assert!(particles.is_empty()); 29 | 30 | particles.push(Particle::new(String::from("Na"), 56.0)); 31 | particles.push(Particle::new(String::from("Na"), 56.0)); 32 | particles.push(Particle::new(String::from("Na"), 56.0)); 33 | assert_eq!(particles.len(), 3); 34 | 35 | particles.clear(); 36 | assert_eq!(particles.len(), 0); 37 | } 38 | 39 | #[test] 40 | fn capacity() { 41 | let mut particles = ParticleVec::with_capacity(9); 42 | assert_eq!(particles.len(), 0); 43 | assert_eq!(particles.capacity(), 9); 44 | 45 | particles.reserve(42); 46 | assert!(particles.capacity() >= 42); 47 | 48 | particles.reserve_exact(100); 49 | assert!(particles.capacity() == 100); 50 | 51 | particles.push(Particle::new(String::from("Na"), 56.0)); 52 | particles.push(Particle::new(String::from("Na"), 56.0)); 53 | assert_eq!(particles.len(), 2); 54 | assert!(particles.capacity() == 100); 55 | 56 | particles.shrink_to_fit(); 57 | assert_eq!(particles.len(), 2); 58 | assert_eq!(particles.capacity(), 2); 59 | } 60 | 61 | #[test] 62 | fn remove() { 63 | let mut particles = ParticleVec::new(); 64 | particles.push(Particle::new(String::from("Cl"), 0.0)); 65 | particles.push(Particle::new(String::from("Na"), 0.0)); 66 | particles.push(Particle::new(String::from("Br"), 0.0)); 67 | particles.push(Particle::new(String::from("Zn"), 0.0)); 68 | 69 | let particle = particles.remove(1); 70 | assert_eq!(particle.name, "Na"); 71 | assert_eq!(particles.name[0], "Cl"); 72 | assert_eq!(particles.name[1], "Br"); 73 | assert_eq!(particles.name[2], "Zn"); 74 | } 75 | 76 | #[test] 77 | fn swap_remove() { 78 | let mut particles = ParticleVec::new(); 79 | particles.push(Particle::new(String::from("Cl"), 0.0)); 80 | particles.push(Particle::new(String::from("Na"), 0.0)); 81 | particles.push(Particle::new(String::from("Br"), 0.0)); 82 | particles.push(Particle::new(String::from("Zn"), 0.0)); 83 | 84 | let particle = particles.swap_remove(1); 85 | assert_eq!(particle.name, "Na"); 86 | assert_eq!(particles.index(0).name, "Cl"); 87 | assert_eq!(particles.index(1).name, "Zn"); 88 | assert_eq!(particles.index(2).name, "Br"); 89 | } 90 | 91 | #[test] 92 | fn insert() { 93 | let mut particles = ParticleVec::new(); 94 | particles.push(Particle::new(String::from("Cl"), 0.0)); 95 | particles.push(Particle::new(String::from("Na"), 0.0)); 96 | 97 | particles.insert(1, Particle::new(String::from("Zn"), 0.0)); 98 | assert_eq!(particles.index(0).name, "Cl"); 99 | assert_eq!(particles.index(1).name, "Zn"); 100 | assert_eq!(particles.index(2).name, "Na"); 101 | } 102 | 103 | #[test] 104 | fn pop() { 105 | let mut particles = ParticleVec::new(); 106 | particles.push(Particle::new(String::from("Cl"), 0.0)); 107 | particles.push(Particle::new(String::from("Na"), 0.0)); 108 | 109 | let particle = particles.pop(); 110 | assert_eq!(particle, Some(Particle::new(String::from("Na"), 0.0))); 111 | 112 | let particle = particles.pop(); 113 | assert_eq!(particle, Some(Particle::new(String::from("Cl"), 0.0))); 114 | 115 | let particle = particles.pop(); 116 | assert_eq!(particle, None) 117 | } 118 | 119 | #[test] 120 | fn append() { 121 | let mut particles = ParticleVec::new(); 122 | particles.push(Particle::new(String::from("Cl"), 0.0)); 123 | particles.push(Particle::new(String::from("Na"), 0.0)); 124 | 125 | let mut others = ParticleVec::new(); 126 | others.push(Particle::new(String::from("Zn"), 0.0)); 127 | others.push(Particle::new(String::from("Mg"), 0.0)); 128 | 129 | particles.append(&mut others); 130 | assert_eq!(particles.index(0).name, "Cl"); 131 | assert_eq!(particles.index(1).name, "Na"); 132 | assert_eq!(particles.index(2).name, "Zn"); 133 | assert_eq!(particles.index(3).name, "Mg"); 134 | } 135 | 136 | #[test] 137 | fn split_off() { 138 | let mut particles = ParticleVec::new(); 139 | particles.push(Particle::new(String::from("Cl"), 0.0)); 140 | particles.push(Particle::new(String::from("Na"), 0.0)); 141 | particles.push(Particle::new(String::from("Zn"), 0.0)); 142 | particles.push(Particle::new(String::from("Mg"), 0.0)); 143 | 144 | let other = particles.split_off(2); 145 | assert_eq!(particles.len(), 2); 146 | assert_eq!(other.len(), 2); 147 | 148 | assert_eq!(particles.index(0).name, "Cl"); 149 | assert_eq!(particles.index(1).name, "Na"); 150 | assert_eq!(other.index(0).name, "Zn"); 151 | assert_eq!(other.index(1).name, "Mg"); 152 | } 153 | 154 | #[test] 155 | fn retain() { 156 | let mut particles = ParticleVec::new(); 157 | particles.push(Particle::new(String::from("Cl"), 0.0)); 158 | particles.push(Particle::new(String::from("Na"), 0.0)); 159 | particles.push(Particle::new(String::from("Zn"), 0.0)); 160 | particles.push(Particle::new(String::from("C"), 0.0)); 161 | 162 | particles.retain(|particle| particle.name.starts_with('C')); 163 | assert_eq!(particles.len(), 2); 164 | assert_eq!(particles.index(0).name, "Cl"); 165 | assert_eq!(particles.index(1).name, "C"); 166 | } 167 | 168 | #[test] 169 | fn retain_mut() { 170 | let mut particles = ParticleVec::new(); 171 | particles.push(Particle::new(String::from("Cl"), 1.0)); 172 | particles.push(Particle::new(String::from("Na"), 1.0)); 173 | particles.push(Particle::new(String::from("Zn"), 0.0)); 174 | particles.push(Particle::new(String::from("C"), 1.0)); 175 | 176 | particles.retain_mut(|particle| { 177 | particle.name.make_ascii_uppercase(); 178 | *particle.mass > 0.5 179 | }); 180 | assert_eq!(particles.len(), 3); 181 | assert!(["CL", "NA", "C"].iter().copied().eq(particles.name.iter())); 182 | } 183 | 184 | #[derive(StructOfArray)] 185 | struct IncrOnDrop { 186 | cell: Rc>, 187 | } 188 | 189 | impl Drop for IncrOnDrop { 190 | fn drop(&mut self) { 191 | self.cell.set(self.cell.get() + 1); 192 | } 193 | } 194 | 195 | #[test] 196 | fn drop_vec() { 197 | let counter = Rc::new(Cell::default()); 198 | let mut vec = IncrOnDropVec::new(); 199 | for _ in 0..5 { 200 | vec.push(IncrOnDrop { 201 | cell: counter.clone(), 202 | }); 203 | } 204 | 205 | assert_eq!(counter.get(), 0); 206 | drop(vec); 207 | assert_eq!(counter.get(), 5); 208 | } 209 | -------------------------------------------------------------------------------- /tests/zip.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::float_cmp)] 2 | 3 | use soa_derive::soa_zip; 4 | mod particles; 5 | use self::particles::{Particle, ParticleVec, ParticleSlice, ParticleSliceMut}; 6 | 7 | #[test] 8 | fn vec() { 9 | let mut particles = ParticleVec::new(); 10 | particles.push(Particle::new(String::from("Na"), 56.0)); 11 | particles.push(Particle::new(String::from("Na"), 56.0)); 12 | particles.push(Particle::new(String::from("Na"), 56.0)); 13 | particles.push(Particle::new(String::from("Na"), 56.0)); 14 | 15 | for name in soa_zip!(&particles, [name]) { 16 | assert_eq!(name, "Na"); 17 | } 18 | 19 | for &mass in soa_zip!(&particles, [mass]) { 20 | assert_eq!(mass, 56.0); 21 | } 22 | 23 | for (name, &mass) in soa_zip!(&particles, [name, mass]) { 24 | assert_eq!(name, "Na"); 25 | assert_eq!(mass, 56.0); 26 | } 27 | 28 | for (_, mass) in soa_zip!(&mut particles, [name, mut mass]) { 29 | *mass = 42.0; 30 | } 31 | 32 | for (name, _) in soa_zip!(&mut particles, [mut name, mut mass]) { 33 | *name = "Fe".into(); 34 | } 35 | 36 | for (name, &mass) in soa_zip!(&particles, [name, mass]) { 37 | assert_eq!(name, "Fe"); 38 | assert_eq!(mass, 42.0); 39 | } 40 | } 41 | 42 | #[test] 43 | fn slice() { 44 | let mut particles = ParticleVec::new(); 45 | particles.push(Particle::new(String::from("Na"), 56.0)); 46 | particles.push(Particle::new(String::from("Na"), 56.0)); 47 | particles.push(Particle::new(String::from("Na"), 56.0)); 48 | particles.push(Particle::new(String::from("Na"), 56.0)); 49 | 50 | let particles = particles.as_slice(); 51 | 52 | for name in soa_zip!(&particles, [name]) { 53 | assert_eq!(name, "Na"); 54 | } 55 | 56 | for &mass in soa_zip!(&particles, [mass]) { 57 | assert_eq!(mass, 56.0); 58 | } 59 | 60 | for (name, &mass) in soa_zip!(&particles, [name, mass]) { 61 | assert_eq!(name, "Na"); 62 | assert_eq!(mass, 56.0); 63 | } 64 | } 65 | 66 | #[test] 67 | fn slice_mut() { 68 | let mut particles = ParticleVec::new(); 69 | particles.push(Particle::new(String::from("Na"), 56.0)); 70 | particles.push(Particle::new(String::from("Na"), 56.0)); 71 | particles.push(Particle::new(String::from("Na"), 56.0)); 72 | particles.push(Particle::new(String::from("Na"), 56.0)); 73 | 74 | let mut particles = particles.as_mut_slice(); 75 | 76 | for name in soa_zip!(&particles, [name]) { 77 | assert_eq!(name, "Na"); 78 | } 79 | 80 | for &mass in soa_zip!(&particles, [mass]) { 81 | assert_eq!(mass, 56.0); 82 | } 83 | 84 | for (name, &mass) in soa_zip!(&particles, [name, mass]) { 85 | assert_eq!(name, "Na"); 86 | assert_eq!(mass, 56.0); 87 | } 88 | 89 | for (_, mass) in soa_zip!(&mut particles, [name, mut mass]) { 90 | *mass = 42.0; 91 | } 92 | 93 | for (name, _) in soa_zip!(&mut particles, [mut name, mut mass]) { 94 | *name = "Fe".into(); 95 | } 96 | 97 | for (name, &mass) in soa_zip!(&particles, [name, mass]) { 98 | assert_eq!(name, "Fe"); 99 | assert_eq!(mass, 42.0); 100 | } 101 | } 102 | 103 | #[test] 104 | fn external() { 105 | let mut particles = ParticleVec::new(); 106 | particles.push(Particle::new(String::from("Na"), 56.0)); 107 | particles.push(Particle::new(String::from("Na"), 56.0)); 108 | particles.push(Particle::new(String::from("Na"), 56.0)); 109 | particles.push(Particle::new(String::from("Na"), 56.0)); 110 | 111 | let bar = vec![42.0; 4]; 112 | for (mass, &bar) in soa_zip!(&mut particles, [mut mass], &bar) { 113 | *mass = bar; 114 | } 115 | 116 | for &mass in &particles.mass { 117 | assert_eq!(mass, 42.0); 118 | } 119 | } 120 | 121 | struct Wrapper { 122 | particles: ParticleVec 123 | } 124 | 125 | impl Wrapper { 126 | fn new(particles: ParticleVec) -> Wrapper { 127 | Wrapper { 128 | particles, 129 | } 130 | } 131 | 132 | fn particles(&self) -> ParticleSlice { 133 | self.particles.as_slice() 134 | } 135 | 136 | fn particles_mut(&mut self) -> ParticleSliceMut { 137 | self.particles.as_mut_slice() 138 | } 139 | } 140 | 141 | #[test] 142 | fn access_particles_through_function() { 143 | let mut particles = ParticleVec::new(); 144 | particles.push(Particle::new(String::from("Na"), 56.0)); 145 | particles.push(Particle::new(String::from("Na"), 56.0)); 146 | particles.push(Particle::new(String::from("Na"), 56.0)); 147 | particles.push(Particle::new(String::from("Na"), 56.0)); 148 | 149 | let mut wrapper = Wrapper::new(particles); 150 | 151 | for name in soa_zip!(wrapper.particles(), [name]) { 152 | assert_eq!(name, "Na"); 153 | } 154 | 155 | for &mass in soa_zip!(wrapper.particles(), [mass]) { 156 | assert_eq!(mass, 56.0); 157 | } 158 | 159 | for (name, &mass) in soa_zip!(wrapper.particles(), [name, mass]) { 160 | assert_eq!(name, "Na"); 161 | assert_eq!(mass, 56.0); 162 | } 163 | 164 | for (_, mass) in soa_zip!(wrapper.particles_mut(), [name, mut mass]) { 165 | *mass = 42.0; 166 | } 167 | 168 | for (name, _) in soa_zip!(wrapper.particles_mut(), [mut name, mut mass]) { 169 | *name = "Fe".into(); 170 | } 171 | 172 | for (name, &mass) in soa_zip!(wrapper.particles(), [name, mass]) { 173 | assert_eq!(name, "Fe"); 174 | assert_eq!(mass, 42.0); 175 | } 176 | } 177 | --------------------------------------------------------------------------------