├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUING.md ├── Cargo.toml ├── README.md ├── epserde-derive ├── Cargo.toml ├── LICENSE-Apache-2.0 ├── LICENSE-LGPL-2.1-or-later ├── README.md └── src │ └── lib.rs └── epserde ├── Cargo.toml ├── LICENSE-Apache-2.0 ├── LICENSE-LGPL-2.1-or-later ├── README.md ├── build.rs ├── examples ├── array.rs ├── array_inner.rs ├── enum.rs ├── internal_param_deep.rs ├── internal_param_zero.rs ├── iter.rs ├── nested.rs ├── nested_file.rs ├── nested_schema.rs ├── newtype_zero_copy.rs ├── onetuple_zero_copy.rs ├── opt_vec.rs ├── repr_c.rs ├── slice.rs ├── vec_str.rs └── vec_struct.rs ├── src ├── deser │ ├── helpers.rs │ ├── mem_case.rs │ ├── mod.rs │ ├── read.rs │ ├── reader_with_pos.rs │ └── slice_with_pos.rs ├── impls │ ├── array.rs │ ├── boxed_slice.rs │ ├── iter.rs │ ├── mod.rs │ ├── prim.rs │ ├── slice.rs │ ├── stdlib.rs │ ├── string.rs │ ├── tuple.rs │ └── vec.rs ├── lib.rs ├── ser │ ├── helpers.rs │ ├── mod.rs │ ├── write.rs │ └── write_with_names.rs ├── traits │ ├── copy_type.rs │ ├── mod.rs │ └── type_info.rs └── utils │ ├── aligned_cursor.rs │ └── mod.rs └── tests ├── test_bad_deser.rs ├── test_bad_ser.rs ├── test_boxed_slice.rs ├── test_generics.rs ├── test_max_size_of.rs ├── test_memcase.rs ├── test_phantom.rs ├── test_prim.rs ├── test_slice.rs ├── test_std.rs ├── test_stdlib.rs ├── test_tuples.rs ├── test_type_hash_val.rs ├── test_types.rs └── test_zero.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CARGO_TERM_COLOR: always 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Check formatting 14 | run: cargo fmt -- --check 15 | - name: Build 16 | run: cargo build --verbose 17 | - name: Run tests 18 | run: cargo test --verbose 19 | - name: Run examples 20 | working-directory: ./epserde 21 | run: for example in examples/*.rs ; do cargo run --example "$(basename "${example%.rs}")" ; done 22 | - name: Run clippy 23 | run: cargo clippy #-- -Dclippy::all -Dclippy::cargo 24 | - name: Switch to nightly toolchain 25 | run: rustup default nightly 26 | - name: Test with miri 27 | run: MIRIFLAGS="-Zmiri-disable-isolation" cargo test 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /epserde/Cargo.lock 3 | /epserde-derive/Cargo.lock 4 | .vscode 5 | serialized 6 | lcov.info 7 | test.bin 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [0.9.0] - 4 | 5 | ### Changed 6 | 7 | * `DeserializeInner` is now an unsafe trait, and all deserialization helper 8 | methods handling zero-copy types are also unsafe. This change is necessary 9 | because such methods can deserialize uninhabited types. 10 | 11 | ### Fixed 12 | 13 | * ε-copy deserializing slices of zero-width zero-copy types now works. 14 | 15 | ## [0.8.0] - 2025-03-03 16 | 17 | ### New 18 | 19 | * The ReprHash (now AlignHash) of arrays was wrong and could have led to data 20 | corruption. As a result, some serialized file might return an alignment 21 | error. 22 | 23 | * The implementation for tuples was broken because it assumed that the memory 24 | layout would have been the same of the source layout. We now just support 25 | tuples of zero-copy identical types up to size 12, and `TypeHash` for generic 26 | tuples up to size 12 to help with the idiom `PhantomData<(T, U)>`. For the 27 | other cases, it is necessary to create a `repr(C)` tuple newtype. Note that up 28 | to ε-serde 0.7.0 we provided an erroneous implementation for mixed zero-copy 29 | types. If you serialized a structure using such a tuple, it will be no longer 30 | deserializable. 31 | 32 | * You can now serialize exact-size iterators that will be deserialized as 33 | vectors, making it possible to save incrementally structures larger 34 | than the available memory. 35 | 36 | ## [0.7.0] - 2025-02-18 37 | 38 | ### New 39 | 40 | * Now `SerializeInner` inner has an associated type `SerType` that is used to 41 | write the file header. This is done so `Data<&[u32]>` can be conveniently 42 | serialized as if it were `Data>`. There is no change in the file 43 | format. 44 | 45 | ## [0.6.3] - 2025-02-07 46 | 47 | ### New 48 | 49 | * Memory-mapping can be disabled using the `mmap` default feature. 50 | 51 | ## [0.6.2] - 2025-02-07 52 | 53 | ### Improved 54 | 55 | * Added missing implementation of `TypeHash`, `ReprHash`, `MaxSizeOf`, 56 | `SerializeInner`, `DeserializeInner` for `Range`, `RangeFrom`, `RangeFull`, 57 | `RangeInclusive`, `RangeTo`, `RangeToInclusive`, `Bound`, `ControlFlow`. 58 | 59 | ### Fixed 60 | 61 | * The return type of `Deserialize::load_full` is how an `anyhow::Result`, 62 | analogously to the other `load` functions. 63 | 64 | ## [0.6.1] - 2024-06-03 65 | 66 | ### Fixed 67 | 68 | * Added missing implementation of MaxSizeOf for PhantomData. 69 | 70 | ## [0.6.0] - 2024-06-03 71 | 72 | ### Changed 73 | 74 | * Updated MemDbg to 0.2.1. 75 | 76 | ### Fixed 77 | 78 | * Added const generic parameters values and names to type hash. Note that 79 | this change will invalidate type hashes for structures with generic 80 | constants. 81 | 82 | * Fixed handling of zero-sized zero-copy structs eps_deserialization. 83 | 84 | ## [0.5.1] - 2024-03-18 85 | 86 | ### Changed 87 | 88 | * Added MemDbg, MemSize, and Debug to most structs. 89 | 90 | ### Fixed 91 | 92 | * Renamed the lifetime `'a` in derives to `deserialize_eps_inner_lifetime` 93 | to avoid clashes. 94 | 95 | ## [0.5.0] - 2024-03-18 96 | 97 | ### Changed 98 | 99 | * `util` -> `utils` to follow new guidelines. 100 | -------------------------------------------------------------------------------- /CONTRIBUING.md: -------------------------------------------------------------------------------- 1 | If you want to contribute to this project, please follow the guidelines at 2 | 3 | https://github.com/vigna/rust-dev-guidelines/blob/main/README.md 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "epserde-derive", 6 | "epserde", 7 | ] 8 | -------------------------------------------------------------------------------- /epserde-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "epserde-derive" 3 | authors = [ 4 | "Tommaso Fontana ", 5 | "Sebastiano Vigna ", 6 | ] 7 | description = "Procedural macros for ε-serde" 8 | version = "0.8.0" 9 | edition = "2021" 10 | repository = "https://github.com/vigna/epserde-rs/" 11 | license = "Apache-2.0 OR LGPL-2.1-or-later" 12 | readme = "README.md" 13 | keywords = ["serialization", "zero-copy", "mmap"] 14 | 15 | [lib] 16 | proc-macro = true 17 | 18 | [dependencies] 19 | quote = "1" 20 | proc-macro2 = "1.0" 21 | syn = { version = "2.0", features = ["extra-traits"] } 22 | -------------------------------------------------------------------------------- /epserde-derive/LICENSE-Apache-2.0: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /epserde-derive/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /epserde/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "epserde" 3 | authors = [ 4 | "Tommaso Fontana ", 5 | "Sebastiano Vigna ", 6 | ] 7 | description = "ε-serde is an ε-copy (i.e., almost zero-copy) serialization/deserialization framework" 8 | version = "0.8.0" 9 | edition = "2021" 10 | repository = "https://github.com/vigna/epserde-rs/" 11 | license = "Apache-2.0 OR LGPL-2.1-or-later" 12 | readme = "README.md" 13 | keywords = ["serialization", "zero-copy", "mmap"] 14 | 15 | [dependencies] 16 | epserde-derive = { path = "../epserde-derive", optional = true } 17 | mmap-rs = { version = "0.6.0", optional = true } 18 | bitflags = { version = "2.4.2", default-features = false } 19 | xxhash-rust = { version = "0.8.8", default-features = false, features = [ 20 | "xxh3", 21 | ] } 22 | #epserde-derive = { version = "=0.8.0", optional = true } 23 | anyhow = "1.0.79" 24 | thiserror = "2.0.11" 25 | sealed = "0.6.0" 26 | maligned = "0.2.1" 27 | common_traits = "0.11.2" 28 | mem_dbg = { version = "0.3.0", features = [ 29 | "maligned", 30 | "derive", 31 | ], default-features = false } 32 | 33 | [features] 34 | default = ["std", "mmap", "derive"] 35 | derive = ["epserde-derive"] 36 | std = ["alloc", "mem_dbg/std"] 37 | alloc = [] 38 | mmap = ["mmap-rs", "mem_dbg/mmap-rs"] 39 | -------------------------------------------------------------------------------- /epserde/LICENSE-Apache-2.0: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /epserde/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /epserde/build.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Write; 2 | 3 | fn main() { 4 | let readme_file = env!("CARGO_PKG_README"); 5 | let mut readme = String::from_utf8(std::fs::read(readme_file).unwrap()).unwrap(); 6 | readme.write_str("IT WORKS").unwrap(); 7 | dbg!(&readme); 8 | println!("cargo:rerun-if-changed={readme_file}"); 9 | println!("cargo:rustc-env=README={readme}"); 10 | } 11 | -------------------------------------------------------------------------------- /epserde/examples/array.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | use epserde::prelude::*; 9 | use maligned::A16; 10 | 11 | /// Example of zero-copy deserialization of an array. 12 | fn main() { 13 | // Create a vector to serialize 14 | 15 | let a = [1_usize; 100]; 16 | let mut cursor = >::new(); // Serialize 17 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 18 | 19 | // Do a full-copy deserialization 20 | cursor.set_position(0); 21 | let full = <[usize; 100]>::deserialize_full(&mut cursor).unwrap(); 22 | println!( 23 | "Full-copy deserialization type: {}", 24 | std::any::type_name::<[usize; 100]>(), 25 | ); 26 | println!("Value: {:x?}", full); 27 | 28 | println!(); 29 | 30 | // Do an ε-copy deserialization (which will be a zero-copy deserialization) 31 | let eps = <[usize; 100]>::deserialize_eps(cursor.as_bytes()).unwrap(); 32 | println!( 33 | "ε-copy deserialization type: {}", 34 | std::any::type_name::<<[usize; 100] as DeserializeInner>::DeserType<'_>>(), 35 | ); 36 | println!("Value: {:x?}", eps); 37 | } 38 | -------------------------------------------------------------------------------- /epserde/examples/array_inner.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of zero-copy deserialization of a zero-copy struct. 9 | use epserde::prelude::*; 10 | use maligned::A16; 11 | 12 | #[derive(Epserde, Copy, Clone, Debug)] 13 | #[repr(C)] 14 | #[zero_copy] 15 | struct Data { 16 | a: [usize; 100], 17 | } 18 | 19 | fn main() { 20 | let a = Data { a: [1_usize; 100] }; 21 | let mut cursor = >::new(); 22 | 23 | // Serialize 24 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 25 | 26 | // Do a full-copy deserialization 27 | cursor.set_position(0); 28 | let full = Data::deserialize_full(&mut cursor).unwrap(); 29 | println!( 30 | "Full-copy deserialization type: {}", 31 | std::any::type_name::(), 32 | ); 33 | println!("Value: {:x?}", full); 34 | 35 | println!(); 36 | 37 | // Do an ε-copy deserialization (which will be a zero-copy deserialization) 38 | let eps = Data::deserialize_eps(cursor.as_bytes()).unwrap(); 39 | println!( 40 | "ε-copy deserialization type: {}", 41 | std::any::type_name::<::DeserType<'_>>(), 42 | ); 43 | println!("Value: {:x?}", eps); 44 | } 45 | -------------------------------------------------------------------------------- /epserde/examples/enum.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of an enum with variants some of which depend 9 | /// on a parameter, and some dont. See in particular the failed 10 | /// check of type hash. 11 | use epserde::prelude::*; 12 | use maligned::A16; 13 | 14 | #[derive(Epserde, Debug, Clone, Copy)] 15 | enum Data> { 16 | A, // Unit type 17 | B { a: usize, b: usize }, // Struct variant with two fields 18 | C(T), // Tuple variant with one parametric field 19 | } 20 | 21 | fn main() { 22 | // Note that we need an explicity type annotation here, 23 | // as the type of the enum is not fully determined by the 24 | // value--we need to know the type of the parameter, which 25 | // is assumed to be `Vec` by default. 26 | let a: Data = Data::A; 27 | let mut cursor = >::new(); 28 | // Serialize 29 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 30 | 31 | // Do a full-copy deserialization 32 | cursor.set_position(0); 33 | let full = ::deserialize_full(&mut cursor).unwrap(); 34 | println!( 35 | "Full-copy deserialization type: {}", 36 | std::any::type_name::(), 37 | ); 38 | println!("Value: {:x?}", full); 39 | 40 | println!(); 41 | 42 | // Do an ε-copy deserialization 43 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 44 | println!( 45 | "ε-copy deserialization type: {}", 46 | std::any::type_name::<::DeserType<'_>>(), 47 | ); 48 | println!("Value: {:x?}", eps); 49 | 50 | // Now we give to the parameter a type different from the 51 | // default one. 52 | let a: Data> = Data::A; 53 | let mut cursor = >::new(); 54 | // Serialize 55 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 56 | 57 | println!(); 58 | 59 | println!("Deserializing with a different parameter type..."); 60 | // When we try to deserialize without specifying again 61 | // the type, we get an error even if we just serialized 62 | // Data::A because the default value of the parameter 63 | // is different from the one we used. 64 | cursor.set_position(0); 65 | println!( 66 | "Error in full-copy deserialization: {}", 67 | ::deserialize_full(&mut cursor).err().unwrap() 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /epserde/examples/internal_param_deep.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of an internal parameter of a deep-copy structure, which 9 | /// is left untouched, but needs some decoration to be used. 10 | use epserde::prelude::*; 11 | use maligned::A16; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct Data { 15 | a: Vec, 16 | } 17 | 18 | fn main() { 19 | // Create a new value to serialize 20 | let data = Data { 21 | a: vec![vec![0x89; 6]; 9], 22 | }; 23 | let mut cursor = >::new(); 24 | // Serialize 25 | let schema = data.serialize_with_schema(&mut cursor).unwrap(); 26 | 27 | // Show the schema 28 | println!("{}", schema.debug(cursor.as_bytes())); 29 | 30 | // Do a full-copy deserialization 31 | cursor.set_position(0); 32 | let full = >>::deserialize_full(&mut cursor).unwrap(); 33 | println!( 34 | "Full-copy deserialization type: {}", 35 | std::any::type_name::>>(), 36 | ); 37 | println!("Value: {:x?}", full); 38 | 39 | println!(); 40 | 41 | // Do an ε-copy deserialization 42 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 43 | println!( 44 | "ε-copy deserialization type: {}", 45 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 46 | ); 47 | println!("Value: {:x?}", eps); 48 | } 49 | -------------------------------------------------------------------------------- /epserde/examples/internal_param_zero.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | use epserde::prelude::*; 9 | use maligned::A16; 10 | 11 | /// Example of an internal parameter of a zero-copy structure, 12 | /// which is left untouched, but needs some decoration to be used. 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct Data { 15 | a: Vec, 16 | } 17 | 18 | fn main() { 19 | // Create a new value to serialize 20 | let data = Data { 21 | a: vec![0, 1, 2, 3], 22 | }; 23 | let mut cursor = >::new(); 24 | // Serialize 25 | let schema = data.serialize_with_schema(&mut cursor).unwrap(); 26 | 27 | // Show the schema 28 | println!("{}", schema.debug(cursor.as_bytes())); 29 | 30 | // Do a full-copy deserialization 31 | cursor.set_position(0); 32 | let full = >::deserialize_full(&mut cursor).unwrap(); 33 | println!( 34 | "Full-copy deserialization type: {}", 35 | std::any::type_name::>(), 36 | ); 37 | println!("Value: {:x?}", full); 38 | 39 | println!(); 40 | 41 | // Do an ε-copy deserialization 42 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 43 | println!( 44 | "ε-copy deserialization type: {}", 45 | std::any::type_name::< as DeserializeInner>::DeserType<'_>>(), 46 | ); 47 | println!("Value: {:x?}", eps); 48 | } 49 | -------------------------------------------------------------------------------- /epserde/examples/iter.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 Sebastiano Vigna 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use std::slice::Iter; 8 | 9 | /// Example showcasing the convenience serialization of iterators. 10 | use epserde::{impls::iter::SerIter, prelude::*}; 11 | use maligned::A16; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct Data { 15 | a: A, 16 | } 17 | 18 | fn main() { 19 | let a = vec![0, 1, 2, 3]; 20 | // Turn it into an interator 21 | let i: Iter<'_, i32> = a.iter(); 22 | 23 | println!("Original type: {}", std::any::type_name::>()); 24 | 25 | let mut cursor = >::new(); 26 | // Serialize the iterator 27 | let _bytes_written = SerIter::from(i).serialize(&mut cursor).unwrap(); 28 | 29 | // Do a full-copy deserialization as a vector 30 | cursor.set_position(0); 31 | let full = >::deserialize_full(&mut cursor).unwrap(); 32 | println!( 33 | "Full-copy deserialization type: {}", 34 | std::any::type_name::>(), 35 | ); 36 | println!("Value: {:x?}", full); 37 | 38 | println!(); 39 | 40 | // Do an ε-copy deserialization as a slice 41 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 42 | println!( 43 | "ε-copy deserialization type: {}", 44 | std::any::type_name::< as DeserializeInner>::DeserType<'_>>(), 45 | ); 46 | println!("Value: {:x?}", eps); 47 | 48 | println!(); 49 | println!(); 50 | 51 | // Let's do with a structure 52 | let i: Iter<'_, i32> = a.iter(); 53 | let d: Data>> = Data { 54 | a: SerIter::from(i), 55 | }; 56 | 57 | println!( 58 | "Original type: {}", 59 | std::any::type_name::>>>>() 60 | ); 61 | 62 | // Serialize the structure 63 | cursor.set_position(0); 64 | let _bytes_written = d.serialize(&mut cursor).unwrap(); 65 | 66 | // Do a full-copy deserialization 67 | cursor.set_position(0); 68 | let full = >>::deserialize_full(&mut cursor).unwrap(); 69 | println!( 70 | "Full-copy deserialization type: {}", 71 | std::any::type_name::>>(), 72 | ); 73 | println!("Value: {:x?}", full); 74 | 75 | println!(); 76 | 77 | // Do an ε-copy deserialization 78 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 79 | println!( 80 | "ε-copy deserialization type: {}", 81 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 82 | ); 83 | println!("Value: {:x?}", eps); 84 | } 85 | -------------------------------------------------------------------------------- /epserde/examples/nested.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of a nested struct in which one of the fields 9 | /// of the inner struct is recursively ε-copied, as its 10 | /// type is a parameter. 11 | use epserde::prelude::*; 12 | use maligned::A16; 13 | 14 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 15 | struct StructParam { 16 | a: A, 17 | b: B, 18 | test: isize, 19 | } 20 | 21 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 22 | struct Data { 23 | a: A, 24 | /// This is a field whose type is not a parameter, 25 | /// so it will not be ε-copied, but rather fully copied. 26 | b: Vec, 27 | } 28 | 29 | type Struct = StructParam, Data>>; 30 | 31 | fn main() { 32 | // Create a new value to serialize 33 | let person = Struct { 34 | a: vec![0x89; 6], 35 | b: Data { 36 | a: vec![0x42; 7], 37 | b: vec![0xbadf00d; 2], 38 | }, 39 | test: -0xbadf00d, 40 | }; 41 | let mut cursor = >::new(); 42 | // Serialize 43 | let _bytes_written = person.serialize(&mut cursor).unwrap(); 44 | 45 | // Do a full-copy deserialization 46 | cursor.set_position(0); 47 | let full = Struct::deserialize_full(&mut cursor).unwrap(); 48 | println!( 49 | "Full-copy deserialization type: {}", 50 | std::any::type_name::(), 51 | ); 52 | println!("Value: {:x?}", full); 53 | assert_eq!(person, full); 54 | 55 | println!(); 56 | 57 | // Do an ε-copy deserialization 58 | let eps = Struct::deserialize_eps(cursor.as_bytes()).unwrap(); 59 | println!( 60 | "ε-copy deserialization type: {}", 61 | std::any::type_name::<::DeserType<'_>>(), 62 | ); 63 | println!("Value: {:x?}", eps); 64 | assert_eq!(person.a, eps.a); 65 | assert_eq!(person.b.a, eps.b.a); 66 | assert_eq!(person.b.b, eps.b.b); 67 | assert_eq!(person.test, eps.test); 68 | } 69 | -------------------------------------------------------------------------------- /epserde/examples/nested_file.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of a nested struct in which one of the fields 9 | /// of the inner struct is recursively ε-copied, as its 10 | /// type is a parameter. We also serialize on file. 11 | use epserde::prelude::*; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct StructParam { 15 | a: A, 16 | b: B, 17 | test: isize, 18 | } 19 | 20 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 21 | struct Data { 22 | a: A, 23 | /// This is a field whose type is not a parameter, 24 | /// so it will not be ε-copied, but rather fully copied. 25 | b: Vec, 26 | } 27 | 28 | type Struct = StructParam, Data>>; 29 | 30 | fn main() { 31 | // Create a new value to serialize 32 | let s = Struct { 33 | a: vec![0x89; 6], 34 | b: Data { 35 | a: vec![0x42; 7], 36 | b: vec![0xbadf00d; 2], 37 | }, 38 | test: -0xbadf00d, 39 | }; 40 | // Create an aligned vector to serialize into so we can do an ε-copy 41 | // deserialization safely 42 | let mut file = std::fs::File::create("test.bin").unwrap(); 43 | // Serialize 44 | let _bytes_written = s.serialize(&mut file).unwrap(); 45 | 46 | drop(file); 47 | 48 | let mut file = std::fs::File::open("test.bin").unwrap(); 49 | 50 | // Do a full-copy deserialization 51 | 52 | let full = Struct::deserialize_full(&mut file).unwrap(); 53 | println!( 54 | "Full-copy deserialization type: {}", 55 | std::any::type_name::(), 56 | ); 57 | println!("Value: {:x?}", full); 58 | assert_eq!(s, full); 59 | 60 | println!(); 61 | 62 | // Do an ε-copy deserialization 63 | let file = std::fs::read("test.bin").unwrap(); 64 | let eps = Struct::deserialize_eps(&file).unwrap(); 65 | println!( 66 | "ε-copy deserialization type: {}", 67 | std::any::type_name::<::DeserType<'_>>(), 68 | ); 69 | println!("Value: {:x?}", eps); 70 | assert_eq!(s.a, eps.a); 71 | assert_eq!(s.b.a, eps.b.a); 72 | assert_eq!(s.b.b, eps.b.b); 73 | assert_eq!(s.test, eps.test); 74 | } 75 | -------------------------------------------------------------------------------- /epserde/examples/nested_schema.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example of a nested struct in which one of the fields 9 | /// of the inner struct is recursively ε-copied, as its 10 | /// type is a parameter. We also generate a schema. 11 | use epserde::prelude::*; 12 | use maligned::A16; 13 | 14 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 15 | struct StructParam { 16 | a: A, 17 | b: B, 18 | test: isize, 19 | } 20 | 21 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 22 | struct Data { 23 | a: A, 24 | /// This is a field whose type is not a parameter, 25 | /// so it will not be ε-copied, but rather fully copied. 26 | b: Vec, 27 | } 28 | 29 | type Struct = StructParam, Data>>; 30 | 31 | fn main() { 32 | // create a new value to serialize 33 | let person = Struct { 34 | a: vec![0x89; 6], 35 | b: Data { 36 | a: vec![0x42; 7], 37 | b: vec![0xbadf00d; 2], 38 | }, 39 | test: -0xbadf00d, 40 | }; 41 | let mut cursor = >::new(); 42 | // Serialize 43 | let schema = person.serialize_with_schema(&mut cursor).unwrap(); 44 | 45 | // Show the schema 46 | println!("{}", schema.debug(cursor.as_bytes())); 47 | 48 | // Do a full-copy deserialization 49 | cursor.set_position(0); 50 | let full = Struct::deserialize_full(&mut cursor).unwrap(); 51 | println!( 52 | "Full-copy deserialization type: {}", 53 | std::any::type_name::(), 54 | ); 55 | println!("Value: {:x?}", full); 56 | assert_eq!(person, full); 57 | 58 | println!(); 59 | 60 | // Do an ε-copy deserialization 61 | let eps = Struct::deserialize_eps(cursor.as_bytes()).unwrap(); 62 | println!( 63 | "ε-copy deserialization type: {}", 64 | std::any::type_name::<::DeserType<'_>>(), 65 | ); 66 | println!("Value: {:x?}", eps); 67 | assert_eq!(person.a, eps.a); 68 | assert_eq!(person.b.a, eps.b.a); 69 | assert_eq!(person.b.b, eps.b.b); 70 | assert_eq!(person.test, eps.test); 71 | } 72 | -------------------------------------------------------------------------------- /epserde/examples/newtype_zero_copy.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showing how the standard behavior of ε-serde on primitive 9 | /// types (returning a value rather than a reference) is somewhat custom: 10 | /// if we derive code for a zero-copy newtype containing just a `usize`, 11 | /// the associated deserialization type is a reference. 12 | use epserde::prelude::*; 13 | use maligned::A16; 14 | 15 | #[derive(Epserde, Copy, Debug, PartialEq, Eq, Default, Clone)] 16 | #[repr(C)] 17 | #[zero_copy] 18 | struct USize(usize); 19 | 20 | fn main() { 21 | let x = USize(0); 22 | let mut cursor = >::new(); 23 | // Serialize 24 | let _bytes_written = x.serialize(&mut cursor).unwrap(); 25 | 26 | // Do a full-copy deserialization 27 | cursor.set_position(0); 28 | let full = ::deserialize_full(&mut cursor).unwrap(); 29 | println!( 30 | "Full-copy deserialization type: {}", 31 | std::any::type_name::(), 32 | ); 33 | println!("Value: {:x?}", full); 34 | assert_eq!(x, full); 35 | 36 | println!(); 37 | 38 | // Do an ε-copy deserialization (which will be zero-copy deserialization) 39 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 40 | println!( 41 | "ε-copy deserialization type: {}", 42 | std::any::type_name::<::DeserType<'_>>(), 43 | ); 44 | println!("Value: {:x?}", eps); 45 | assert_eq!(x, *eps); 46 | } 47 | -------------------------------------------------------------------------------- /epserde/examples/onetuple_zero_copy.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showing how the standard behavior of ε-serde on primitive 9 | /// types (returning a value rather than a reference) is somewhat custom: 10 | /// the deserialization type associated to a one-element tuple containing 11 | /// just a `usize` is a reference. 12 | use epserde::prelude::*; 13 | use maligned::A16; 14 | 15 | fn main() { 16 | let x = (0_usize,); 17 | let mut cursor = >::new(); 18 | // Serialize 19 | let _bytes_written = x.serialize(&mut cursor).unwrap(); 20 | 21 | // Do a full-copy deserialization 22 | cursor.set_position(0); 23 | let full = <(usize,)>::deserialize_full(&mut cursor).unwrap(); 24 | println!( 25 | "Full-copy deserialization type: {}", 26 | std::any::type_name::<(usize,)>(), 27 | ); 28 | println!("Value: {:x?}", full); 29 | assert_eq!(x, full); 30 | 31 | println!(); 32 | 33 | // Do an ε-copy deserialization (which will be zero-copy deserialization) 34 | let eps = <(usize,)>::deserialize_eps(cursor.as_bytes()).unwrap(); 35 | println!( 36 | "ε-copy deserialization type: {}", 37 | std::any::type_name::<<(usize,) as DeserializeInner>::DeserType<'_>>(), 38 | ); 39 | println!("Value: {:x?}", eps); 40 | assert_eq!(x, *eps); 41 | } 42 | -------------------------------------------------------------------------------- /epserde/examples/opt_vec.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showcasing serialization of an `Option``. 9 | use epserde::prelude::*; 10 | use maligned::A16; 11 | 12 | fn main() { 13 | let a = Some(vec![0, 1, 2, 3]); 14 | let mut cursor = >::new(); 15 | // Serialize 16 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 17 | 18 | // Do a full-copy deserialization 19 | cursor.set_position(0); 20 | let full = >>::deserialize_full(&mut cursor).unwrap(); 21 | println!( 22 | "Full-copy deserialization type: {}", 23 | std::any::type_name::>>(), 24 | ); 25 | println!("Value: {:x?}", full); 26 | 27 | println!(); 28 | 29 | // Do an ε-copy deserialization 30 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 31 | println!( 32 | "ε-copy deserialization type: {}", 33 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 34 | ); 35 | println!("Value: {:x?}", eps); 36 | 37 | let mut cursor = >::new(); 38 | println!("\n"); 39 | 40 | // Serialize 41 | let a: Option> = None; 42 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 43 | 44 | // Do a full-copy deserialization 45 | cursor.set_position(0); 46 | let full = >>::deserialize_full(&mut cursor).unwrap(); 47 | println!( 48 | "Full-copy deserialization type: {}", 49 | std::any::type_name::>>(), 50 | ); 51 | println!("Value: {:x?}", full); 52 | 53 | println!(); 54 | 55 | // Do an ε-copy deserialization 56 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 57 | println!( 58 | "ε-copy deserialization type: {}", 59 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 60 | ); 61 | println!("Value: {:x?}", eps); 62 | } 63 | -------------------------------------------------------------------------------- /epserde/examples/repr_c.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showing that vectors of a zero-copy type are ε-copy 9 | /// deserialized to a reference. 10 | use epserde::prelude::*; 11 | use maligned::A16; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct Object { 15 | a: A, 16 | test: isize, 17 | } 18 | 19 | #[repr(C)] 20 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone, Copy)] 21 | // We want to use zero-copy deserialization on Point, 22 | // and thus ε-copy deserialization on Vec, etc. 23 | #[zero_copy] 24 | struct Point { 25 | x: usize, 26 | y: usize, 27 | } 28 | 29 | fn main() { 30 | let point: Object> = Object { 31 | a: vec![Point { x: 2, y: 1 }; 6], 32 | test: -0xbadf00d, 33 | }; 34 | let mut cursor = >::new(); 35 | // Serialize 36 | let _bytes_written = point.serialize(&mut cursor).unwrap(); 37 | 38 | // Do a full-copy deserialization 39 | cursor.set_position(0); 40 | let full = >>::deserialize_full(&mut cursor).unwrap(); 41 | println!( 42 | "Full-copy deserialization type: {}", 43 | std::any::type_name::>>(), 44 | ); 45 | println!("Value: {:x?}", full); 46 | assert_eq!(point, full); 47 | 48 | println!(); 49 | 50 | // Do an ε-copy deserialization 51 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 52 | println!( 53 | "ε-copy deserialization type: {}", 54 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 55 | ); 56 | println!("Value: {:x?}", eps); 57 | assert_eq!(point.a, eps.a); 58 | assert_eq!(point.test, eps.test); 59 | } 60 | -------------------------------------------------------------------------------- /epserde/examples/slice.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showcasing the convenience serialization of references to slices. 9 | use epserde::prelude::*; 10 | use maligned::A16; 11 | 12 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 13 | struct Data { 14 | a: A, 15 | } 16 | 17 | fn main() { 18 | let a = vec![0, 1, 2, 3]; 19 | // Turn it into a slice 20 | let a: &[i32] = a.as_ref(); 21 | 22 | println!("Original type: {}", std::any::type_name::<&[i32]>()); 23 | 24 | let mut cursor = >::new(); 25 | // Serialize the slice 26 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 27 | 28 | // Do a full-copy deserialization as a vector 29 | cursor.set_position(0); 30 | let full = >::deserialize_full(&mut cursor).unwrap(); 31 | println!( 32 | "Full-copy deserialization type: {}", 33 | std::any::type_name::>(), 34 | ); 35 | println!("Value: {:x?}", full); 36 | 37 | println!(); 38 | 39 | // Do an ε-copy deserialization as, again, a slice 40 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 41 | println!( 42 | "ε-copy deserialization type: {}", 43 | std::any::type_name::< as DeserializeInner>::DeserType<'_>>(), 44 | ); 45 | println!("Value: {:x?}", eps); 46 | 47 | println!(); 48 | println!(); 49 | 50 | // Let's do with a structure 51 | let d = Data { a }; 52 | 53 | println!("Original type: {}", std::any::type_name::>()); 54 | 55 | // Serialize the structure 56 | cursor.set_position(0); 57 | let _bytes_written = d.serialize(&mut cursor).unwrap(); 58 | 59 | // Do a full-copy deserialization 60 | cursor.set_position(0); 61 | let full = >>::deserialize_full(&mut cursor).unwrap(); 62 | println!( 63 | "Full-copy deserialization type: {}", 64 | std::any::type_name::>>(), 65 | ); 66 | println!("Value: {:x?}", full); 67 | 68 | println!(); 69 | 70 | // Do an ε-copy deserialization 71 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 72 | println!( 73 | "ε-copy deserialization type: {}", 74 | std::any::type_name::<> as DeserializeInner>::DeserType<'_>>(), 75 | ); 76 | println!("Value: {:x?}", eps); 77 | } 78 | -------------------------------------------------------------------------------- /epserde/examples/vec_str.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showing that ε-copy deserialization can be used with 9 | /// a `Vec`, giving back a `Vec<&str>`. 10 | use epserde::prelude::*; 11 | use maligned::A16; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 14 | struct Data { 15 | a: A, 16 | } 17 | 18 | type StringData = Data>; 19 | 20 | fn main() { 21 | let data = StringData { 22 | a: vec!["A".to_owned(), "B".to_owned(), "C".to_owned()], 23 | }; 24 | let mut cursor = >::new(); 25 | // Serialize 26 | let _bytes_written = data.serialize(&mut cursor).unwrap(); 27 | 28 | // Do a full-copy deserialization 29 | cursor.set_position(0); 30 | let full = StringData::deserialize_full(&mut cursor).unwrap(); 31 | println!( 32 | "Full-copy deserialization type: {}", 33 | std::any::type_name::(), 34 | ); 35 | println!("Value: {:x?}", full); 36 | 37 | println!(); 38 | 39 | // Do an ε-copy deserialization 40 | let eps = StringData::deserialize_eps(cursor.as_bytes()).unwrap(); 41 | println!( 42 | "ε-copy deserialization type: {}", 43 | std::any::type_name::<::DeserType<'_>>(), 44 | ); 45 | println!("Value: {:x?}", eps); 46 | } 47 | -------------------------------------------------------------------------------- /epserde/examples/vec_struct.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /// Example showing that vectors of zero-copy types are 9 | /// ε-copy deserialized as references to slices. 10 | use epserde::prelude::*; 11 | use maligned::A16; 12 | 13 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone, Copy)] 14 | #[repr(C)] 15 | #[zero_copy] 16 | struct Data { 17 | a: usize, 18 | } 19 | 20 | fn main() { 21 | let a = vec![Data { a: 5 }, Data { a: 6 }]; 22 | let mut cursor = >::new(); 23 | // Serialize 24 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 25 | 26 | // Do a full-copy deserialization 27 | cursor.set_position(0); 28 | let full = >::deserialize_full(&mut cursor).unwrap(); 29 | println!( 30 | "Full-copy deserialization type: {}", 31 | std::any::type_name::>(), 32 | ); 33 | println!("Value: {:x?}", full); 34 | 35 | println!(); 36 | 37 | // Do an ε-copy deserialization 38 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 39 | println!( 40 | "ε-copy deserialization type: {}", 41 | std::any::type_name::< as DeserializeInner>::DeserType<'_>>(), 42 | ); 43 | println!("Value: {:x?}", eps); 44 | } 45 | -------------------------------------------------------------------------------- /epserde/src/deser/helpers.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Helpers for deserialization. 11 | 12 | */ 13 | 14 | use super::SliceWithPos; 15 | use super::{read::*, DeserializeInner}; 16 | use crate::deser; 17 | use crate::traits::*; 18 | use core::mem::MaybeUninit; 19 | 20 | /// Full-copy deserialize a zero-copy structure. 21 | pub unsafe fn deserialize_full_zero( 22 | backend: &mut impl ReadWithPos, 23 | ) -> deser::Result { 24 | backend.align::()?; 25 | unsafe { 26 | let mut buf: MaybeUninit = MaybeUninit::uninit(); 27 | let slice = core::slice::from_raw_parts_mut( 28 | &mut buf as *mut MaybeUninit as *mut u8, 29 | core::mem::size_of::(), 30 | ); 31 | backend.read_exact(slice)?; 32 | Ok(buf.assume_init()) 33 | } 34 | } 35 | 36 | /// Full-copy deserialize a vector of zero-copy structures. 37 | /// 38 | /// Note that this method uses a single [`ReadNoStd::read_exact`] 39 | /// call to read the entire vector. 40 | pub unsafe fn deserialize_full_vec_zero( 41 | backend: &mut impl ReadWithPos, 42 | ) -> deser::Result> { 43 | let len = usize::_deserialize_full_inner(backend)?; 44 | backend.align::()?; 45 | let mut res = Vec::with_capacity(len); 46 | // SAFETY: we just allocated this vector so it is safe to set the length. 47 | // read_exact guarantees that the vector will be filled with data. 48 | #[allow(clippy::uninit_vec)] 49 | unsafe { 50 | res.set_len(len); 51 | backend.read_exact(res.align_to_mut::().1)?; 52 | } 53 | 54 | Ok(res) 55 | } 56 | 57 | /// Full-copy deserialize a vector of deep-copy structures. 58 | pub fn deserialize_full_vec_deep( 59 | backend: &mut impl ReadWithPos, 60 | ) -> deser::Result> { 61 | let len = usize::_deserialize_full_inner(backend)?; 62 | let mut res = Vec::with_capacity(len); 63 | for _ in 0..len { 64 | res.push(T::_deserialize_full_inner(backend)?); 65 | } 66 | Ok(res) 67 | } 68 | 69 | /// ε-copy deserialize a reference to a zero-copy structure 70 | /// backed by the `data` field of `backend`. 71 | pub unsafe fn deserialize_eps_zero<'a, T: ZeroCopy>( 72 | backend: &mut SliceWithPos<'a>, 73 | ) -> deser::Result<&'a T> { 74 | let bytes = core::mem::size_of::(); 75 | if bytes == 0 { 76 | // SAFETY: T is zero-sized and `assume_init` is safe. 77 | #[allow(invalid_value)] 78 | #[allow(clippy::uninit_assumed_init)] 79 | return Ok(unsafe { MaybeUninit::uninit().assume_init() }); 80 | } 81 | backend.align::()?; 82 | let (pre, data, after) = unsafe { backend.data[..bytes].align_to::() }; 83 | debug_assert!(pre.is_empty()); 84 | debug_assert!(after.is_empty()); 85 | let res = &data[0]; 86 | backend.skip(bytes); 87 | Ok(res) 88 | } 89 | 90 | /// ε-copy deserialize a reference to a slice of zero-copy structures 91 | /// backed by the `data` field of `backend`. 92 | pub unsafe fn deserialize_eps_slice_zero<'a, T: ZeroCopy>( 93 | backend: &mut SliceWithPos<'a>, 94 | ) -> deser::Result<&'a [T]> { 95 | let len = usize::_deserialize_full_inner(backend)?; 96 | let bytes = len * core::mem::size_of::(); 97 | if core::mem::size_of::() == 0 { 98 | // SAFETY: T is zero-sized and `assume_init` is safe. 99 | #[allow(invalid_value)] 100 | #[allow(clippy::uninit_assumed_init)] 101 | return Ok(unsafe { std::slice::from_raw_parts(MaybeUninit::uninit().assume_init(), len) }); 102 | } 103 | backend.align::()?; 104 | let (pre, data, after) = unsafe { backend.data[..bytes].align_to::() }; 105 | debug_assert!(pre.is_empty()); 106 | debug_assert!(after.is_empty()); 107 | backend.skip(bytes); 108 | Ok(data) 109 | } 110 | 111 | /// ε-copy deserialize a vector of deep-copy structures. 112 | pub fn deserialize_eps_vec_deep<'a, T: DeepCopy + DeserializeInner>( 113 | backend: &mut SliceWithPos<'a>, 114 | ) -> deser::Result::DeserType<'a>>> { 115 | let len = usize::_deserialize_full_inner(backend)?; 116 | let mut res = Vec::with_capacity(len); 117 | for _ in 0..len { 118 | res.push(T::_deserialize_eps_inner(backend)?); 119 | } 120 | Ok(res) 121 | } 122 | -------------------------------------------------------------------------------- /epserde/src/deser/mem_case.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use bitflags::bitflags; 8 | use core::{mem::size_of, ops::Deref}; 9 | use maligned::A64; 10 | use mem_dbg::{MemDbg, MemSize}; 11 | 12 | bitflags! { 13 | /// Flags for [`map`] and [`load_mmap`]. 14 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 15 | pub struct Flags: u32 { 16 | /// Suggest to map a region using transparent huge pages. This flag 17 | /// is only a suggestion, and it is ignored if the kernel does not 18 | /// support transparent huge pages. It is mainly useful to support 19 | /// `madvise()`-based huge pages on Linux. Note that at the time 20 | /// of this writing Linux does not support transparent huge pages 21 | /// in file-based memory mappings. 22 | const TRANSPARENT_HUGE_PAGES = 1 << 0; 23 | /// Suggest that the mapped region will be accessed sequentially. 24 | /// 25 | /// This flag is only a suggestion, and it is ignored if the kernel does 26 | /// not support it. It is mainly useful to support `madvise()` on Linux. 27 | const SEQUENTIAL = 1 << 1; 28 | /// Suggest that the mapped region will be accessed randomly. 29 | /// 30 | /// This flag is only a suggestion, and it is ignored if the kernel does 31 | /// not support it. It is mainly useful to support `madvise()` on Linux. 32 | const RANDOM_ACCESS = 1 << 2; 33 | } 34 | } 35 | 36 | /// Empty flags. 37 | impl core::default::Default for Flags { 38 | fn default() -> Self { 39 | Flags::empty() 40 | } 41 | } 42 | 43 | impl Flags { 44 | /// Translates internal flags to `mmap_rs` flags. 45 | #[cfg(feature = "mmap")] 46 | pub(crate) fn mmap_flags(&self) -> mmap_rs::MmapFlags { 47 | let mut flags: mmap_rs::MmapFlags = mmap_rs::MmapFlags::empty(); 48 | if self.contains(Self::SEQUENTIAL) { 49 | flags |= mmap_rs::MmapFlags::SEQUENTIAL; 50 | } 51 | if self.contains(Self::RANDOM_ACCESS) { 52 | flags |= mmap_rs::MmapFlags::RANDOM_ACCESS; 53 | } 54 | if self.contains(Self::TRANSPARENT_HUGE_PAGES) { 55 | flags |= mmap_rs::MmapFlags::TRANSPARENT_HUGE_PAGES; 56 | } 57 | 58 | flags 59 | } 60 | } 61 | 62 | /// The [alignment](maligned::Alignment) by the [`Memory`](MemBackend::Memory) variant of [`MemBackend`]. 63 | pub type MemoryAlignment = A64; 64 | 65 | /// Possible backends of a [`MemCase`]. The `None` variant is used when the data structure is 66 | /// created in memory; the `Memory` variant is used when the data structure is deserialized 67 | /// from a file loaded into a heap-allocated memory region; the `Mmap` variant is used when 68 | /// the data structure is deserialized from a `mmap()`-based region, either coming from 69 | /// an allocation or a from mapping a file. 70 | #[derive(Debug, MemDbg, MemSize)] 71 | pub enum MemBackend { 72 | /// No backend. The data structure is a standard Rust data structure. 73 | /// This variant is returned by [`MemCase::encase`]. 74 | None, 75 | /// The backend is a heap-allocated in a memory region aligned to 16 bytes. 76 | /// This variant is returned by [`crate::deser::Deserialize::load_mem`]. 77 | Memory(Box<[MemoryAlignment]>), 78 | /// The backend is the result to a call to `mmap()`. 79 | /// This variant is returned by [`crate::deser::Deserialize::load_mmap`] and [`crate::deser::Deserialize::mmap`]. 80 | #[cfg(feature = "mmap")] 81 | Mmap(mmap_rs::Mmap), 82 | } 83 | 84 | impl MemBackend { 85 | pub fn as_ref(&self) -> Option<&[u8]> { 86 | match self { 87 | MemBackend::None => None, 88 | MemBackend::Memory(mem) => Some(unsafe { 89 | core::slice::from_raw_parts( 90 | mem.as_ptr() as *const MemoryAlignment as *const u8, 91 | mem.len() * size_of::(), 92 | ) 93 | }), 94 | #[cfg(feature = "mmap")] 95 | MemBackend::Mmap(mmap) => Some(mmap), 96 | } 97 | } 98 | } 99 | 100 | /// A wrapper keeping together an immutable structure and the memory 101 | /// it was deserialized from. [`MemCase`] instances can not be cloned, but references 102 | /// to such instances can be shared freely. 103 | /// 104 | /// [`MemCase`] implements [`Deref`] and [`AsRef`] to the 105 | /// wrapped type, so it can be used almost transparently and 106 | /// with no performance cost. However, 107 | /// if you need to use a memory-mapped structure as a field in 108 | /// a struct and you want to avoid `dyn`, you will have 109 | /// to use [`MemCase`] as the type of the field. 110 | /// [`MemCase`] implements [`From`] for the 111 | /// wrapped type, using the no-op [`None`](`MemBackend#variant.None`) variant 112 | /// of [`MemBackend`], so a structure can be [encased](MemCase::encase) 113 | /// almost transparently. 114 | #[derive(Debug, MemDbg, MemSize)] 115 | pub struct MemCase(pub(crate) S, pub(crate) MemBackend); 116 | 117 | impl MemCase { 118 | /// Encases a data structure in a [`MemCase`] with no backend. 119 | pub fn encase(s: S) -> MemCase { 120 | MemCase(s, MemBackend::None) 121 | } 122 | } 123 | 124 | unsafe impl Send for MemCase {} 125 | unsafe impl Sync for MemCase {} 126 | 127 | impl Deref for MemCase { 128 | type Target = S; 129 | #[inline(always)] 130 | fn deref(&self) -> &Self::Target { 131 | &self.0 132 | } 133 | } 134 | 135 | impl AsRef for MemCase { 136 | #[inline(always)] 137 | fn as_ref(&self) -> &S { 138 | &self.0 139 | } 140 | } 141 | 142 | impl From for MemCase { 143 | fn from(s: S) -> Self { 144 | MemCase::encase(s) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /epserde/src/deser/read.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | No-std support for reading while keeping track of the current position. 11 | 12 | */ 13 | 14 | use crate::prelude::*; 15 | 16 | /// [`std::io::Read`]-like trait for serialization that does not 17 | /// depend on [`std`]. 18 | /// 19 | /// In an [`std`] context, the user does not need to use directly 20 | /// this trait as we provide a blanket 21 | /// implementation that implements [`ReadNoStd`] for all types that implement 22 | /// [`std::io::Read`]. In particular, in such a context you can use [`std::io::Cursor`] 23 | /// for in-memory deserialization. 24 | pub trait ReadNoStd { 25 | /// Read some bytes 26 | fn read_exact(&mut self, buf: &mut [u8]) -> deser::Result<()>; 27 | } 28 | 29 | #[cfg(feature = "std")] 30 | use std::io::Read; 31 | #[cfg(feature = "std")] 32 | impl ReadNoStd for W { 33 | #[inline(always)] 34 | fn read_exact(&mut self, buf: &mut [u8]) -> deser::Result<()> { 35 | Read::read_exact(self, buf).map_err(|_| deser::Error::ReadError) 36 | } 37 | } 38 | 39 | /// A trait for [`ReadNoStd`] that also keeps track of the current position. 40 | /// 41 | /// This is needed because the [`Read`] trait doesn't have a `seek` method and 42 | /// [`std::io::Seek`] would be a requirement much stronger than needed. 43 | pub trait ReadWithPos: ReadNoStd + Sized { 44 | /// Return the current position. 45 | fn pos(&self) -> usize; 46 | 47 | /// Pad the cursor to the next multiple of [`MaxSizeOf::max_size_of`] 'T'. 48 | fn align(&mut self) -> deser::Result<()>; 49 | } 50 | -------------------------------------------------------------------------------- /epserde/src/deser/reader_with_pos.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | use crate::prelude::*; 9 | 10 | use super::ReadNoStd; 11 | use mem_dbg::{MemDbg, MemSize}; 12 | 13 | /// A wrapper for a [`ReadNoStd`] that implements [`ReadWithPos`] 14 | /// by keeping track of the current position. 15 | #[derive(Debug, MemDbg, MemSize)] 16 | pub struct ReaderWithPos<'a, F: ReadNoStd> { 17 | /// What we actually readfrom 18 | backend: &'a mut F, 19 | /// How many bytes we have read from the start 20 | pos: usize, 21 | } 22 | 23 | impl<'a, F: ReadNoStd> ReaderWithPos<'a, F> { 24 | #[inline(always)] 25 | /// Create a new [`ReadWithPos`] on top of a generic [`ReadNoStd`]. 26 | pub fn new(backend: &'a mut F) -> Self { 27 | Self { backend, pos: 0 } 28 | } 29 | } 30 | 31 | impl ReadNoStd for ReaderWithPos<'_, F> { 32 | fn read_exact(&mut self, buf: &mut [u8]) -> deser::Result<()> { 33 | self.backend.read_exact(buf)?; 34 | self.pos += buf.len(); 35 | Ok(()) 36 | } 37 | } 38 | 39 | impl ReadWithPos for ReaderWithPos<'_, F> { 40 | fn pos(&self) -> usize { 41 | self.pos 42 | } 43 | 44 | fn align(&mut self) -> deser::Result<()> { 45 | // Skip bytes as needed 46 | let padding = crate::pad_align_to(self.pos, T::max_size_of()); 47 | self.read_exact(&mut vec![0; padding])?; 48 | // No alignment check, we are fully deserializing 49 | Ok(()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /epserde/src/deser/slice_with_pos.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | use super::*; 9 | use crate::prelude::*; 10 | use mem_dbg::{MemDbg, MemSize}; 11 | 12 | /// [`std::io::Cursor`]-like trait for deserialization that does not 13 | /// depend on [`std`]. 14 | #[derive(Debug, Clone, MemDbg, MemSize)] 15 | pub struct SliceWithPos<'a> { 16 | pub data: &'a [u8], 17 | pub pos: usize, 18 | } 19 | 20 | impl<'a> SliceWithPos<'a> { 21 | pub fn new(backend: &'a [u8]) -> Self { 22 | Self { 23 | data: backend, 24 | pos: 0, 25 | } 26 | } 27 | 28 | pub fn skip(&mut self, bytes: usize) { 29 | self.data = &self.data[bytes..]; 30 | self.pos += bytes; 31 | } 32 | } 33 | 34 | impl ReadNoStd for SliceWithPos<'_> { 35 | fn read_exact(&mut self, buf: &mut [u8]) -> deser::Result<()> { 36 | let len = buf.len(); 37 | if len > self.data.len() { 38 | return Err(Error::ReadError); 39 | } 40 | buf.copy_from_slice(&self.data[..len]); 41 | self.data = &self.data[len..]; 42 | self.pos += len; 43 | Ok(()) 44 | } 45 | } 46 | 47 | impl ReadWithPos for SliceWithPos<'_> { 48 | fn pos(&self) -> usize { 49 | self.pos 50 | } 51 | 52 | /// Pad the cursor to the correct alignment. 53 | /// 54 | /// Note that this method also checks that 55 | /// the absolute memory position is properly aligned. 56 | fn align(&mut self) -> deser::Result<()> { 57 | // Skip bytes as needed 58 | let padding = crate::pad_align_to(self.pos, T::max_size_of()); 59 | self.skip(padding); 60 | // Check that the ptr is indeed aligned 61 | if self.data.as_ptr() as usize % T::max_size_of() != 0 { 62 | Err(Error::AlignmentError) 63 | } else { 64 | Ok(()) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /epserde/src/impls/array.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for arrays. 11 | 12 | */ 13 | use crate::prelude::*; 14 | use core::hash::Hash; 15 | use core::mem::MaybeUninit; 16 | use deser::*; 17 | use ser::*; 18 | 19 | impl CopyType for [T; N] { 20 | type Copy = T::Copy; 21 | } 22 | 23 | impl TypeHash for [T; N] { 24 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 25 | "[]".hash(hasher); 26 | hasher.write_usize(N); 27 | T::type_hash(hasher); 28 | } 29 | } 30 | 31 | impl AlignHash for [T; N] { 32 | fn align_hash(hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) { 33 | if N == 0 { 34 | return; 35 | } 36 | T::align_hash(hasher, offset_of); 37 | *offset_of += (N - 1) * size_of::(); 38 | } 39 | } 40 | 41 | impl MaxSizeOf for [T; N] { 42 | fn max_size_of() -> usize { 43 | T::max_size_of() 44 | } 45 | } 46 | 47 | impl SerializeInner for [T; N] 48 | where 49 | [T; N]: SerializeHelper<::Copy>, 50 | { 51 | type SerType = Self; 52 | const IS_ZERO_COPY: bool = T::IS_ZERO_COPY; 53 | const ZERO_COPY_MISMATCH: bool = T::ZERO_COPY_MISMATCH; 54 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 55 | SerializeHelper::_serialize_inner(self, backend) 56 | } 57 | } 58 | 59 | impl SerializeHelper 60 | for [T; N] 61 | { 62 | #[inline(always)] 63 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 64 | serialize_zero(backend, self) 65 | } 66 | } 67 | 68 | impl SerializeHelper for [T; N] { 69 | #[inline(always)] 70 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 71 | for item in self.iter() { 72 | backend.write("item", item)?; 73 | } 74 | Ok(()) 75 | } 76 | } 77 | 78 | unsafe impl DeserializeInner for [T; N] 79 | where 80 | [T; N]: DeserializeHelper<::Copy, FullType = [T; N]>, 81 | { 82 | type DeserType<'a> = <[T; N] as DeserializeHelper<::Copy>>::DeserType<'a>; 83 | #[inline(always)] 84 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 85 | <[T; N] as DeserializeHelper<::Copy>>::_deserialize_full_inner_impl(backend) 86 | } 87 | 88 | #[inline(always)] 89 | fn _deserialize_eps_inner<'a>( 90 | backend: &mut SliceWithPos<'a>, 91 | ) -> deser::Result<<[T; N] as DeserializeHelper<::Copy>>::DeserType<'a>> { 92 | <[T; N] as DeserializeHelper<::Copy>>::_deserialize_eps_inner_impl(backend) 93 | } 94 | } 95 | 96 | unsafe impl DeserializeHelper for [T; N] { 97 | type FullType = Self; 98 | type DeserType<'a> = &'a [T; N]; 99 | #[inline(always)] 100 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 101 | let mut res = MaybeUninit::<[T; N]>::uninit(); 102 | backend.align::()?; 103 | // SAFETY: read_exact guarantees that the array will be filled with data. 104 | unsafe { 105 | backend.read_exact(res.assume_init_mut().align_to_mut::().1)?; 106 | Ok(res.assume_init()) 107 | } 108 | } 109 | 110 | #[inline(always)] 111 | fn _deserialize_eps_inner_impl<'a>( 112 | backend: &mut SliceWithPos<'a>, 113 | ) -> deser::Result<::DeserType<'a>> { 114 | backend.align::()?; 115 | let bytes = std::mem::size_of::<[T; N]>(); 116 | let (pre, data, after) = unsafe { backend.data[..bytes].align_to::<[T; N]>() }; 117 | debug_assert!(pre.is_empty()); 118 | debug_assert!(after.is_empty()); 119 | let res = &data[0]; 120 | backend.skip(bytes); 121 | Ok(res) 122 | } 123 | } 124 | 125 | unsafe impl DeserializeHelper for [T; N] { 126 | type FullType = Self; 127 | type DeserType<'a> = [::DeserType<'a>; N]; 128 | #[inline(always)] 129 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 130 | let mut res = MaybeUninit::<[T; N]>::uninit(); 131 | unsafe { 132 | for item in &mut res.assume_init_mut().iter_mut() { 133 | std::ptr::write(item, T::_deserialize_full_inner(backend)?); 134 | } 135 | Ok(res.assume_init()) 136 | } 137 | } 138 | #[inline(always)] 139 | fn _deserialize_eps_inner_impl<'a>( 140 | backend: &mut SliceWithPos<'a>, 141 | ) -> deser::Result<::DeserType<'a>> { 142 | let mut res = MaybeUninit::<::DeserType<'_>>::uninit(); 143 | unsafe { 144 | for item in &mut res.assume_init_mut().iter_mut() { 145 | std::ptr::write(item, T::_deserialize_eps_inner(backend)?); 146 | } 147 | Ok(res.assume_init()) 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /epserde/src/impls/boxed_slice.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for boxed slices. 11 | 12 | */ 13 | use crate::deser::helpers::*; 14 | use crate::prelude::*; 15 | use core::hash::Hash; 16 | use deser::*; 17 | use ser::*; 18 | 19 | impl CopyType for Box<[T]> { 20 | type Copy = Deep; 21 | } 22 | 23 | impl TypeHash for Box<[T]> { 24 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 25 | "Box<[]>".hash(hasher); 26 | T::type_hash(hasher); 27 | } 28 | } 29 | 30 | impl AlignHash for Box<[T]> { 31 | fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) { 32 | T::align_hash(hasher, &mut 0); 33 | } 34 | } 35 | 36 | impl SerializeInner for Box<[T]> 37 | where 38 | Box<[T]>: SerializeHelper<::Copy>, 39 | { 40 | type SerType = Self; 41 | const IS_ZERO_COPY: bool = false; 42 | const ZERO_COPY_MISMATCH: bool = false; 43 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 44 | SerializeHelper::_serialize_inner(self, backend) 45 | } 46 | } 47 | 48 | impl SerializeHelper for Box<[T]> { 49 | #[inline(always)] 50 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 51 | serialize_slice_zero(backend, self) 52 | } 53 | } 54 | 55 | impl SerializeHelper for Box<[T]> { 56 | #[inline(always)] 57 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 58 | serialize_slice_deep(backend, self) 59 | } 60 | } 61 | 62 | // This delegates to a private helper trait which we can specialize on in stable rust 63 | unsafe impl DeserializeInner for Box<[T]> 64 | where 65 | Box<[T]>: DeserializeHelper<::Copy, FullType = Box<[T]>>, 66 | { 67 | type DeserType<'a> = as DeserializeHelper<::Copy>>::DeserType<'a>; 68 | #[inline(always)] 69 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 70 | as DeserializeHelper<::Copy>>::_deserialize_full_inner_impl( 71 | backend, 72 | ) 73 | } 74 | 75 | #[inline(always)] 76 | fn _deserialize_eps_inner<'a>( 77 | backend: &mut SliceWithPos<'a>, 78 | ) -> deser::Result< as DeserializeHelper<::Copy>>::DeserType<'a>> { 79 | as DeserializeHelper<::Copy>>::_deserialize_eps_inner_impl(backend) 80 | } 81 | } 82 | 83 | unsafe impl DeserializeHelper for Box<[T]> { 84 | type FullType = Self; 85 | type DeserType<'a> = &'a [T]; 86 | #[inline(always)] 87 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 88 | Ok(unsafe { deserialize_full_vec_zero::(backend) }?.into_boxed_slice()) 89 | } 90 | #[inline(always)] 91 | fn _deserialize_eps_inner_impl<'a>( 92 | backend: &mut SliceWithPos<'a>, 93 | ) -> deser::Result<::DeserType<'a>> { 94 | unsafe { deserialize_eps_slice_zero(backend) } 95 | } 96 | } 97 | 98 | unsafe impl DeserializeHelper for Box<[T]> { 99 | type FullType = Self; 100 | type DeserType<'a> = Box<[::DeserType<'a>]>; 101 | #[inline(always)] 102 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 103 | Ok(deserialize_full_vec_deep(backend)?.into_boxed_slice()) 104 | } 105 | #[inline(always)] 106 | fn _deserialize_eps_inner_impl<'a>( 107 | backend: &mut SliceWithPos<'a>, 108 | ) -> deser::Result<::DeserType<'a>> { 109 | Ok(deserialize_eps_vec_deep::(backend)?.into_boxed_slice()) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /epserde/src/impls/iter.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2025 Sebastiano Vigna 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | /*! 8 | 9 | Implementations for exact-size iterators. 10 | 11 | In theory all types serialized by ε-serde must be immutable. However, 12 | we provide a convenience implementation that serializes 13 | [exact-size iterators](core::iter::ExactSizeIterator) returning 14 | references to `T` as vectors of `T`. 15 | 16 | More precisely, we provide a [`SerIter`] type that [wraps](SerIter::new) 17 | an iterator into a serializable type. We provide a [`From`] implementation for convenience. 18 | 19 | Note, however, that you must deserialize the iterator as a vector—see 20 | the example in the [crate-level documentation](crate). 21 | 22 | */ 23 | 24 | use core::{cell::RefCell, ops::DerefMut}; 25 | 26 | use crate::prelude::*; 27 | use ser::*; 28 | 29 | #[derive(Clone, Debug, PartialEq, Eq, Default)] 30 | pub struct SerIter<'a, T: 'a, I: ExactSizeIterator>(RefCell); 31 | 32 | impl<'a, T: ZeroCopy + TypeHash, I: ExactSizeIterator> CopyType 33 | for SerIter<'a, T, I> 34 | { 35 | type Copy = Deep; 36 | } 37 | 38 | impl<'a, T: ZeroCopy + TypeHash, I: ExactSizeIterator> SerIter<'a, T, I> { 39 | pub fn new(iter: I) -> Self { 40 | SerIter(RefCell::new(iter)) 41 | } 42 | } 43 | 44 | impl<'a, T: ZeroCopy + TypeHash, I: ExactSizeIterator> From for SerIter<'a, T, I> { 45 | fn from(iter: I) -> Self { 46 | SerIter::new(iter) 47 | } 48 | } 49 | 50 | impl<'a, T: ZeroCopy + TypeHash, I: ExactSizeIterator> TypeHash 51 | for SerIter<'a, T, I> 52 | { 53 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 54 | Vec::::type_hash(hasher); 55 | } 56 | } 57 | 58 | impl<'a, T: ZeroCopy + AlignHash, I: ExactSizeIterator> AlignHash 59 | for SerIter<'a, T, I> 60 | { 61 | fn align_hash(hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) { 62 | Vec::::align_hash(hasher, offset_of); 63 | } 64 | } 65 | 66 | impl< 67 | 'a, 68 | T: ZeroCopy + SerializeInner + TypeHash + AlignHash, 69 | I: ExactSizeIterator, 70 | > SerializeInner for SerIter<'a, T, I> 71 | where 72 | SerIter<'a, T, I>: SerializeHelper<::Copy>, 73 | { 74 | type SerType = Vec; 75 | const IS_ZERO_COPY: bool = false; 76 | const ZERO_COPY_MISMATCH: bool = false; 77 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 78 | SerializeHelper::_serialize_inner(self, backend) 79 | } 80 | } 81 | 82 | impl< 83 | 'a, 84 | T: ZeroCopy + SerializeInner + TypeHash + AlignHash, 85 | I: ExactSizeIterator, 86 | > SerializeHelper for SerIter<'a, T, I> 87 | { 88 | #[inline(always)] 89 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 90 | check_zero_copy::(); 91 | // This code must be kept aligned with that of Vec for zero-copy 92 | // types 93 | let mut iter = self.0.borrow_mut(); 94 | let len = iter.len(); 95 | backend.write("len", &len)?; 96 | backend.align::()?; 97 | 98 | let mut c = 0; 99 | for item in iter.deref_mut() { 100 | serialize_zero_unchecked(backend, item)?; 101 | c += 1; 102 | } 103 | 104 | if c != len { 105 | Err(ser::Error::IteratorLengthMismatch { 106 | actual: c, 107 | expected: len, 108 | }) 109 | } else { 110 | Ok(()) 111 | } 112 | } 113 | } 114 | 115 | impl< 116 | 'a, 117 | T: DeepCopy + SerializeInner + TypeHash + AlignHash, 118 | I: ExactSizeIterator, 119 | > SerializeHelper for SerIter<'a, T, I> 120 | { 121 | #[inline(always)] 122 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 123 | check_mismatch::(); 124 | // This code must be kept aligned with that of Vec for deep-copy 125 | // types 126 | let mut iter = self.0.borrow_mut(); 127 | let len = iter.len(); 128 | backend.write("len", &len)?; 129 | 130 | let mut c = 0; 131 | for item in iter.deref_mut() { 132 | item._serialize_inner(backend)?; 133 | c += 1; 134 | } 135 | 136 | if c != len { 137 | Err(ser::Error::IteratorLengthMismatch { 138 | actual: c, 139 | expected: len, 140 | }) 141 | } else { 142 | Ok(()) 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /epserde/src/impls/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations of [`SerializeInner`](crate::ser::SerializeInner) 11 | and [`DeserializeInner`](crate::deser::DeserializeInner) for standard Rust types. 12 | 13 | */ 14 | 15 | pub mod array; 16 | pub mod boxed_slice; 17 | pub mod iter; 18 | pub mod prim; 19 | pub mod slice; 20 | #[cfg(feature = "std")] 21 | pub mod stdlib; 22 | pub mod string; 23 | pub mod tuple; 24 | #[cfg(any(feature = "alloc", feature = "std"))] 25 | pub mod vec; 26 | -------------------------------------------------------------------------------- /epserde/src/impls/prim.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for primitive types, `()`, [`PhantomData`] and [`Option`]. 11 | 12 | */ 13 | 14 | use crate::prelude::*; 15 | use common_traits::NonZero; 16 | use core::hash::Hash; 17 | use core::marker::PhantomData; 18 | use core::mem::size_of; 19 | use core::num::{ 20 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 21 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, 22 | }; 23 | use deser::*; 24 | use ser::*; 25 | 26 | macro_rules! impl_prim_type_hash { 27 | ($($ty:ty),*) => {$( 28 | impl CopyType for $ty { 29 | type Copy = Zero; 30 | } 31 | 32 | impl TypeHash for $ty { 33 | fn type_hash( 34 | hasher: &mut impl core::hash::Hasher, 35 | ) { 36 | stringify!($ty).hash(hasher); 37 | } 38 | } 39 | 40 | impl AlignHash for $ty { 41 | fn align_hash(hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) { 42 | crate::traits::std_align_hash::(hasher, offset_of) 43 | } 44 | } 45 | 46 | impl MaxSizeOf for $ty { 47 | fn max_size_of() -> usize { 48 | size_of::<$ty>() 49 | } 50 | } 51 | )*}; 52 | } 53 | 54 | macro_rules! impl_prim_ser_des { 55 | ($($ty:ty),*) => {$( 56 | impl SerializeInner for $ty { 57 | type SerType = Self; 58 | // Note that primitive types are declared zero-copy to be able to 59 | // be part of zero-copy types, but we actually deserialize 60 | // them in isolation as values. 61 | const IS_ZERO_COPY: bool = true; 62 | const ZERO_COPY_MISMATCH: bool = false; 63 | 64 | #[inline(always)] 65 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 66 | backend.write_all(&self.to_ne_bytes()) 67 | } 68 | } 69 | 70 | unsafe impl DeserializeInner for $ty { 71 | #[inline(always)] 72 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<$ty> { 73 | let mut buf = [0; size_of::<$ty>()]; 74 | backend.read_exact(&mut buf)?; 75 | Ok(<$ty>::from_ne_bytes(buf)) 76 | } 77 | type DeserType<'a> = Self; 78 | #[inline(always)] 79 | fn _deserialize_eps_inner<'a>( 80 | backend: &mut SliceWithPos<'a>, 81 | ) -> deser::Result> { 82 | let res = <$ty>::from_ne_bytes( 83 | backend.data[..size_of::<$ty>()] 84 | .try_into() 85 | .unwrap()); 86 | 87 | backend.skip(size_of::<$ty>()); 88 | Ok(res) 89 | } 90 | } 91 | )*}; 92 | } 93 | 94 | impl_prim_type_hash!(isize, i8, i16, i32, i64, i128, usize, u8, u16, u32, u64, u128, f32, f64); 95 | impl_prim_ser_des!(isize, i8, i16, i32, i64, i128, usize, u8, u16, u32, u64, u128, f32, f64); 96 | 97 | macro_rules! impl_nonzero_ser_des { 98 | ($($ty:ty),*) => {$( 99 | impl SerializeInner for $ty { 100 | type SerType = Self; // Note that primitive types are declared zero-copy to be able to 101 | // be part of zero-copy types, but we actually deserialize 102 | // them in isolation as values. 103 | const IS_ZERO_COPY: bool = true; 104 | const ZERO_COPY_MISMATCH: bool = false; 105 | 106 | #[inline(always)] 107 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 108 | backend.write_all(&self.get().to_ne_bytes()) 109 | } 110 | } 111 | 112 | unsafe impl DeserializeInner for $ty { 113 | #[inline(always)] 114 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<$ty> { 115 | let mut buf = [0; size_of::<$ty>()]; 116 | backend.read_exact(&mut buf)?; 117 | Ok(<$ty as NonZero>::BaseType::from_ne_bytes(buf).try_into().unwrap()) 118 | } 119 | type DeserType<'a> = Self; 120 | #[inline(always)] 121 | fn _deserialize_eps_inner<'a>( 122 | backend: &mut SliceWithPos<'a>, 123 | ) -> deser::Result> { 124 | let res = <$ty as NonZero>::BaseType::from_ne_bytes( 125 | backend.data[..size_of::<$ty>()] 126 | .try_into() 127 | .unwrap()).try_into().unwrap(); 128 | 129 | backend.skip(size_of::<$ty>()); 130 | Ok(res) 131 | } 132 | } 133 | )*}; 134 | } 135 | 136 | impl_prim_type_hash!( 137 | NonZeroIsize, 138 | NonZeroI8, 139 | NonZeroI16, 140 | NonZeroI32, 141 | NonZeroI64, 142 | NonZeroI128, 143 | NonZeroUsize, 144 | NonZeroU8, 145 | NonZeroU16, 146 | NonZeroU32, 147 | NonZeroU64, 148 | NonZeroU128 149 | ); 150 | 151 | impl_nonzero_ser_des!( 152 | NonZeroIsize, 153 | NonZeroI8, 154 | NonZeroI16, 155 | NonZeroI32, 156 | NonZeroI64, 157 | NonZeroI128, 158 | NonZeroUsize, 159 | NonZeroU8, 160 | NonZeroU16, 161 | NonZeroU32, 162 | NonZeroU64, 163 | NonZeroU128 164 | ); 165 | 166 | impl_prim_type_hash!(bool, char, ()); 167 | 168 | // Booleans are zero-copy serialized as u8. 169 | 170 | impl SerializeInner for bool { 171 | type SerType = Self; 172 | const IS_ZERO_COPY: bool = true; 173 | const ZERO_COPY_MISMATCH: bool = false; 174 | 175 | #[inline(always)] 176 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 177 | let val = if *self { 1 } else { 0 }; 178 | backend.write_all(&[val]) 179 | } 180 | } 181 | 182 | unsafe impl DeserializeInner for bool { 183 | #[inline(always)] 184 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 185 | Ok(u8::_deserialize_full_inner(backend)? != 0) 186 | } 187 | type DeserType<'a> = Self; 188 | #[inline(always)] 189 | fn _deserialize_eps_inner<'a>( 190 | backend: &mut SliceWithPos<'a>, 191 | ) -> deser::Result> { 192 | let res = backend.data[0] != 0; 193 | backend.skip(1); 194 | Ok(res) 195 | } 196 | } 197 | 198 | // Chars are zero-copy serialized as u32. 199 | 200 | impl SerializeInner for char { 201 | type SerType = Self; 202 | const IS_ZERO_COPY: bool = true; 203 | const ZERO_COPY_MISMATCH: bool = false; 204 | 205 | #[inline(always)] 206 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 207 | (*self as u32)._serialize_inner(backend) 208 | } 209 | } 210 | 211 | unsafe impl DeserializeInner for char { 212 | #[inline(always)] 213 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 214 | Ok(char::from_u32(u32::_deserialize_full_inner(backend)?).unwrap()) 215 | } 216 | type DeserType<'a> = Self; 217 | #[inline(always)] 218 | fn _deserialize_eps_inner<'a>( 219 | backend: &mut SliceWithPos<'a>, 220 | ) -> deser::Result> { 221 | Ok(char::from_u32(u32::_deserialize_eps_inner(backend)?).unwrap()) 222 | } 223 | } 224 | 225 | // () is zero-copy. No reading or writing is performed when (de)serializing it. 226 | 227 | impl SerializeInner for () { 228 | type SerType = (); 229 | const IS_ZERO_COPY: bool = true; 230 | const ZERO_COPY_MISMATCH: bool = false; 231 | 232 | #[inline(always)] 233 | fn _serialize_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> { 234 | Ok(()) 235 | } 236 | } 237 | 238 | unsafe impl DeserializeInner for () { 239 | #[inline(always)] 240 | fn _deserialize_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result { 241 | Ok(()) 242 | } 243 | type DeserType<'a> = Self; 244 | #[inline(always)] 245 | fn _deserialize_eps_inner<'a>( 246 | _backend: &mut SliceWithPos<'a>, 247 | ) -> deser::Result> { 248 | Ok(()) 249 | } 250 | } 251 | 252 | // PhantomData is zero-copy. No reading or writing is performed when 253 | // (de)serializing it. The type parameter does not have to be sized, 254 | // but it does have to implement TypeHash, as we must be able to tell 255 | // apart structures with different type parameters stored in a PhantomData. 256 | 257 | impl CopyType for PhantomData { 258 | type Copy = Zero; 259 | } 260 | 261 | impl MaxSizeOf for PhantomData { 262 | fn max_size_of() -> usize { 263 | 0 264 | } 265 | } 266 | 267 | impl TypeHash for PhantomData { 268 | #[inline(always)] 269 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 270 | "PhantomData".hash(hasher); 271 | T::type_hash(hasher); 272 | } 273 | } 274 | 275 | impl AlignHash for PhantomData { 276 | #[inline(always)] 277 | fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {} 278 | } 279 | 280 | impl SerializeInner for PhantomData { 281 | type SerType = Self; 282 | const IS_ZERO_COPY: bool = true; 283 | const ZERO_COPY_MISMATCH: bool = false; 284 | 285 | #[inline(always)] 286 | fn _serialize_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> { 287 | Ok(()) 288 | } 289 | } 290 | 291 | unsafe impl DeserializeInner for PhantomData { 292 | #[inline(always)] 293 | fn _deserialize_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result { 294 | Ok(PhantomData::) 295 | } 296 | type DeserType<'a> = Self; 297 | #[inline(always)] 298 | fn _deserialize_eps_inner<'a>( 299 | _backend: &mut SliceWithPos<'a>, 300 | ) -> deser::Result> { 301 | Ok(PhantomData) 302 | } 303 | } 304 | 305 | // Options are deep-copy types serialized as a one-byte tag (0 for None, 1 for Some) followed, in case, by the value. 306 | 307 | impl CopyType for Option { 308 | type Copy = Deep; 309 | } 310 | 311 | impl TypeHash for Option { 312 | #[inline(always)] 313 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 314 | "Option".hash(hasher); 315 | T::type_hash(hasher); 316 | } 317 | } 318 | 319 | impl AlignHash for Option { 320 | fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) { 321 | T::align_hash(hasher, &mut 0); 322 | } 323 | } 324 | 325 | impl SerializeInner for Option { 326 | type SerType = Self; 327 | const IS_ZERO_COPY: bool = false; 328 | const ZERO_COPY_MISMATCH: bool = false; 329 | 330 | #[inline(always)] 331 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 332 | match self { 333 | None => backend.write("Tag", &0_u8), 334 | Some(val) => { 335 | backend.write("Tag", &1_u8)?; 336 | backend.write("Some", val) 337 | } 338 | } 339 | } 340 | } 341 | 342 | unsafe impl DeserializeInner for Option { 343 | #[inline(always)] 344 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 345 | let tag = u8::_deserialize_full_inner(backend)?; 346 | match tag { 347 | 0 => Ok(None), 348 | 1 => Ok(Some(T::_deserialize_full_inner(backend)?)), 349 | _ => Err(deser::Error::InvalidTag(tag as usize)), 350 | } 351 | } 352 | type DeserType<'a> = Option<::DeserType<'a>>; 353 | #[inline(always)] 354 | fn _deserialize_eps_inner<'a>( 355 | backend: &mut SliceWithPos<'a>, 356 | ) -> deser::Result> { 357 | let tag = u8::_deserialize_full_inner(backend)?; 358 | match tag { 359 | 0 => Ok(None), 360 | 1 => Ok(Some(T::_deserialize_eps_inner(backend)?)), 361 | _ => Err(deser::Error::InvalidTag(backend.data[0] as usize)), 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /epserde/src/impls/slice.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for (references to) slices. 11 | 12 | In theory all types serialized by ε-serde must not contain references. However, 13 | we provide a convenience implementation that serializes references to 14 | slices as vectors. Moreover, we implement [`TypeHash`] and 15 | [`AlignHash`] for slices, so that they can be used with 16 | [`PhantomData`](std::marker::PhantomData). 17 | 18 | Note, however, that you must deserialize the slice as a vector, 19 | even when it appears a type parameter—see the example 20 | in the [crate-level documentation](crate). 21 | 22 | */ 23 | 24 | use core::hash::Hash; 25 | 26 | use crate::prelude::*; 27 | use ser::*; 28 | 29 | impl TypeHash for [T] { 30 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 31 | "[]".hash(hasher); 32 | T::type_hash(hasher); 33 | } 34 | } 35 | 36 | impl CopyType for &[T] { 37 | type Copy = Deep; 38 | } 39 | 40 | impl TypeHash for &[T] { 41 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 42 | Vec::::type_hash(hasher); 43 | } 44 | } 45 | 46 | impl AlignHash for &[T] { 47 | fn align_hash(hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) { 48 | Vec::::align_hash(hasher, offset_of); 49 | } 50 | } 51 | 52 | impl SerializeInner for &[T] 53 | where 54 | Vec: SerializeHelper<::Copy>, 55 | { 56 | type SerType = Vec; 57 | const IS_ZERO_COPY: bool = false; 58 | const ZERO_COPY_MISMATCH: bool = false; 59 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> Result<()> { 60 | // SAFETY: the fake vector we create is never used, and we forget it immediately 61 | // after writing it to the backend. 62 | let fake = unsafe { Vec::from_raw_parts(self.as_ptr() as *mut T, self.len(), self.len()) }; 63 | ser::SerializeInner::_serialize_inner(&fake, backend)?; 64 | core::mem::forget(fake); 65 | Ok(()) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /epserde/src/impls/string.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for strings. 11 | 12 | */ 13 | 14 | use crate::prelude::*; 15 | use core::hash::Hash; 16 | use deser::*; 17 | use ser::*; 18 | 19 | impl CopyType for String { 20 | type Copy = Deep; 21 | } 22 | 23 | #[cfg(all(feature = "alloc", not(feature = "std")))] 24 | use alloc::string::String; 25 | 26 | #[cfg(feature = "alloc")] 27 | impl TypeHash for String { 28 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 29 | "String".hash(hasher); 30 | } 31 | } 32 | 33 | impl AlignHash for String { 34 | fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {} 35 | } 36 | 37 | impl TypeHash for Box { 38 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 39 | "Box".hash(hasher); 40 | } 41 | } 42 | 43 | impl AlignHash for Box { 44 | fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {} 45 | } 46 | 47 | impl TypeHash for str { 48 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 49 | "str".hash(hasher); 50 | } 51 | } 52 | 53 | impl AlignHash for str { 54 | fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {} 55 | } 56 | 57 | impl SerializeInner for String { 58 | type SerType = Self; 59 | // Vec<$ty> can, but Vec> cannot! 60 | const IS_ZERO_COPY: bool = false; 61 | const ZERO_COPY_MISMATCH: bool = false; 62 | 63 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 64 | serialize_slice_zero(backend, self.as_bytes()) 65 | } 66 | } 67 | 68 | unsafe impl DeserializeInner for String { 69 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 70 | let slice = unsafe { deserialize_full_vec_zero(backend) }?; 71 | Ok(String::from_utf8(slice).unwrap()) 72 | } 73 | type DeserType<'a> = &'a str; 74 | #[inline(always)] 75 | fn _deserialize_eps_inner<'a>( 76 | backend: &mut SliceWithPos<'a>, 77 | ) -> deser::Result> { 78 | let slice = unsafe { deserialize_eps_slice_zero(backend) }?; 79 | // SAFETY: Actually this is unsafe if the data we read is not valid UTF-8 80 | Ok(unsafe { 81 | #[allow(clippy::transmute_bytes_to_str)] 82 | core::mem::transmute::<&'_ [u8], &'_ str>(slice) 83 | }) 84 | } 85 | } 86 | 87 | impl CopyType for Box { 88 | type Copy = Deep; 89 | } 90 | 91 | impl SerializeInner for Box { 92 | type SerType = Self; 93 | // Box<[$ty]> can, but Vec> cannot! 94 | const IS_ZERO_COPY: bool = false; 95 | const ZERO_COPY_MISMATCH: bool = false; 96 | 97 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 98 | serialize_slice_zero(backend, self.as_bytes()) 99 | } 100 | } 101 | 102 | unsafe impl DeserializeInner for Box { 103 | #[inline(always)] 104 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 105 | Ok(String::_deserialize_full_inner(backend)?.into_boxed_str()) 106 | } 107 | type DeserType<'a> = &'a str; 108 | #[inline(always)] 109 | fn _deserialize_eps_inner<'a>( 110 | backend: &mut SliceWithPos<'a>, 111 | ) -> deser::Result> { 112 | String::_deserialize_eps_inner(backend) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /epserde/src/impls/tuple.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | //! Implementations for tuples. 9 | //! 10 | //! We only support tuples of up to 12 elements of the same [`ZeroCopy`] type. 11 | //! The is no `repr(C)` for tuples, so we [cannot guarantee that the storage 12 | //! order of the fields is 13 | //! well-defined](https://doc.rust-lang.org/reference/type-layout.html#the-rust-representation). 14 | //! 15 | //! To circumvent this problem, you can define a tuple newtype with a `repr(C)` 16 | //! attribute. 17 | //! 18 | //! We also provide a [`TypeHash`] implementation for tuples of up to 12 19 | //! elements to help with the idiom `PhantomData<(T1, T2, …)>`. 20 | //! 21 | //! Note that up to ε-serde 0.7.0 we provided an erroneous implementation for 22 | //! mixed zero-copy types. If you serialized a structure using such a tuple, 23 | //! it will be no longer deserializable. 24 | 25 | use crate::prelude::*; 26 | use core::hash::Hash; 27 | use deser::*; 28 | use ser::*; 29 | 30 | macro_rules! impl_type_hash { 31 | ($($t:ident),*) => { 32 | impl<$($t: TypeHash,)*> TypeHash for ($($t,)*) 33 | { 34 | #[inline(always)] 35 | fn type_hash( 36 | hasher: &mut impl core::hash::Hasher, 37 | ) { 38 | "()".hash(hasher); 39 | $( 40 | <$t>::type_hash(hasher); 41 | )* 42 | } 43 | } 44 | } 45 | } 46 | 47 | macro_rules! impl_tuples { 48 | ($($t:ident),*) => { 49 | impl CopyType for ($($t,)*) { 50 | type Copy = Zero; 51 | } 52 | 53 | impl AlignHash for ($($t,)*) 54 | { 55 | #[inline(always)] 56 | fn align_hash( 57 | hasher: &mut impl core::hash::Hasher, 58 | offset_of: &mut usize, 59 | ) { 60 | $( 61 | <$t>::align_hash(hasher, offset_of); 62 | )* 63 | } 64 | } 65 | 66 | impl MaxSizeOf for ($($t,)*) 67 | { 68 | #[inline(always)] 69 | fn max_size_of() -> usize { 70 | let mut max_size_of = 0; 71 | $(if max_size_of < std::cmp::max(max_size_of, <$t>::max_size_of()) { 72 | max_size_of = <$t>::max_size_of(); 73 | })* 74 | max_size_of 75 | } 76 | } 77 | 78 | impl SerializeInner for ($($t,)*) { 79 | type SerType = Self; 80 | const IS_ZERO_COPY: bool = true; 81 | const ZERO_COPY_MISMATCH: bool = false; 82 | 83 | #[inline(always)] 84 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 85 | serialize_zero(backend, self) 86 | } 87 | } 88 | 89 | unsafe impl DeserializeInner for ($($t,)*) { 90 | type DeserType<'a> = &'a ($($t,)*); 91 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 92 | unsafe { deserialize_full_zero::<($($t,)*)>(backend) } 93 | } 94 | 95 | fn _deserialize_eps_inner<'a>( 96 | backend: &mut SliceWithPos<'a>, 97 | ) -> deser::Result> { 98 | unsafe { deserialize_eps_zero::<($($t,)*)>(backend) } 99 | } 100 | } 101 | }; 102 | } 103 | 104 | macro_rules! impl_tuples_muncher { 105 | ($ty:ident, $($t:ident),*) => { 106 | impl_tuples!($ty, $($t),*); 107 | impl_tuples_muncher!($($t),*); 108 | }; 109 | ($ty:ident) => { 110 | impl_tuples!($ty); 111 | }; 112 | () => {}; 113 | } 114 | 115 | impl_tuples_muncher!(T, T, T, T, T, T, T, T, T, T, T, T); 116 | 117 | macro_rules! impl_type_hash_muncher { 118 | ($ty:ident, $($t:ident),*) => { 119 | impl_type_hash!($ty, $($t),*); 120 | impl_type_hash_muncher!($($t),*); 121 | }; 122 | ($ty:ident) => { 123 | impl_type_hash!($ty); 124 | }; 125 | () => {}; 126 | } 127 | 128 | impl_type_hash_muncher!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); 129 | -------------------------------------------------------------------------------- /epserde/src/impls/vec.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Implementations for vectors. 11 | 12 | */ 13 | use crate::deser; 14 | use crate::deser::helpers::*; 15 | use crate::deser::*; 16 | use crate::ser; 17 | use crate::ser::helpers::*; 18 | use crate::ser::*; 19 | use crate::traits::*; 20 | use core::hash::Hash; 21 | 22 | #[cfg(all(feature = "alloc", not(feature = "std")))] 23 | use alloc::vec::Vec; 24 | 25 | impl CopyType for Vec { 26 | type Copy = Deep; 27 | } 28 | 29 | impl TypeHash for Vec { 30 | fn type_hash(hasher: &mut impl core::hash::Hasher) { 31 | "Vec".hash(hasher); 32 | T::type_hash(hasher); 33 | } 34 | } 35 | 36 | impl AlignHash for Vec { 37 | fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) { 38 | T::align_hash(hasher, &mut 0); 39 | } 40 | } 41 | 42 | impl SerializeInner for Vec 43 | where 44 | Vec: SerializeHelper<::Copy>, 45 | { 46 | type SerType = Self; 47 | const IS_ZERO_COPY: bool = false; 48 | const ZERO_COPY_MISMATCH: bool = false; 49 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 50 | SerializeHelper::_serialize_inner(self, backend) 51 | } 52 | } 53 | 54 | impl SerializeHelper for Vec { 55 | #[inline(always)] 56 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 57 | serialize_slice_zero(backend, self.as_slice()) 58 | } 59 | } 60 | 61 | impl SerializeHelper for Vec { 62 | #[inline(always)] 63 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> { 64 | serialize_slice_deep(backend, self.as_slice()) 65 | } 66 | } 67 | 68 | // This delegates to a private helper trait which we can specialize on in stable rust 69 | unsafe impl DeserializeInner for Vec 70 | where 71 | Vec: DeserializeHelper<::Copy, FullType = Vec>, 72 | { 73 | type DeserType<'a> = as DeserializeHelper<::Copy>>::DeserType<'a>; 74 | #[inline(always)] 75 | fn _deserialize_full_inner(backend: &mut impl ReadWithPos) -> deser::Result { 76 | as DeserializeHelper<::Copy>>::_deserialize_full_inner_impl(backend) 77 | } 78 | 79 | #[inline(always)] 80 | fn _deserialize_eps_inner<'a>( 81 | backend: &mut SliceWithPos<'a>, 82 | ) -> deser::Result< as DeserializeHelper<::Copy>>::DeserType<'a>> { 83 | as DeserializeHelper<::Copy>>::_deserialize_eps_inner_impl(backend) 84 | } 85 | } 86 | 87 | unsafe impl DeserializeHelper for Vec { 88 | type FullType = Self; 89 | type DeserType<'a> = &'a [T]; 90 | #[inline(always)] 91 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 92 | unsafe { deserialize_full_vec_zero(backend) } 93 | } 94 | #[inline(always)] 95 | fn _deserialize_eps_inner_impl<'a>( 96 | backend: &mut SliceWithPos<'a>, 97 | ) -> deser::Result<::DeserType<'a>> { 98 | unsafe { deserialize_eps_slice_zero(backend) } 99 | } 100 | } 101 | 102 | unsafe impl DeserializeHelper for Vec { 103 | type FullType = Self; 104 | type DeserType<'a> = Vec<::DeserType<'a>>; 105 | #[inline(always)] 106 | fn _deserialize_full_inner_impl(backend: &mut impl ReadWithPos) -> deser::Result { 107 | deserialize_full_vec_deep::(backend) 108 | } 109 | #[inline(always)] 110 | fn _deserialize_eps_inner_impl<'a>( 111 | backend: &mut SliceWithPos<'a>, 112 | ) -> deser::Result<::DeserType<'a>> { 113 | deserialize_eps_vec_deep::(backend) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /epserde/src/lib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | #![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] 9 | #![deny(unconditional_recursion)] 10 | #![cfg_attr(not(feature = "std"), no_std)] 11 | #[cfg(all(feature = "alloc", not(feature = "std")))] 12 | extern crate alloc; 13 | 14 | #[cfg(feature = "derive")] 15 | pub use epserde_derive::{Epserde, TypeInfo}; 16 | 17 | pub mod deser; 18 | pub mod impls; 19 | pub mod ser; 20 | pub mod traits; 21 | pub mod utils; 22 | 23 | pub mod prelude { 24 | pub use crate::deser; 25 | pub use crate::deser::DeserType; 26 | pub use crate::deser::Deserialize; 27 | pub use crate::deser::DeserializeHelper; 28 | pub use crate::deser::DeserializeInner; 29 | pub use crate::deser::Flags; 30 | pub use crate::deser::MemCase; 31 | pub use crate::deser::ReadWithPos; 32 | pub use crate::deser::SliceWithPos; 33 | pub use crate::impls::iter::SerIter; 34 | pub use crate::ser; 35 | pub use crate::ser::Serialize; 36 | pub use crate::ser::SerializeHelper; 37 | pub use crate::ser::SerializeInner; 38 | pub use crate::traits::*; 39 | pub use crate::utils::*; 40 | #[cfg(feature = "derive")] 41 | pub use epserde_derive::Epserde; 42 | } 43 | 44 | /// (Major, Minor) version of the file format, this follows semantic versioning 45 | pub const VERSION: (u16, u16) = (1, 1); 46 | 47 | /// Magic cookie, also used as endianess marker. 48 | pub const MAGIC: u64 = u64::from_ne_bytes(*b"epserde "); 49 | /// What we will read if the endianness is mismatched. 50 | pub const MAGIC_REV: u64 = u64::from_le_bytes(MAGIC.to_be_bytes()); 51 | 52 | /// Compute the padding needed for alignment, that is, the smallest 53 | /// number such that `((value + pad_align_to(value, align_to) & (align_to - 1) == 0`. 54 | pub fn pad_align_to(value: usize, align_to: usize) -> usize { 55 | value.wrapping_neg() & (align_to - 1) 56 | } 57 | 58 | #[test] 59 | 60 | fn test_pad_align_to() { 61 | assert_eq!(7 + pad_align_to(7, 8), 8); 62 | assert_eq!(8 + pad_align_to(8, 8), 8); 63 | assert_eq!(9 + pad_align_to(9, 8), 16); 64 | assert_eq!(36 + pad_align_to(36, 16), 48); 65 | } 66 | -------------------------------------------------------------------------------- /epserde/src/ser/helpers.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Helpers for serialization. 11 | 12 | */ 13 | 14 | use super::{SerializeInner, WriteWithNames}; 15 | use crate::ser; 16 | use crate::traits::*; 17 | 18 | pub fn check_zero_copy() { 19 | if !V::IS_ZERO_COPY { 20 | panic!( 21 | "Cannot serialize type {} declared as zero-copy as it is not zero-copy", 22 | core::any::type_name::() 23 | ); 24 | } 25 | } 26 | 27 | /// Serialize a zero-copy structure checking [that the type is actually 28 | /// zero-copy](SerializeInner::IS_ZERO_COPY) and [aligning the stream 29 | /// beforehand](WriteWithNames::align). 30 | /// 31 | /// This function makes the appropriate checks, write the necessary padding and 32 | /// then calls [`serialize_zero_unchecked`](serialize_zero_unchecked). 33 | pub fn serialize_zero( 34 | backend: &mut impl WriteWithNames, 35 | value: &V, 36 | ) -> ser::Result<()> { 37 | check_zero_copy::(); 38 | backend.align::()?; 39 | serialize_zero_unchecked(backend, value) 40 | } 41 | 42 | /// Serialize a zero-copy structure without checking [that the type is actually 43 | /// zero-copy](SerializeInner::IS_ZERO_COPY) and without [aligning the 44 | /// stream](WriteWithNames::align). 45 | /// 46 | /// Note that this method uses a single [`write_all`](std::io::Write::write_all) 47 | /// call to write the entire structure. 48 | #[inline(always)] 49 | pub fn serialize_zero_unchecked( 50 | backend: &mut impl WriteWithNames, 51 | value: &V, 52 | ) -> ser::Result<()> { 53 | let buffer = unsafe { 54 | core::slice::from_raw_parts(value as *const V as *const u8, core::mem::size_of::()) 55 | }; 56 | backend.write_bytes::(buffer) 57 | } 58 | 59 | /// Serialize a slice of zero-copy structures by encoding 60 | /// its length first, and then its bytes properly [aligned](WriteWithNames::align). 61 | /// 62 | /// Note that this method uses a single `write_all` 63 | /// call to write the entire slice. 64 | /// 65 | /// Here we check [that the type is actually zero-copy](SerializeInner::IS_ZERO_COPY). 66 | pub fn serialize_slice_zero( 67 | backend: &mut impl WriteWithNames, 68 | data: &[V], 69 | ) -> ser::Result<()> { 70 | check_zero_copy::(); 71 | 72 | let len = data.len(); 73 | backend.write("len", &len)?; 74 | let num_bytes = core::mem::size_of_val(data); 75 | let buffer = unsafe { core::slice::from_raw_parts(data.as_ptr() as *const u8, num_bytes) }; 76 | backend.align::()?; 77 | backend.write_bytes::(buffer) 78 | } 79 | 80 | pub fn check_mismatch() { 81 | if V::ZERO_COPY_MISMATCH { 82 | eprintln!("Type {} is zero-copy, but it has not declared as such; use the #[deep_copy] attribute to silence this warning", core::any::type_name::()); 83 | } 84 | } 85 | 86 | /// Serialize a slice of deep-copy structures by encoding 87 | /// its length first, and then the contents item by item. 88 | /// 89 | /// Here we warn [that the type might actually be zero-copy](SerializeInner::ZERO_COPY_MISMATCH). 90 | pub fn serialize_slice_deep( 91 | backend: &mut impl WriteWithNames, 92 | data: &[V], 93 | ) -> ser::Result<()> { 94 | check_mismatch::(); 95 | let len = data.len(); 96 | backend.write("len", &len)?; 97 | for item in data.iter() { 98 | backend.write("item", item)?; 99 | } 100 | Ok(()) 101 | } 102 | -------------------------------------------------------------------------------- /epserde/src/ser/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Serialization traits and types. 11 | 12 | [`Serialize`] is the main serialization trait, providing a 13 | [`Serialize::serialize`] method that serializes the type into a 14 | generic [`WriteNoStd`] backend, and a [`Serialize::serialize_with_schema`] method 15 | that additionally returns a [`Schema`] describing the data that has been written. 16 | The implementation of this trait 17 | is based on [`SerializeInner`], which is automatically derived 18 | with `#[derive(Serialize)]`. 19 | 20 | */ 21 | 22 | use crate::traits::*; 23 | use crate::*; 24 | 25 | use core::hash::Hasher; 26 | use std::{io::BufWriter, path::Path}; 27 | 28 | pub mod write_with_names; 29 | pub use write_with_names::*; 30 | pub mod helpers; 31 | pub use helpers::*; 32 | pub mod write; 33 | pub use write::*; 34 | 35 | pub type Result = core::result::Result; 36 | 37 | /// Main serialization trait. It is separated from [`SerializeInner`] to 38 | /// avoid that the user modify its behavior, and hide internal serialization 39 | /// methods. 40 | /// 41 | /// It provides a convenience method [`Serialize::store`] that serializes 42 | /// the type to a file. 43 | pub trait Serialize { 44 | /// Serialize the type using the given backend. 45 | fn serialize(&self, backend: &mut impl WriteNoStd) -> Result { 46 | let mut write_with_pos = WriterWithPos::new(backend); 47 | self.serialize_on_field_write(&mut write_with_pos)?; 48 | Ok(write_with_pos.pos()) 49 | } 50 | 51 | /// Serialize the type using the given backend and return a [schema](Schema) 52 | /// describing the data that has been written. 53 | /// 54 | /// This method is mainly useful for debugging and to check cross-language 55 | /// interoperability. 56 | fn serialize_with_schema(&self, backend: &mut impl WriteNoStd) -> Result { 57 | let mut writer_with_pos = WriterWithPos::new(backend); 58 | let mut schema_writer = SchemaWriter::new(&mut writer_with_pos); 59 | self.serialize_on_field_write(&mut schema_writer)?; 60 | Ok(schema_writer.schema) 61 | } 62 | 63 | /// Serialize the type using the given [`WriteWithNames`]. 64 | fn serialize_on_field_write(&self, backend: &mut impl WriteWithNames) -> Result<()>; 65 | 66 | /// Convenience method to serialize to a file. 67 | fn store(&self, path: impl AsRef) -> Result<()> { 68 | let file = std::fs::File::create(path).map_err(Error::FileOpenError)?; 69 | let mut buf_writer = BufWriter::new(file); 70 | self.serialize(&mut buf_writer)?; 71 | Ok(()) 72 | } 73 | } 74 | 75 | /// Inner trait to implement serialization of a type. This trait exists 76 | /// to separate the user-facing [`Serialize`] trait from the low-level 77 | /// serialization mechanism of [`SerializeInner::_serialize_inner`]. Moreover, 78 | /// it makes it possible to behave slighly differently at the top 79 | /// of the recursion tree (e.g., to write the endianness marker). 80 | /// 81 | /// The user should not implement this trait directly, but rather derive it. 82 | pub trait SerializeInner { 83 | /// This is the type that will be written in the header of the file, and 84 | /// thus the type that will be deserialized. In most cases it is `Self`, but 85 | /// in some cases, as for [references to slices](crate::impls::slice), 86 | /// it is customized. 87 | type SerType; 88 | /// Inner constant used by the derive macros to keep 89 | /// track recursively of whether the type 90 | /// satisfies the conditions for being zero-copy. It is checked 91 | /// at runtime against the trait implemented by the type, and 92 | /// if a [`ZeroCopy`] type has this constant set to `false` 93 | /// serialization will panic. 94 | const IS_ZERO_COPY: bool; 95 | 96 | /// Inner constant used by the derive macros to keep 97 | /// track of whether all fields of a type are zero-copy 98 | /// but neither the attribute `#[zero_copy]` nor the attribute `#[deep_copy]` 99 | /// was specified. It is checked at runtime, and if it is true 100 | /// a warning will be issued, as the type could be zero-copy, 101 | /// which would be more efficient. 102 | const ZERO_COPY_MISMATCH: bool; 103 | 104 | /// Serialize this structure using the given backend. 105 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> Result<()>; 106 | } 107 | 108 | /// Blanket implementation that prevents the user from overwriting the 109 | /// methods in [`Serialize`]. 110 | /// 111 | /// This implementation [writes a header](`write_header`) containing some hashes 112 | /// and debug information and then delegates to [WriteWithNames::write]. 113 | impl Serialize for T 114 | where 115 | ::SerType: TypeHash + AlignHash, 116 | { 117 | /// Serialize the type using the given [`WriteWithNames`]. 118 | fn serialize_on_field_write(&self, backend: &mut impl WriteWithNames) -> Result<()> { 119 | // write the header using the serialized type, not the type itself 120 | // this is done so that you can serialize types with reference to slices 121 | // that can then be deserialized as vectors. 122 | write_header::<::SerType>(backend)?; 123 | backend.write("ROOT", self)?; 124 | backend.flush() 125 | } 126 | } 127 | 128 | /// Write the header. 129 | /// 130 | /// Must be kept in sync with [`crate::deser::check_header`]. 131 | pub fn write_header(backend: &mut impl WriteWithNames) -> Result<()> { 132 | backend.write("MAGIC", &MAGIC)?; 133 | backend.write("VERSION_MAJOR", &VERSION.0)?; 134 | backend.write("VERSION_MINOR", &VERSION.1)?; 135 | backend.write("USIZE_SIZE", &(core::mem::size_of::() as u8))?; 136 | 137 | let mut type_hasher = xxhash_rust::xxh3::Xxh3::new(); 138 | T::type_hash(&mut type_hasher); 139 | 140 | let mut align_hasher = xxhash_rust::xxh3::Xxh3::new(); 141 | let mut offset_of = 0; 142 | T::align_hash(&mut align_hasher, &mut offset_of); 143 | 144 | backend.write("TYPE_HASH", &type_hasher.finish())?; 145 | backend.write("REPR_HASH", &align_hasher.finish())?; 146 | backend.write("TYPE_NAME", &core::any::type_name::().to_string()) 147 | } 148 | 149 | /// A helper trait that makes it possible to implement differently 150 | /// serialization for [`crate::traits::ZeroCopy`] and [`crate::traits::DeepCopy`] types. 151 | /// See [`crate::traits::CopyType`] for more information. 152 | pub trait SerializeHelper { 153 | fn _serialize_inner(&self, backend: &mut impl WriteWithNames) -> Result<()>; 154 | } 155 | 156 | #[derive(Debug)] 157 | /// Errors that can happen during serialization. 158 | pub enum Error { 159 | /// The underlying writer returned an error. 160 | WriteError, 161 | /// [`Serialize::store`] could not open the provided file. 162 | FileOpenError(std::io::Error), 163 | /// The declared length of an iterator did not match 164 | /// the actual length. 165 | IteratorLengthMismatch { actual: usize, expected: usize }, 166 | } 167 | 168 | impl std::error::Error for Error {} 169 | 170 | impl core::fmt::Display for Error { 171 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 172 | match self { 173 | Self::WriteError => write!(f, "Write error during ε-serde serialization"), 174 | Self::FileOpenError(error) => { 175 | write!( 176 | f, 177 | "Error opening file during ε-serde serialization: {}", 178 | error 179 | ) 180 | } 181 | Self::IteratorLengthMismatch { actual, expected } => write!( 182 | f, 183 | "Iterator length mismatch during ε-serde serialization: expected {} items, got {}", 184 | expected, actual 185 | ), 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /epserde/src/ser/write.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | No-std support for writing while keeping track of the current position. 11 | 12 | */ 13 | 14 | use crate::prelude::*; 15 | use mem_dbg::{MemDbg, MemSize}; 16 | 17 | /// [`std::io::Write`]-like trait for serialization that does not 18 | /// depend on [`std`]. 19 | /// 20 | /// In an [`std`] context, the user does not need to use directly 21 | /// this trait as we provide a blanket 22 | /// implementation that implements [`WriteNoStd`] for all types that implement 23 | /// [`std::io::Write`]. In particular, in such a context you can use [`std::io::Cursor`] 24 | /// for in-memory serialization. 25 | pub trait WriteNoStd { 26 | /// Write some bytes. 27 | fn write_all(&mut self, buf: &[u8]) -> ser::Result<()>; 28 | 29 | /// Flush all changes to the underlying storage if applicable. 30 | fn flush(&mut self) -> ser::Result<()>; 31 | } 32 | 33 | #[cfg(feature = "std")] 34 | use std::io::Write; 35 | 36 | #[cfg(feature = "std")] 37 | impl WriteNoStd for W { 38 | #[inline(always)] 39 | fn write_all(&mut self, buf: &[u8]) -> ser::Result<()> { 40 | Write::write_all(self, buf).map_err(|_| ser::Error::WriteError) 41 | } 42 | #[inline(always)] 43 | fn flush(&mut self) -> ser::Result<()> { 44 | Write::flush(self).map_err(|_| ser::Error::WriteError) 45 | } 46 | } 47 | 48 | /// A trait for [`WriteNoStd`] that also keeps track of the current position. 49 | /// 50 | /// This is needed because the [`Write`] trait doesn't have a `seek` method and 51 | /// [`std::io::Seek`] would be a requirement much stronger than needed. 52 | pub trait WriteWithPos: WriteNoStd { 53 | fn pos(&self) -> usize; 54 | } 55 | 56 | /// A wrapper for a [`WriteNoStd`] that implements [`WriteWithPos`] 57 | /// by keeping track of the current position. 58 | #[derive(Debug, MemDbg, MemSize)] 59 | pub struct WriterWithPos<'a, F: WriteNoStd> { 60 | /// What we actually write on. 61 | backend: &'a mut F, 62 | /// How many bytes we have written from the start. 63 | pos: usize, 64 | } 65 | 66 | impl<'a, F: WriteNoStd> WriterWithPos<'a, F> { 67 | #[inline(always)] 68 | /// Create a new [`WriterWithPos`] on top of a generic [`WriteNoStd`] `F`. 69 | pub fn new(backend: &'a mut F) -> Self { 70 | Self { backend, pos: 0 } 71 | } 72 | } 73 | 74 | impl WriteNoStd for WriterWithPos<'_, F> { 75 | #[inline(always)] 76 | fn write_all(&mut self, buf: &[u8]) -> ser::Result<()> { 77 | self.backend.write_all(buf)?; 78 | self.pos += buf.len(); 79 | Ok(()) 80 | } 81 | 82 | #[inline(always)] 83 | fn flush(&mut self) -> ser::Result<()> { 84 | self.backend.flush() 85 | } 86 | } 87 | 88 | impl WriteWithPos for WriterWithPos<'_, F> { 89 | #[inline(always)] 90 | fn pos(&self) -> usize { 91 | self.pos 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /epserde/src/ser/write_with_names.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Traits and implementations to write named field during serialization. 11 | 12 | [`SerializeInner::_serialize_inner`] writes on a [`WriteWithNames`], rather 13 | than on a [`WriteWithPos`], with the purpose of easily recording write 14 | events happening during a serialization. 15 | 16 | */ 17 | 18 | use super::*; 19 | use mem_dbg::{MemDbg, MemSize}; 20 | 21 | /// Trait extending [`WriteWithPos`] with methods providing 22 | /// alignment, serialization of named data, and writing of byte slices 23 | /// of zero-copy types. 24 | /// 25 | /// The purpose of this trait is that of interposing between [`SerializeInner`] 26 | /// and the underlying [`WriteWithPos`] a layer in which serialization operations 27 | /// can be easily intercepted and recorded. In particular, serialization methods 28 | /// must use the methods of this trait if they want to record the schema of the 29 | /// serialized data; this is true (maybe counterintuitively) even of ancillary 30 | /// data such as tags and slice lengths: see [`helpers`] or the 31 | /// [implementation of `Option`](impls::prim) for examples. 32 | /// All methods have a default 33 | /// implementation that must be replicated in other implementations. 34 | /// 35 | /// There are two implementations of [`WriteWithNames`]: [`WriterWithPos`], 36 | /// which uses the default implementation, and [`SchemaWriter`], 37 | /// which additionally records a [`Schema`] of the serialized data. 38 | pub trait WriteWithNames: WriteWithPos + Sized { 39 | /// Add some zero padding so that `self.pos() % V:max_size_of() == 0.` 40 | /// 41 | /// Other implementations must write the same number of zeros. 42 | fn align(&mut self) -> Result<()> { 43 | let padding = pad_align_to(self.pos(), V::max_size_of()); 44 | for _ in 0..padding { 45 | self.write_all(&[0])?; 46 | } 47 | Ok(()) 48 | } 49 | 50 | /// Write a value with an associated name. 51 | /// 52 | /// The default implementation simply delegates to [`SerializeInner::_serialize_inner`]. 53 | /// Other implementations might use the name information (e.g., [`SchemaWriter`]), 54 | /// but they must in the end delegate to [`SerializeInner::_serialize_inner`]. 55 | fn write(&mut self, _field_name: &str, value: &V) -> Result<()> { 56 | value._serialize_inner(self) 57 | } 58 | 59 | /// Write the memory representation of a (slice of a) zero-copy type. 60 | /// 61 | /// The default implementation simply delegates to [`WriteNoStd::write_all`]. 62 | /// Other implementations might use the type information in `V` (e.g., [`SchemaWriter`]), 63 | /// but they must in the end delegate to [`WriteNoStd::write_all`]. 64 | fn write_bytes(&mut self, value: &[u8]) -> Result<()> { 65 | self.write_all(value) 66 | } 67 | } 68 | 69 | impl WriteWithNames for WriterWithPos<'_, F> {} 70 | 71 | /// Information about data written during serialization, either fields or 72 | /// ancillary data such as option tags and slice lengths. 73 | #[derive(Debug, Clone, MemDbg, MemSize)] 74 | pub struct SchemaRow { 75 | /// Name of the piece of data. 76 | pub field: String, 77 | /// Type of the piece of data. 78 | pub ty: String, 79 | /// Offset from the start of the file. 80 | pub offset: usize, 81 | /// Length in bytes of the piece of data. 82 | pub size: usize, 83 | /// The alignment needed by the piece of data, zero if not applicable 84 | /// (e.g., primitive fields, ancillary data, or structures). 85 | pub align: usize, 86 | } 87 | 88 | #[derive(Default, Debug, Clone, MemDbg, MemSize)] 89 | /// A vector containing all the fields written during serialization, including 90 | /// ancillary data such as slice lengths and [`Option`] tags. 91 | pub struct Schema(pub Vec); 92 | 93 | impl Schema { 94 | /// Return a CSV representation of the schema, including data. 95 | /// 96 | /// WARNING: the size of the CSV will be larger than the size of the 97 | /// serialized file, so it is not a good idea to call this method 98 | /// on big structures. 99 | pub fn debug(&self, data: &[u8]) -> String { 100 | let mut result = "field,offset,align,size,ty,bytes\n".to_string(); 101 | for i in 0..self.0.len().saturating_sub(1) { 102 | let row = &self.0[i]; 103 | // if it's a composed type, don't print the bytes 104 | if row.offset == self.0[i + 1].offset { 105 | result.push_str(&format!( 106 | "{},{},{},{},{},\n", 107 | row.field, row.offset, row.align, row.size, row.ty, 108 | )); 109 | } else { 110 | result.push_str(&format!( 111 | "{},{},{},{},{},{:02x?}\n", 112 | row.field, 113 | row.offset, 114 | row.align, 115 | row.size, 116 | row.ty, 117 | &data[row.offset..row.offset + row.size], 118 | )); 119 | } 120 | } 121 | 122 | // the last field can't be a composed type by definition 123 | if let Some(row) = self.0.last() { 124 | result.push_str(&format!( 125 | "{},{},{},{},{},{:02x?}\n", 126 | row.field, 127 | row.offset, 128 | row.align, 129 | row.size, 130 | row.ty, 131 | &data[row.offset..row.offset + row.size], 132 | )); 133 | } 134 | 135 | result 136 | } 137 | 138 | /// Return a CSV representation of the schema, excluding data. 139 | pub fn to_csv(&self) -> String { 140 | let mut result = "field,offset,align,size,ty\n".to_string(); 141 | for row in &self.0 { 142 | result.push_str(&format!( 143 | "{},{},{},{},{}\n", 144 | row.field, row.offset, row.align, row.size, row.ty 145 | )); 146 | } 147 | result 148 | } 149 | } 150 | 151 | /// A [`WriteWithNames`] that keeps track of the data written on an underlying 152 | /// [`WriteWithPos`] in a [`Schema`]. 153 | #[derive(Debug, MemDbg, MemSize)] 154 | pub struct SchemaWriter<'a, W> { 155 | /// The schema so far. 156 | pub schema: Schema, 157 | /// A recursively-built sequence of previous names. 158 | path: Vec, 159 | /// What we actually write on. 160 | writer: &'a mut W, 161 | } 162 | 163 | impl<'a, W: WriteWithPos> SchemaWriter<'a, W> { 164 | #[inline(always)] 165 | /// Create a new empty [`SchemaWriter`] on top of a generic writer `W`. 166 | pub fn new(backend: &'a mut W) -> Self { 167 | Self { 168 | schema: Default::default(), 169 | path: vec![], 170 | writer: backend, 171 | } 172 | } 173 | } 174 | impl WriteNoStd for SchemaWriter<'_, W> { 175 | fn write_all(&mut self, buf: &[u8]) -> ser::Result<()> { 176 | self.writer.write_all(buf) 177 | } 178 | 179 | fn flush(&mut self) -> ser::Result<()> { 180 | self.writer.flush() 181 | } 182 | } 183 | 184 | impl WriteWithPos for SchemaWriter<'_, W> { 185 | fn pos(&self) -> usize { 186 | self.writer.pos() 187 | } 188 | } 189 | 190 | /// WARNING: these implementations must be kept in sync with the ones 191 | /// in the default implementation of [`WriteWithNames`]. 192 | impl WriteWithNames for SchemaWriter<'_, W> { 193 | #[inline(always)] 194 | fn align(&mut self) -> Result<()> { 195 | let padding = pad_align_to(self.pos(), T::max_size_of()); 196 | if padding != 0 { 197 | self.schema.0.push(SchemaRow { 198 | field: "PADDING".into(), 199 | ty: format!("[u8; {}]", padding), 200 | offset: self.pos(), 201 | size: padding, 202 | align: 1, 203 | }); 204 | for _ in 0..padding { 205 | self.write_all(&[0])?; 206 | } 207 | } 208 | 209 | Ok(()) 210 | } 211 | 212 | #[inline(always)] 213 | fn write(&mut self, field_name: &str, value: &V) -> Result<()> { 214 | // prepare a row with the field name and the type 215 | self.path.push(field_name.into()); 216 | let pos = self.pos(); 217 | 218 | let len = self.schema.0.len(); 219 | value._serialize_inner(self)?; 220 | 221 | // This is slightly inefficient because we have to shift 222 | // the whole vector, but it's not a big deal and it keeps 223 | // the schema in the correct order. 224 | self.schema.0.insert( 225 | len, 226 | SchemaRow { 227 | field: self.path.join("."), 228 | ty: core::any::type_name::().to_string(), 229 | offset: pos, 230 | align: 0, 231 | size: self.pos() - pos, 232 | }, 233 | ); 234 | self.path.pop(); 235 | Ok(()) 236 | } 237 | 238 | #[inline(always)] 239 | fn write_bytes(&mut self, value: &[u8]) -> Result<()> { 240 | self.path.push("zero".to_string()); 241 | // Note that we are writing the schema row of the field before 242 | // having written its content. 243 | self.schema.0.push(SchemaRow { 244 | field: self.path.join("."), 245 | ty: core::any::type_name::().to_string(), 246 | offset: self.pos(), 247 | size: value.len(), 248 | align: V::max_size_of(), 249 | }); 250 | self.path.pop(); 251 | 252 | self.write_all(value) 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /epserde/src/traits/copy_type.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Traits to mark types as zero-copy or deep-copy. 11 | 12 | */ 13 | 14 | use crate::prelude::MaxSizeOf; 15 | use sealed::sealed; 16 | 17 | /// Internal trait used to select whether a type is zero-copy 18 | /// or deep-copy. 19 | /// 20 | /// It has only two implementations, [`Zero`] and [`Deep`]. 21 | /// 22 | /// In the first case, the type can be serialized 23 | /// from memory and deserialized to memory as a sequence of bytes; 24 | /// in the second case, one has to deserialize the type field 25 | /// by field. 26 | #[sealed] 27 | pub trait CopySelector { 28 | const IS_ZERO_COPY: bool; 29 | } 30 | /// An implementation of a [`CopySelector`] specifying that a type is zero-copy. 31 | pub struct Zero {} 32 | 33 | #[sealed] 34 | impl CopySelector for Zero { 35 | const IS_ZERO_COPY: bool = true; 36 | } 37 | 38 | /// An implementation of a [`CopySelector`] specifying that a type is deep-copy. 39 | pub struct Deep {} 40 | 41 | #[sealed] 42 | impl CopySelector for Deep { 43 | const IS_ZERO_COPY: bool = false; 44 | } 45 | 46 | /** 47 | 48 | Marker trait for data specifying whether it is zero-copy or deep-copy. 49 | 50 | The trait comes in two flavors: `CopySelector` and 51 | `CopySelector`. To each of these flavors corresponds two 52 | dependent traits, [`ZeroCopy`] (which requires implementing [`MaxSizeOf`]) 53 | and [`DeepCopy`], which are automatically 54 | implemented. 55 | ```rust 56 | use epserde::traits::*; 57 | 58 | struct MyType {} 59 | 60 | impl CopyType for MyType { 61 | type Copy = Deep; 62 | } 63 | // Now MyType implements DeepCopy 64 | ``` 65 | You should not implement this trait manually, but rather use the provided [derive macro](epserde_derive::Epserde). 66 | 67 | We use this trait to implement a different behavior for [`ZeroCopy`] and [`DeepCopy`] types, 68 | in particular on arrays, vectors, and boxed slices, 69 | [working around the bug that prevents the compiler from understanding that implementations 70 | for the two flavors of `CopySelector` are mutually 71 | exclusive](https://github.com/rust-lang/rfcs/pull/1672#issuecomment-1405377983). 72 | 73 | For an array of elements of type `T` to be zero-copy serializable and 74 | deserializable, `T` must implement `CopySelector`. The conditions for this marker trait are that 75 | `T` is a [copy type](Copy), that it has a fixed memory layout, 76 | and that it does not contain any reference (in particular, that it has `'static` lifetime). 77 | If this happen vectors of `T` or boxed slices of `T` can be ε-copy deserialized 78 | using a reference to a slice of `T`. 79 | 80 | You can make zero-copy your own types, but you must ensure that they do not 81 | contain references and that they have a fixed memory layout; for structures, this requires 82 | `repr(C)`. ε-serde will track these conditions at compile time and check them at 83 | runtime: in case of failure, serialization will panic. 84 | 85 | Since we cannot use negative trait bounds, every type that is used as a parameter of 86 | an array, vector or boxed slice must implement either `CopySelector` 87 | or `CopySelector`. In the latter 88 | case, slices will be deserialized element by element, and the result will be a fully 89 | deserialized vector or boxed 90 | slice. If you do not implement either of these traits, the type will not be serializable inside 91 | vectors or boxed slices but error messages will be very unhelpful due to the 92 | contrived way we have to implement mutually exclusive types. 93 | 94 | If you use the provided derive macros all this logic will be hidden from you. You'll 95 | just have to add `#[zero_copy]` to your structures (if you want them to be zero-copy) 96 | and ε-serde will do the rest. 97 | 98 | */ 99 | pub trait CopyType: Sized { 100 | type Copy: CopySelector; 101 | } 102 | 103 | /// Marker trait for zero-copy types. You should never implement 104 | /// this trait directly, but rather implement [`CopyType`] with `Copy=Zero`. 105 | pub trait ZeroCopy: CopyType + Copy + MaxSizeOf + 'static {} 106 | impl + Copy + MaxSizeOf + 'static> ZeroCopy for T {} 107 | 108 | /// Marker trait for deep-copy types. You should never implement 109 | /// this trait directly, but rather implement [`CopyType`] with `Copy=Deep`. 110 | pub trait DeepCopy: CopyType {} 111 | impl> DeepCopy for T {} 112 | -------------------------------------------------------------------------------- /epserde/src/traits/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Basic traits that must be implemented by all types using ε-serde. 11 | 12 | If you use the procedural macro [`Epserde`](epserde_derive::Epserde), you do not 13 | need to worry about these traits—they will be implemented for you. 14 | 15 | */ 16 | 17 | pub mod type_info; 18 | pub use type_info::*; 19 | 20 | pub mod copy_type; 21 | pub use copy_type::*; 22 | -------------------------------------------------------------------------------- /epserde/src/traits/type_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 4 | * 5 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 6 | */ 7 | 8 | /*! 9 | 10 | Traits computing information about a type. 11 | 12 | */ 13 | 14 | use crate::pad_align_to; 15 | use core::hash::Hash; 16 | 17 | use super::ZeroCopy; 18 | 19 | /// Recursively compute a type hash for a type. 20 | /// 21 | /// [`TypeHash::type_hash`] is a recursive function that computes information 22 | /// about a type. It is used to check that the type of the data being 23 | /// deserialized matches syntactically the type of the data that was written. 24 | /// 25 | /// The type hasher should store information about the name and the type of the 26 | /// fields of a type, and the name of the type itself. 27 | pub trait TypeHash { 28 | /// Accumulate type information in `hasher`. 29 | fn type_hash(hasher: &mut impl core::hash::Hasher); 30 | 31 | /// Call [`TypeHash::type_hash`] on a value. 32 | fn type_hash_val(&self, hasher: &mut impl core::hash::Hasher) { 33 | Self::type_hash(hasher); 34 | } 35 | } 36 | 37 | /// Recursively compute an alignment hash for a type. 38 | /// 39 | /// [`AlignHash::align_hash`] is a recursive function that computes alignment 40 | /// information about zero-copy types. It is used to check that the alignment 41 | /// (and thus padding) of data that is zero-copied matches the alignment at 42 | /// serialization time. 43 | /// 44 | /// More precisely, at each call a zero-copy type looks at `offset_of`, assuming 45 | /// that the type is stored at that offset in the structure, hashes in the 46 | /// padding necessary to make `offset_of` a multiple of [`core::mem::align_of`] 47 | /// the type, hashes in the type size, and finally increases `offset_of` by 48 | /// [`core::mem::size_of`] the type. 49 | /// 50 | /// All deep-copy types must implement [`AlignHash`] by calling the [`AlignHash`] 51 | /// implementations of their fields with offset argument `&mut 0` (or a mutable 52 | /// reference to a variable initialized to 0). 53 | /// 54 | /// If the fields have no alignement requirements (e.g., all types of strings), 55 | /// the implementation can be a no-op. 56 | pub trait AlignHash { 57 | /// Accumulate alignment information in `hasher` assuming to be positioned 58 | /// at `offset_of`. 59 | fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize); 60 | 61 | /// Call [`AlignHash::align_hash`] on a value. 62 | fn align_hash_val(&self, hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) { 63 | Self::align_hash(hasher, offset_of); 64 | } 65 | } 66 | 67 | /// A function providing a reasonable default 68 | /// implementation of [`AlignHash::align_hash`] for basic sized types. 69 | pub(crate) fn std_align_hash( 70 | hasher: &mut impl core::hash::Hasher, 71 | offset_of: &mut usize, 72 | ) { 73 | let padding = pad_align_to(*offset_of, core::mem::align_of::()); 74 | padding.hash(hasher); 75 | core::mem::size_of::().hash(hasher); 76 | *offset_of += padding; 77 | *offset_of += core::mem::size_of::(); 78 | } 79 | 80 | /// A trait providing the maximum size of a primitive field in a type maximized 81 | /// with [`core::mem::align_of`]. 82 | /// 83 | /// We use the value returned by [`MaxSizeOf::max_size_of`] to generate padding 84 | /// before storing a zero-copy type. Note that this is different from the 85 | /// padding used to align the same type inside a struct, which is not under our 86 | /// control and is given by [`core::mem::align_of`]. 87 | /// 88 | /// In this way we increase interoperability between architectures with 89 | /// different alignment requirements for the same types (e.g., 4 or 8 bytes for 90 | /// `u64`). 91 | /// 92 | /// By maximizing with [`core::mem::align_of`] we ensure that we provide 93 | /// sufficient alignment in case the attribute `repr(align(N))` was specified. 94 | /// 95 | /// Deep-copy types do not need to implement [`MaxSizeOf`]. 96 | pub trait MaxSizeOf: Sized { 97 | fn max_size_of() -> usize; 98 | } 99 | -------------------------------------------------------------------------------- /epserde/src/utils/aligned_cursor.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use core::slice; 8 | use std::io::{Read, Seek, SeekFrom, Write}; 9 | 10 | use maligned::{Alignment, A16}; 11 | use mem_dbg::{MemDbg, MemSize}; 12 | 13 | /// An aligned version of [`Cursor`](std::io::Cursor). 14 | /// 15 | /// The standard library implementation of a [cursor](std::io::Cursor) is not 16 | /// aligned, and thus cannot be used to create examples or unit tests for 17 | /// ε-serde. This version has a [settable alignment](maligned::Alignment) that 18 | /// is guaranteed to be respected by the underlying storage. 19 | /// 20 | /// Note that length and position are stored as `usize` values, so the maximum 21 | /// length and position are `usize::MAX`. This is different from 22 | /// [`Cursor`](std::io::Cursor), which uses a `u64`. 23 | #[derive(Debug, Clone, MemDbg, MemSize)] 24 | pub struct AlignedCursor { 25 | vec: Vec, 26 | pos: usize, 27 | len: usize, 28 | } 29 | 30 | impl AlignedCursor { 31 | /// Return a new empty [`AlignedCursor`]. 32 | pub fn new() -> Self { 33 | Self { 34 | vec: Vec::new(), 35 | pos: 0, 36 | len: 0, 37 | } 38 | } 39 | 40 | /// Return a new empty [`AlignedCursor`] with a specified capacity. 41 | pub fn with_capacity(capacity: usize) -> Self { 42 | Self { 43 | vec: Vec::with_capacity(capacity.div_ceil(std::mem::size_of::())), 44 | pos: 0, 45 | len: 0, 46 | } 47 | } 48 | 49 | /// Consume this cursor, returning the underlying storage and the length of 50 | /// the data in bytes. 51 | pub fn into_parts(self) -> (Vec, usize) { 52 | (self.vec, self.len) 53 | } 54 | 55 | /// Return a reference to the underlying storage as bytes. 56 | /// 57 | /// Only the first [len](AlignedCursor::len) bytes are valid. 58 | /// 59 | /// Note that the reference is always to the whole storage, 60 | /// independently of the current [position](AlignedCursor::position). 61 | pub fn as_bytes(&mut self) -> &[u8] { 62 | let ptr = self.vec.as_mut_ptr() as *mut u8; 63 | unsafe { slice::from_raw_parts(ptr, self.len) } 64 | } 65 | 66 | /// Return a mutable reference to the underlying storage as bytes. 67 | /// 68 | /// Only the first [len](AlignedCursor::len) bytes are valid. 69 | /// 70 | /// Note that the reference is always to the whole storage, 71 | /// independently of the current [position](AlignedCursor::position). 72 | pub fn as_bytes_mut(&mut self) -> &mut [u8] { 73 | let ptr = self.vec.as_mut_ptr() as *mut u8; 74 | unsafe { slice::from_raw_parts_mut(ptr, self.len) } 75 | } 76 | 77 | /// Return the length in bytes of the data in this cursor. 78 | pub fn len(&self) -> usize { 79 | self.len 80 | } 81 | 82 | /// Return whether this cursor contains no data. 83 | pub fn is_empty(&self) -> bool { 84 | self.len == 0 85 | } 86 | 87 | /// Return the current position of this cursor. 88 | pub fn position(&self) -> usize { 89 | self.pos 90 | } 91 | 92 | /// Set the current position of this cursor. 93 | /// 94 | /// Valid positions are all `usize` values. 95 | pub fn set_position(&mut self, pos: usize) { 96 | self.pos = pos; 97 | } 98 | } 99 | 100 | impl Default for AlignedCursor { 101 | fn default() -> Self { 102 | Self::new() 103 | } 104 | } 105 | 106 | impl Read for AlignedCursor { 107 | fn read(&mut self, buf: &mut [u8]) -> std::io::Result { 108 | if self.pos >= self.len { 109 | return Ok(0); 110 | } 111 | let pos = self.pos; 112 | let rem = self.len - pos; 113 | let to_copy = std::cmp::min(buf.len(), rem) as usize; 114 | buf[..to_copy].copy_from_slice(&self.as_bytes()[pos..pos + to_copy]); 115 | self.pos += to_copy; 116 | Ok(to_copy) 117 | } 118 | } 119 | 120 | impl Write for AlignedCursor { 121 | fn write(&mut self, buf: &[u8]) -> std::io::Result { 122 | let len = buf.len().min(usize::MAX - self.pos); 123 | if !buf.is_empty() && len == 0 { 124 | return Err(std::io::Error::new( 125 | std::io::ErrorKind::InvalidInput, 126 | "write operation overflows usize::MAX length limit", 127 | )); 128 | } 129 | 130 | let cap = self.vec.len().saturating_mul(std::mem::size_of::()); 131 | let rem = cap - self.pos; 132 | if rem < len { 133 | self.vec.resize( 134 | (self.pos + len).div_ceil(std::mem::size_of::()), 135 | T::default(), 136 | ); 137 | } 138 | 139 | let pos = self.pos; 140 | 141 | // SAFETY: we now have enough space in the vec. 142 | let bytes = unsafe { 143 | slice::from_raw_parts_mut( 144 | self.vec.as_mut_ptr() as *mut u8, 145 | self.vec.len() * std::mem::size_of::(), 146 | ) 147 | }; 148 | bytes[pos..pos + len].copy_from_slice(buf); 149 | self.pos += len; 150 | self.len = self.len.max(self.pos); 151 | Ok(len) 152 | } 153 | 154 | fn flush(&mut self) -> std::io::Result<()> { 155 | Ok(()) 156 | } 157 | } 158 | 159 | impl Seek for AlignedCursor { 160 | fn seek(&mut self, style: SeekFrom) -> std::io::Result { 161 | let (base_pos, offset) = match style { 162 | SeekFrom::Start(n) if n > usize::MAX as u64 => { 163 | return Err(std::io::Error::new( 164 | std::io::ErrorKind::InvalidInput, 165 | "cursor length would be greater than usize::MAX", 166 | )) 167 | } 168 | SeekFrom::Start(n) => { 169 | self.pos = n as usize; 170 | return Ok(self.pos as u64); 171 | } 172 | SeekFrom::End(n) => (self.len as u64, n), 173 | SeekFrom::Current(n) => (self.pos as u64, n), 174 | }; 175 | 176 | match base_pos.checked_add_signed(offset) { 177 | Some(n) if n <= usize::MAX as u64 => { 178 | self.pos = n as usize; 179 | Ok(n) 180 | } 181 | _ => Err(std::io::Error::new( 182 | std::io::ErrorKind::InvalidInput, 183 | "invalid seek to a negative or overflowing position", 184 | )), 185 | } 186 | } 187 | 188 | fn stream_position(&mut self) -> std::io::Result { 189 | Ok(self.pos as u64) 190 | } 191 | } 192 | 193 | #[cfg(test)] 194 | mod tests { 195 | use std::error::Error; 196 | 197 | use super::*; 198 | 199 | #[test] 200 | fn test_aligned_cursor() -> Result<(), Box> { 201 | let mut cursor = AlignedCursor::::new(); 202 | for i in 0_usize..1000 { 203 | cursor.write_all(&i.to_ne_bytes()).unwrap(); 204 | } 205 | 206 | for i in (0..1000).rev() { 207 | let mut buf = [0; std::mem::size_of::()]; 208 | cursor.set_position(i * std::mem::size_of::()); 209 | cursor.read_exact(&mut buf).unwrap(); 210 | assert_eq!(i.to_ne_bytes(), buf); 211 | } 212 | 213 | for i in (0..1000).rev() { 214 | let mut buf = [0; std::mem::size_of::()]; 215 | let pos = cursor.seek(SeekFrom::Start(i * std::mem::size_of::() as u64))?; 216 | assert_eq!(pos, cursor.position() as u64); 217 | cursor.read_exact(&mut buf).unwrap(); 218 | assert_eq!(i.to_ne_bytes(), buf); 219 | } 220 | 221 | for i in (0..1000).rev() { 222 | let mut buf = [0; std::mem::size_of::()]; 223 | let pos = cursor.seek(SeekFrom::End( 224 | (-i - 1) * std::mem::size_of::() as i64, 225 | ))?; 226 | assert_eq!(pos, cursor.position() as u64); 227 | cursor.read_exact(&mut buf).unwrap(); 228 | assert_eq!((999 - i).to_ne_bytes(), buf); 229 | } 230 | 231 | cursor.set_position(0); 232 | 233 | for i in 0_usize..500 { 234 | let mut buf = [0; std::mem::size_of::()]; 235 | let pos = cursor.seek(SeekFrom::Current(std::mem::size_of::() as i64))?; 236 | assert_eq!(pos, cursor.position() as u64); 237 | cursor.read_exact(&mut buf).unwrap(); 238 | assert_eq!((i * 2 + 1).to_ne_bytes(), buf); 239 | } 240 | 241 | assert!(cursor 242 | .seek(SeekFrom::End(-1001 * std::mem::size_of::() as i64,)) 243 | .is_err()); 244 | 245 | Ok(()) 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /epserde/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Sebastiano Vigna 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | mod aligned_cursor; 8 | pub use aligned_cursor::AlignedCursor; 9 | -------------------------------------------------------------------------------- /epserde/tests/test_bad_deser.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use core::hash::Hasher; 8 | use epserde::prelude::*; 9 | use epserde::*; 10 | use maligned::A16; 11 | use xxhash_rust::xxh3::Xxh3; 12 | 13 | #[test] 14 | fn test_wrong_endianess() { 15 | let data = 1337_usize; 16 | 17 | let mut cursor = >::new(); 18 | 19 | let schema = data.serialize_with_schema(&mut cursor).unwrap(); 20 | println!("{}", schema.debug(cursor.as_bytes())); 21 | println!("{:02x?}", cursor.as_bytes()); 22 | 23 | // set the reversed endianess 24 | cursor.as_bytes_mut()[0..8].copy_from_slice(&MAGIC_REV.to_ne_bytes()); 25 | 26 | let err = ::deserialize_full(&mut std::io::Cursor::new(cursor.as_bytes())); 27 | assert!(err.is_err()); 28 | assert!(matches!(err.unwrap_err(), deser::Error::EndiannessError)); 29 | 30 | let err = ::deserialize_eps(cursor.as_bytes()); 31 | assert!(err.is_err()); 32 | assert!(matches!(err.unwrap_err(), deser::Error::EndiannessError)); 33 | 34 | // set a wrong magic cookie 35 | let bad_magic: u64 = 0x8989898989898989; 36 | cursor.as_bytes_mut()[0..8].copy_from_slice(&bad_magic.to_ne_bytes()); 37 | 38 | let err = ::deserialize_full(&mut std::io::Cursor::new(cursor.as_bytes())); 39 | if let Err(deser::Error::MagicCookieError(bad_magic_read)) = err { 40 | assert_eq!(bad_magic_read, bad_magic); 41 | } else { 42 | panic!("wrong error type: {:?}", err); 43 | } 44 | 45 | let err = ::deserialize_eps(cursor.as_bytes()); 46 | if let Err(deser::Error::MagicCookieError(bad_magic_read)) = err { 47 | assert_eq!(bad_magic_read, bad_magic); 48 | } else { 49 | panic!("wrong error type: {:?}", err); 50 | } 51 | // reset the magic, but set a wrong version 52 | cursor.as_bytes_mut()[0..8].copy_from_slice(&MAGIC.to_ne_bytes()); 53 | let bad_version: u16 = 0xffff; 54 | cursor.as_bytes_mut()[8..10].copy_from_slice(&bad_version.to_ne_bytes()); 55 | 56 | let err = ::deserialize_full(&mut std::io::Cursor::new(cursor.as_bytes())); 57 | if let Err(deser::Error::MajorVersionMismatch(bad_version_read)) = err { 58 | assert_eq!(bad_version_read, bad_version); 59 | } else { 60 | panic!("wrong error type: {:?}", err); 61 | } 62 | 63 | let err = ::deserialize_eps(cursor.as_bytes()); 64 | if let Err(deser::Error::MajorVersionMismatch(bad_version_read)) = err { 65 | assert_eq!(bad_version_read, bad_version); 66 | } else { 67 | panic!("wrong error type: {:?}", err); 68 | } 69 | 70 | // reset the Major version, but set a wrong minor version 71 | cursor.as_bytes_mut()[8..10].copy_from_slice(&VERSION.0.to_ne_bytes()); 72 | let bad_version: u16 = 0xffff; 73 | cursor.as_bytes_mut()[10..12].copy_from_slice(&bad_version.to_ne_bytes()); 74 | 75 | let err = ::deserialize_full(&mut std::io::Cursor::new(cursor.as_bytes())); 76 | if let Err(deser::Error::MinorVersionMismatch(bad_version_read)) = err { 77 | assert_eq!(bad_version_read, bad_version); 78 | } else { 79 | panic!("wrong error type {:?}", err); 80 | } 81 | 82 | let err = ::deserialize_eps(cursor.as_bytes()); 83 | if let Err(deser::Error::MinorVersionMismatch(bad_version_read)) = err { 84 | assert_eq!(bad_version_read, bad_version); 85 | } else { 86 | panic!("wrong error type {:?}", err); 87 | } 88 | 89 | // reset the minor version, but deserialize with the wrong type 90 | cursor.as_bytes_mut()[10..12].copy_from_slice(&VERSION.1.to_ne_bytes()); 91 | 92 | let mut type_hasher = Xxh3::with_seed(0); 93 | ::type_hash(&mut type_hasher); 94 | let usize_type_hash = type_hasher.finish(); 95 | 96 | let mut type_hasher = Xxh3::with_seed(0); 97 | ::type_hash(&mut type_hasher); 98 | let i8_hash = type_hasher.finish(); 99 | 100 | let result = ::deserialize_full(&mut std::io::Cursor::new(cursor.as_bytes())); 101 | if let Err(err) = result { 102 | eprintln!("{err}"); 103 | if let deser::Error::WrongTypeHash { 104 | ser_type_name, 105 | ser_type_hash, 106 | self_type_name, 107 | self_type_hash, 108 | } = err 109 | { 110 | assert_eq!(ser_type_name, "usize"); 111 | assert_eq!(ser_type_hash, usize_type_hash); 112 | assert_eq!(self_type_name, "i8"); 113 | assert_eq!(self_type_hash, i8_hash); 114 | } else { 115 | panic!("wrong error type: {:?}", err); 116 | } 117 | } else { 118 | panic!("No error: {:?}", err); 119 | } 120 | 121 | let result = ::deserialize_eps(cursor.as_bytes()); 122 | if let Err(err) = result { 123 | eprintln!("{err}"); 124 | if let deser::Error::WrongTypeHash { 125 | ser_type_name, 126 | ser_type_hash, 127 | self_type_name, 128 | self_type_hash, 129 | } = err 130 | { 131 | assert_eq!(ser_type_name, "usize"); 132 | assert_eq!(ser_type_hash, usize_type_hash); 133 | assert_eq!(self_type_name, "i8"); 134 | assert_eq!(self_type_hash, i8_hash); 135 | } else { 136 | panic!("wrong error type: {:?}", err); 137 | } 138 | } else { 139 | panic!("No error: {:?}", err); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /epserde/tests/test_bad_ser.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | /* 8 | 9 | // This test should not compile, as the field of a zero-copy structure is not zero-copy. 10 | 11 | #[test] 12 | fn test_fake_zero() { 13 | use epserde::prelude::*; 14 | #[derive(Epserde)] 15 | struct NewType { 16 | data: Vec, 17 | } 18 | 19 | impl MaxSizeOf for NewType { 20 | fn max_size_of() -> usize { 21 | 0 22 | } 23 | } 24 | #[derive(Epserde)] 25 | #[zero_copy] 26 | #[repr(C)] 27 | struct FakeZero { 28 | a: NewType, 29 | } 30 | 31 | let result = std::panic::catch_unwind(|| { 32 | let mut cursor = >::new(); 33 | let a = FakeZero { 34 | a: NewType { 35 | data: vec![0x89; 6], 36 | }, 37 | }; 38 | // This must panic. 39 | let _ = a.serialize(&mut cursor); 40 | }); 41 | assert!(result.is_err()); 42 | } 43 | */ 44 | -------------------------------------------------------------------------------- /epserde/tests/test_boxed_slice.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use anyhow::Result; 8 | use epserde::prelude::*; 9 | use maligned::A16; 10 | 11 | #[derive(Epserde, Debug, PartialEq, Eq, Clone)] 12 | struct Data { 13 | a: A, 14 | b: [i32; Q], 15 | } 16 | 17 | #[derive(Epserde, Debug, PartialEq, Eq, Clone)] 18 | struct B { 19 | c: C, 20 | } 21 | 22 | #[test] 23 | fn test_boxed_slices() -> Result<()> { 24 | let a = vec![1, 2, 3, 4].into_boxed_slice(); 25 | let mut cursor = >::new(); 26 | a.serialize(&mut cursor)?; 27 | cursor.set_position(0); 28 | let b = Box::<[i32]>::deserialize_full(&mut cursor)?; 29 | assert_eq!(a, b); 30 | let b = Box::<[i32]>::deserialize_eps(cursor.as_bytes())?; 31 | assert_eq!(b, a.as_ref()); 32 | 33 | cursor.set_position(0); 34 | let d = Data { 35 | a: vec![0, 1, 2, 3].into_boxed_slice(), 36 | b: [1, 2, 3], 37 | }; 38 | d.serialize(&mut cursor)?; 39 | cursor.set_position(0); 40 | let e = Data::>::deserialize_full(&mut cursor)?; 41 | assert_eq!(e, d); 42 | 43 | cursor.set_position(0); 44 | let d = Data { a, b: [1, 2, 3] }; 45 | d.serialize(&mut cursor)?; 46 | cursor.set_position(0); 47 | let e = Data::>::deserialize_eps(cursor.as_bytes())?; 48 | assert_eq!(e.a, d.a.as_ref()); 49 | Ok(()) 50 | } 51 | -------------------------------------------------------------------------------- /epserde/tests/test_generics.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | use maligned::A16; 9 | use std::marker::PhantomData; 10 | #[derive(Epserde, Debug, PartialEq, Eq, Clone)] 11 | struct Data { 12 | a: A, 13 | b: [i32; Q], 14 | } 15 | 16 | #[test] 17 | fn test_inner_param_full() { 18 | // Create a new value to serialize 19 | let person = Data { 20 | a: vec![0x89; 6], 21 | b: [0xbadf00d; 2], 22 | }; 23 | let mut cursor = >::new(); 24 | // Serialize 25 | let _bytes_written = person.serialize(&mut cursor).unwrap(); 26 | 27 | // Do a full-copy deserialization 28 | cursor.set_position(0); 29 | let full = , 2>>::deserialize_full(&mut cursor).unwrap(); 30 | assert_eq!(person, full); 31 | 32 | println!(); 33 | 34 | // Do an ε-copy deserialization 35 | let eps = , 2>>::deserialize_eps(cursor.as_bytes()).unwrap(); 36 | assert_eq!(person.a, eps.a); 37 | assert_eq!(person.b, eps.b); 38 | } 39 | 40 | #[derive(Epserde, Debug, PartialEq, Eq, Clone)] 41 | struct Data2 { 42 | a: B, 43 | // this should be ignored, but contains `P` in the type name so it might 44 | // be erroneously matched 45 | _marker2: PhantomData<()>, 46 | _marker: std::marker::PhantomData

, 47 | } 48 | 49 | #[test] 50 | fn test_inner_param_eps() { 51 | // Create a new value to serialize 52 | let data = Data2::> { 53 | a: vec![0x89; 6], 54 | _marker2: PhantomData, 55 | _marker: PhantomData, 56 | }; 57 | 58 | let mut cursor = >::new(); 59 | // Serialize 60 | let _bytes_written = data.serialize(&mut cursor).unwrap(); 61 | 62 | // Do a full-copy deserialization 63 | cursor.set_position(0); 64 | let full = >>::deserialize_full(&mut cursor).unwrap(); 65 | assert_eq!(data, full); 66 | // Do an ε-copy deserialization 67 | 68 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 69 | assert_eq!(data.a, eps.a); 70 | } 71 | 72 | #[derive(Epserde, Debug, PartialEq, Eq, Clone, Copy)] 73 | #[zero_copy] 74 | #[repr(C)] 75 | struct Data3; 76 | 77 | #[test] 78 | fn test_consts() { 79 | // Create a new value to serialize 80 | let data = Data3::<11> {}; 81 | 82 | let mut cursor = >::new(); 83 | // Serialize 84 | let _bytes_written = data.serialize(&mut cursor).unwrap(); 85 | cursor.set_position(0); 86 | 87 | // with a different const the deserialization should fail 88 | let eps = >::deserialize_full(&mut cursor); 89 | assert!(eps.is_err()); 90 | 91 | // Do a full-copy deserialization 92 | cursor.set_position(0); 93 | let full = >::deserialize_full(&mut cursor).unwrap(); 94 | assert_eq!(data, full); 95 | 96 | // Do an ε-copy deserialization 97 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 98 | assert_eq!(&data, eps); 99 | 100 | // with a different const the deserialization should fail 101 | let eps = >::deserialize_eps(cursor.as_bytes()); 102 | assert!(eps.is_err()); 103 | } 104 | -------------------------------------------------------------------------------- /epserde/tests/test_max_size_of.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | use maligned::{A16, A64}; 9 | #[derive(Epserde, Debug, Clone, Copy, PartialEq, Eq)] 10 | #[repr(C)] 11 | #[repr(align(32))] 12 | #[repr(align(64))] // The max wins 13 | #[zero_copy] 14 | struct MyStruct64 { 15 | u: u32, 16 | } 17 | 18 | #[derive(Epserde, Debug, Clone, Copy, PartialEq, Eq)] 19 | #[repr(C)] 20 | #[repr(align(2))] 21 | #[zero_copy] 22 | struct MyStruct2 { 23 | u: u32, 24 | } 25 | 26 | #[derive(Epserde, Debug, Clone, Copy, PartialEq, Eq)] 27 | #[repr(C)] 28 | #[zero_copy] 29 | struct MyStruct { 30 | u: u32, 31 | } 32 | 33 | #[test] 34 | /// Check that we don't have any collision on most types 35 | fn test_max_size_of_align() { 36 | assert_eq!(64, MyStruct64::max_size_of()); 37 | assert_eq!(MyStruct::max_size_of(), MyStruct2::max_size_of()); 38 | 39 | let x = MyStruct { u: 0x89 }; 40 | let mut cursor = >::new(); 41 | // Serialize 42 | let _bytes_written = x.serialize(&mut cursor).unwrap(); 43 | 44 | // Do an ε-copy deserialization 45 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 46 | assert_eq!(x, *eps); 47 | 48 | // Create a new value to serialize 49 | let x = MyStruct2 { u: 0x89 }; 50 | let mut cursor = >::new(); 51 | // Serialize 52 | let _bytes_written = x.serialize(&mut cursor).unwrap(); 53 | 54 | // Do an ε-copy deserialization 55 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 56 | assert_eq!(x, *eps); 57 | 58 | // Create a new value to serialize 59 | let x = MyStruct64 { u: 0x89 }; 60 | // We need a higher alignment 61 | let mut cursor = >::new(); 62 | // Serialize 63 | let _bytes_written = x.serialize(&mut cursor).unwrap(); 64 | 65 | // Do an ε-copy deserialization 66 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 67 | assert_eq!(x, *eps); 68 | } 69 | -------------------------------------------------------------------------------- /epserde/tests/test_memcase.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | 9 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 10 | struct PersonVec { 11 | a: A, 12 | b: B, 13 | test: isize, 14 | } 15 | 16 | #[derive(Epserde, Debug, PartialEq, Eq, Default, Clone)] 17 | struct Data { 18 | a: A, 19 | b: Vec, 20 | } 21 | 22 | type Person = PersonVec, Data>>; 23 | 24 | #[cfg(feature = "mmap")] 25 | #[test] 26 | fn test_mem_case() { 27 | // Create a new value to serialize 28 | let person = Person { 29 | a: vec![0x89; 6], 30 | b: Data { 31 | a: vec![0x42; 7], 32 | b: vec![0xbadf00d; 2], 33 | }, 34 | test: -0xbadf00d, 35 | }; 36 | // Serialize 37 | person.store("test.bin").unwrap(); 38 | 39 | let res = Person::load_mem("test.bin").unwrap(); 40 | assert_eq!(person.test, res.test); 41 | assert_eq!(person.a, res.a); 42 | assert_eq!(person.b.a, res.b.a); 43 | assert_eq!(person.b.b, res.b.b); 44 | 45 | let res = Person::load_mmap("test.bin", Flags::empty()).unwrap(); 46 | assert_eq!(person.test, res.test); 47 | assert_eq!(person.a, res.a); 48 | assert_eq!(person.b.a, res.b.a); 49 | assert_eq!(person.b.b, res.b.b); 50 | 51 | let res = Person::load_mem("test.bin").unwrap(); 52 | assert_eq!(person.test, res.test); 53 | assert_eq!(person.a, res.a); 54 | assert_eq!(person.b.a, res.b.a); 55 | assert_eq!(person.b.b, res.b.b); 56 | 57 | let res = Person::load_full("test.bin").unwrap(); 58 | assert_eq!(person.test, res.test); 59 | assert_eq!(person.a, res.a); 60 | assert_eq!(person.b.a, res.b.a); 61 | assert_eq!(person.b.b, res.b.b); 62 | 63 | let res = Person::mmap("test.bin", Flags::empty()).unwrap(); 64 | assert_eq!(person.test, res.test); 65 | assert_eq!(person.a, res.a); 66 | assert_eq!(person.b.a, res.b.a); 67 | assert_eq!(person.b.b, res.b.b); 68 | 69 | let res = Person::mmap("test.bin", Flags::TRANSPARENT_HUGE_PAGES).unwrap(); 70 | assert_eq!(person.test, res.test); 71 | assert_eq!(person.a, res.a); 72 | assert_eq!(person.b.a, res.b.a); 73 | assert_eq!(person.b.b, res.b.b); 74 | 75 | let res = Person::mmap("test.bin", Flags::empty()).unwrap(); 76 | assert_eq!(person.test, res.test); 77 | assert_eq!(person.a, res.a); 78 | assert_eq!(person.b.a, res.b.a); 79 | assert_eq!(person.b.b, res.b.b); 80 | 81 | // cleanup the file 82 | std::fs::remove_file("test.bin").unwrap(); 83 | } 84 | -------------------------------------------------------------------------------- /epserde/tests/test_phantom.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use core::marker::PhantomData; 8 | use epserde::prelude::*; 9 | use epserde::TypeInfo; 10 | use maligned::A16; 11 | 12 | #[test] 13 | /// Test that we can serialize and deserialize a PhantomData 14 | /// This should be a NOOP 15 | fn test_phantom() { 16 | // Create a new value to serialize 17 | let obj = PhantomData::; 18 | let mut cursor = >::new(); 19 | // Serialize 20 | let _bytes_written = obj.serialize(&mut cursor).unwrap(); 21 | 22 | // Do a full-copy deserialization 23 | cursor.set_position(0); 24 | let full = >::deserialize_full(&mut cursor).unwrap(); 25 | assert_eq!(obj, full); 26 | 27 | println!(); 28 | 29 | // Do an ε-copy deserialization 30 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 31 | assert_eq!(obj, eps); 32 | } 33 | 34 | #[derive(Epserde, Debug, PartialEq, Eq, Clone, Default)] 35 | struct DataFull { 36 | a: usize, 37 | b: PhantomData, 38 | } 39 | #[derive(Debug, PartialEq, Eq, Clone, Default, TypeInfo)] 40 | struct NotSerializableType; 41 | 42 | /// Test that we can serialize a PhantomData of a non-serializable type 43 | /// in a full-copy type. 44 | /// This should be a no-op. 45 | #[test] 46 | fn test_not_serializable_in_phantom() { 47 | // Full copy with a non-serializable type 48 | let obj = >::default(); 49 | 50 | let mut cursor = >::new(); 51 | // Serialize 52 | let _bytes_written = obj.serialize(&mut cursor).unwrap(); 53 | 54 | // Do a full-copy deserialization 55 | cursor.set_position(0); 56 | let full = >::deserialize_full(&mut cursor).unwrap(); 57 | assert_eq!(obj, full); 58 | 59 | println!(); 60 | 61 | // Do an ε-copy deserialization 62 | cursor.set_position(0); 63 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 64 | assert_eq!(obj.a, eps.a); 65 | } 66 | 67 | #[derive(Epserde, Copy, Debug, PartialEq, Eq, Clone, Default)] 68 | #[repr(C)] 69 | #[zero_copy] 70 | struct DataZero { 71 | a: usize, 72 | b: PhantomData, 73 | } 74 | #[derive(Epserde, Debug, Copy, PartialEq, Eq, Clone, Default)] 75 | #[repr(C)] 76 | #[zero_copy] 77 | struct ZeroCopyType; 78 | 79 | /// Test that we can serialize a PhantomData in a zero-copy 80 | /// type if the argument of the PhantomData is zero-copy. 81 | /// This should be a no-op. 82 | #[test] 83 | fn test_phantom_zero_copy() { 84 | // Zero copy needs a zero-copy type, even if inside a PhantomData 85 | let obj = >::default(); 86 | 87 | let mut cursor = >::new(); 88 | // Serialize 89 | let _bytes_written = obj.serialize(&mut cursor).unwrap(); 90 | 91 | // Do a full-copy deserialization 92 | cursor.set_position(0); 93 | let zero = >::deserialize_full(&mut cursor).unwrap(); 94 | assert_eq!(obj, zero); 95 | 96 | println!(); 97 | 98 | // Do an ε-copy deserialization 99 | cursor.set_position(0); 100 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 101 | assert_eq!(obj.a, eps.a); 102 | } 103 | 104 | #[derive(Epserde, Copy, Debug, PartialEq, Eq, Clone, Default)] 105 | #[repr(C)] 106 | #[zero_copy] 107 | struct OnlyPhantom { 108 | a: PhantomData, 109 | b: PhantomData<(A, A)>, 110 | } 111 | 112 | /// Test that we can serialize a zero-copy type containing a single 113 | /// PhantomData. 114 | /// This should be a no-op. 115 | #[test] 116 | fn test_only_phantom() { 117 | // Zero copy needs a zero-copy type, even if inside a PhantomData 118 | let obj = >::default(); 119 | 120 | let mut cursor = >::new(); 121 | // Serialize 122 | let _bytes_written = obj.serialize(&mut cursor).unwrap(); 123 | 124 | // Do a full-copy deserialization 125 | cursor.set_position(0); 126 | let zero = >::deserialize_full(&mut cursor).unwrap(); 127 | assert_eq!(obj, zero); 128 | 129 | println!(); 130 | 131 | // Do an ε-copy deserialization 132 | cursor.set_position(0); 133 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 134 | assert_eq!(obj.a, eps.a); 135 | 136 | // Zero copy needs a zero-copy type, even if inside a PhantomData 137 | let vec = vec![>::default(); 10]; 138 | 139 | let mut cursor = >::new(); 140 | // Serialize 141 | let _bytes_written = vec.serialize(&mut cursor).unwrap(); 142 | 143 | // Do a full-copy deserialization 144 | cursor.set_position(0); 145 | let zero = >>::deserialize_full(&mut cursor).unwrap(); 146 | assert_eq!(vec, zero); 147 | 148 | println!(); 149 | 150 | // Do an ε-copy deserialization 151 | cursor.set_position(0); 152 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 153 | assert_eq!(vec, eps); 154 | } 155 | -------------------------------------------------------------------------------- /epserde/tests/test_prim.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use core::num::{ 8 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 9 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, 10 | }; 11 | use epserde::prelude::*; 12 | 13 | macro_rules! impl_test { 14 | ($data:expr, $ty:ty) => {{ 15 | let mut v = vec![]; 16 | let mut cursor = std::io::Cursor::new(&mut v); 17 | 18 | let _ = $data.serialize_with_schema(&mut cursor).unwrap(); 19 | 20 | let full_copy = <$ty>::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 21 | assert_eq!($data, full_copy); 22 | 23 | let eps_copy = <$ty>::deserialize_eps(&v).unwrap(); 24 | assert_eq!($data, eps_copy); 25 | } 26 | { 27 | let mut v = vec![]; 28 | let mut cursor = std::io::Cursor::new(&mut v); 29 | $data.serialize(&mut cursor).unwrap(); 30 | 31 | let full_copy = <$ty>::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 32 | assert_eq!($data, full_copy); 33 | 34 | let eps_copy = <$ty>::deserialize_eps(&v).unwrap(); 35 | assert_eq!($data, eps_copy); 36 | }}; 37 | } 38 | 39 | macro_rules! test_prim { 40 | ($ty:ty, $test_name:ident) => { 41 | #[test] 42 | fn $test_name() { 43 | impl_test!(<$ty>::MAX, $ty); 44 | impl_test!(<$ty>::MIN, $ty); 45 | impl_test!(0 as $ty, $ty); 46 | impl_test!(7 as $ty, $ty); 47 | } 48 | }; 49 | } 50 | 51 | test_prim!(u8, test_u8); 52 | test_prim!(u16, test_u16); 53 | test_prim!(u32, test_u32); 54 | test_prim!(u64, test_u64); 55 | test_prim!(u128, test_u128); 56 | test_prim!(usize, test_usize); 57 | test_prim!(i8, test_i8); 58 | test_prim!(i16, test_i16); 59 | test_prim!(i32, test_i32); 60 | test_prim!(i64, test_i64); 61 | test_prim!(i128, test_i128); 62 | test_prim!(isize, test_isize); 63 | 64 | macro_rules! test_nonzero { 65 | ($ty:ty, $test_name:ident) => { 66 | #[test] 67 | fn $test_name() { 68 | impl_test!(<$ty>::MAX, $ty); 69 | impl_test!(<$ty>::MIN, $ty); 70 | impl_test!(<$ty>::try_from(1).unwrap(), $ty); 71 | impl_test!(<$ty>::try_from(7).unwrap(), $ty); 72 | } 73 | }; 74 | } 75 | 76 | test_nonzero!(NonZeroU8, test_nonzero_u8); 77 | test_nonzero!(NonZeroU16, test_nonzero_u16); 78 | test_nonzero!(NonZeroU32, test_nonzero_u32); 79 | test_nonzero!(NonZeroU64, test_nonzero_u64); 80 | test_nonzero!(NonZeroU128, test_nonzero_u128); 81 | test_nonzero!(NonZeroUsize, test_nonzero_usize); 82 | test_nonzero!(NonZeroI8, testnonzero_i8); 83 | test_nonzero!(NonZeroI16, test_nonzero_i16); 84 | test_nonzero!(NonZeroI32, test_nonzero_i32); 85 | test_nonzero!(NonZeroI64, test_nonzero_i64); 86 | test_nonzero!(NonZeroI128, test_nonzero_i128); 87 | test_nonzero!(NonZeroIsize, test_nonzero_isize); 88 | 89 | #[test] 90 | fn test_unit() { 91 | impl_test!((), ()); 92 | } 93 | 94 | #[test] 95 | fn test_bool() { 96 | impl_test!(true, bool); 97 | impl_test!(false, bool); 98 | } 99 | 100 | const TEST_STRS: &[&str] = &["abc\0\x0a🔥\u{0d2bdf}", ""]; 101 | 102 | #[test] 103 | fn test_char() { 104 | for test_str in TEST_STRS { 105 | for c in test_str.chars() { 106 | impl_test!(c, char); 107 | } 108 | } 109 | } 110 | 111 | #[test] 112 | fn test_string() { 113 | for test_str in TEST_STRS { 114 | let s = test_str.to_string(); 115 | { 116 | let mut v = vec![]; 117 | let mut cursor = std::io::Cursor::new(&mut v); 118 | 119 | let mut schema = s.serialize_with_schema(&mut cursor).unwrap(); 120 | schema.0.sort_by_key(|a| a.offset); 121 | 122 | cursor.set_position(0); 123 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 124 | assert_eq!(s, full_copy); 125 | 126 | let full_copy = ::deserialize_eps(&v).unwrap(); 127 | assert_eq!(s.as_str(), full_copy); 128 | 129 | let _ = schema.to_csv(); 130 | let _ = schema.debug(&v); 131 | } 132 | { 133 | let mut v = vec![]; 134 | let mut cursor = std::io::Cursor::new(&mut v); 135 | s.serialize(&mut cursor).unwrap(); 136 | 137 | cursor.set_position(0); 138 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 139 | assert_eq!(s, full_copy); 140 | 141 | let full_copy = ::deserialize_eps(&v).unwrap(); 142 | assert_eq!(s.as_str(), full_copy); 143 | } 144 | } 145 | } 146 | 147 | #[test] 148 | fn test_box_str() { 149 | for test_str in TEST_STRS { 150 | let s = test_str.to_string().into_boxed_str(); 151 | { 152 | let mut v = vec![]; 153 | let mut cursor = std::io::Cursor::new(&mut v); 154 | 155 | let mut schema = s.serialize_with_schema(&mut cursor).unwrap(); 156 | schema.0.sort_by_key(|a| a.offset); 157 | 158 | cursor.set_position(0); 159 | let full_copy = >::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 160 | assert_eq!(s, full_copy); 161 | 162 | let full_copy = >::deserialize_eps(&v).unwrap(); 163 | assert_eq!(s.as_ref(), full_copy); 164 | } 165 | { 166 | let mut v = vec![]; 167 | let mut cursor = std::io::Cursor::new(&mut v); 168 | s.serialize(&mut cursor).unwrap(); 169 | 170 | cursor.set_position(0); 171 | let full_copy = >::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 172 | assert_eq!(s, full_copy); 173 | 174 | let full_copy = >::deserialize_eps(&v).unwrap(); 175 | assert_eq!(s.as_ref(), full_copy); 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /epserde/tests/test_slice.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use anyhow::Result; 8 | use epserde::prelude::*; 9 | use maligned::A16; 10 | 11 | #[derive(Epserde, Debug, PartialEq, Eq, Clone)] 12 | struct Data { 13 | a: A, 14 | b: [i32; Q], 15 | } 16 | 17 | #[test] 18 | fn test_slices() -> Result<()> { 19 | let a = vec![1, 2, 3, 4]; 20 | let s = a.as_slice(); 21 | let mut cursor = >::new(); 22 | s.serialize(&mut cursor)?; 23 | cursor.set_position(0); 24 | let b = >::deserialize_full(&mut cursor)?; 25 | assert_eq!(a, b.as_slice()); 26 | let b = >::deserialize_eps(cursor.as_bytes())?; 27 | assert_eq!(a, b); 28 | 29 | cursor.set_position(0); 30 | let d = Data { 31 | a: vec![0, 1, 2, 3].into_boxed_slice(), 32 | b: [1, 2, 3], 33 | }; 34 | d.serialize(&mut cursor)?; 35 | cursor.set_position(0); 36 | let e = Data::>::deserialize_full(&mut cursor)?; 37 | assert_eq!(e, d); 38 | 39 | cursor.set_position(0); 40 | let d = Data { a: s, b: [1, 2, 3] }; 41 | d.serialize(&mut cursor)?; 42 | cursor.set_position(0); 43 | let e = Data::>::deserialize_eps(cursor.as_bytes())?; 44 | assert_eq!(e, d); 45 | 46 | Ok(()) 47 | } 48 | -------------------------------------------------------------------------------- /epserde/tests/test_std.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | 9 | fn test_generic(s: T) 10 | where 11 | T: Serialize + Deserialize + PartialEq + core::fmt::Debug, 12 | for<'a> ::DeserType<'a>: PartialEq + core::fmt::Debug, 13 | { 14 | { 15 | let mut v = vec![]; 16 | let mut cursor = std::io::Cursor::new(&mut v); 17 | 18 | let mut schema = s.serialize_with_schema(&mut cursor).unwrap(); 19 | schema.0.sort_by_key(|a| a.offset); 20 | 21 | cursor.set_position(0); 22 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 23 | assert_eq!(s, full_copy); 24 | 25 | let full_copy = ::deserialize_eps(&v).unwrap(); 26 | assert_eq!(full_copy, s); 27 | 28 | let _ = schema.to_csv(); 29 | let _ = schema.debug(&v); 30 | } 31 | { 32 | let mut v = vec![]; 33 | let mut cursor = std::io::Cursor::new(&mut v); 34 | s.serialize(&mut cursor).unwrap(); 35 | 36 | cursor.set_position(0); 37 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 38 | assert_eq!(s, full_copy); 39 | 40 | let full_copy = ::deserialize_eps(&v).unwrap(); 41 | assert_eq!(full_copy, s); 42 | } 43 | } 44 | 45 | #[test] 46 | fn test_range() { 47 | test_generic::>(0..10); 48 | 49 | #[derive(Epserde, PartialEq, Debug)] 50 | struct Data(std::ops::Range); 51 | test_generic(Data(0..10)); 52 | } 53 | -------------------------------------------------------------------------------- /epserde/tests/test_stdlib.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | 9 | fn test_generic(s: T) 10 | where 11 | T: Serialize + Deserialize + PartialEq + core::fmt::Debug, 12 | for<'a> ::DeserType<'a>: PartialEq + core::fmt::Debug, 13 | { 14 | { 15 | let mut v = vec![]; 16 | let mut cursor = std::io::Cursor::new(&mut v); 17 | 18 | let mut schema = s.serialize_with_schema(&mut cursor).unwrap(); 19 | schema.0.sort_by_key(|a| a.offset); 20 | 21 | cursor.set_position(0); 22 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 23 | assert_eq!(s, full_copy); 24 | 25 | let full_copy = ::deserialize_eps(&v).unwrap(); 26 | assert_eq!(full_copy, s); 27 | 28 | let _ = schema.to_csv(); 29 | let _ = schema.debug(&v); 30 | } 31 | { 32 | let mut v = vec![]; 33 | let mut cursor = std::io::Cursor::new(&mut v); 34 | s.serialize(&mut cursor).unwrap(); 35 | 36 | cursor.set_position(0); 37 | let full_copy = ::deserialize_full(&mut std::io::Cursor::new(&v)).unwrap(); 38 | assert_eq!(s, full_copy); 39 | 40 | let full_copy = ::deserialize_eps(&v).unwrap(); 41 | assert_eq!(full_copy, s); 42 | } 43 | } 44 | 45 | #[test] 46 | fn test_range() { 47 | test_generic::>(0..10); 48 | 49 | #[derive(Epserde, PartialEq, Debug)] 50 | struct Data(std::ops::Range); 51 | test_generic(Data(0..10)); 52 | } 53 | -------------------------------------------------------------------------------- /epserde/tests/test_tuples.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | use maligned::A16; 9 | 10 | macro_rules! impl_test { 11 | ($ty:ty, $data:expr) => {{ 12 | let mut cursor = >::new(); 13 | 14 | let _ = $data.serialize_with_schema(&mut cursor).unwrap(); 15 | 16 | cursor.set_position(0); 17 | let full_copy = <$ty>::deserialize_full(&mut cursor).unwrap(); 18 | assert_eq!($data, full_copy); 19 | 20 | let eps_copy = <$ty>::deserialize_eps(cursor.as_bytes()).unwrap(); 21 | assert_eq!($data, *eps_copy); 22 | } 23 | { 24 | let mut cursor = >::new(); 25 | $data.serialize(&mut cursor).unwrap(); 26 | 27 | cursor.set_position(0); 28 | let full_copy = <$ty>::deserialize_full(&mut cursor).unwrap(); 29 | assert_eq!($data, full_copy); 30 | 31 | let eps_copy = <$ty>::deserialize_eps(cursor.as_bytes()).unwrap(); 32 | assert_eq!($data, *eps_copy); 33 | }}; 34 | } 35 | 36 | macro_rules! test_zero { 37 | ($test_name:ident, $ty:ty, $data: expr) => { 38 | #[test] 39 | fn $test_name() { 40 | impl_test!($ty, $data); 41 | } 42 | }; 43 | } 44 | 45 | test_zero!(test_array_i32_2, [i32; 2], [-1, 1]); 46 | test_zero!(test_array_i64_2, [i64; 2], [-1_i64, 1]); 47 | test_zero!(test_tuple_0, (i32, i32), (-1_i32, 1_i32)); 48 | test_zero!(test_tuple_1, (i64, i64), (-1_i64, 1_i64)); 49 | test_zero!( 50 | test_tuple_2, 51 | ((i64, i64), (i64, i64)), 52 | ((-1_i64, 1_i64), (-1_i64, 1_i64)) 53 | ); 54 | test_zero!( 55 | test_tuple_3, 56 | ((i32, i32), (i32, i32)), 57 | ((-1_i32, 1_i32), (-1_i32, 1_i32)) 58 | ); 59 | -------------------------------------------------------------------------------- /epserde/tests/test_type_hash_val.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use core::hash::Hasher; 8 | use epserde::traits::TypeHash; 9 | use std::collections::HashMap; 10 | use xxhash_rust::xxh3::Xxh3; 11 | macro_rules! impl_test { 12 | ($hashes:expr, $value:expr) => {{ 13 | let mut hasher = Xxh3::with_seed(0); 14 | ($value).type_hash_val(&mut hasher); 15 | let type_hash = hasher.finish(); 16 | let res = $hashes.insert(type_hash, stringify!($value)); 17 | assert!( 18 | res.is_none(), 19 | "Collision on type {} with {}", 20 | stringify!($value), 21 | res.unwrap() 22 | ); 23 | }}; 24 | } 25 | 26 | #[test] 27 | /// Check that we don't have any collision on most types 28 | fn test_type_hash_collision() { 29 | let mut hashes = HashMap::new(); 30 | impl_test!(hashes, ()); 31 | impl_test!(hashes, true); 32 | impl_test!(hashes, '🔥'); 33 | impl_test!(hashes, Some('🔥')); 34 | impl_test!(hashes, Some(1_u8)); 35 | 36 | impl_test!(hashes, 1_u8); 37 | impl_test!(hashes, 1_u16); 38 | impl_test!(hashes, 1_u32); 39 | impl_test!(hashes, 1_u64); 40 | impl_test!(hashes, 1_u128); 41 | impl_test!(hashes, 1_usize); 42 | impl_test!(hashes, 1_i8); 43 | impl_test!(hashes, 1_i16); 44 | impl_test!(hashes, 1_i32); 45 | impl_test!(hashes, 1_i64); 46 | impl_test!(hashes, 1_i128); 47 | impl_test!(hashes, 1_isize); 48 | 49 | impl_test!(hashes, vec![1_usize, 2, 3, 4, 5]); 50 | impl_test!(hashes, vec![1_u8, 2, 3, 4, 5]); 51 | impl_test!(hashes, (1_u8, 3_u8, 2_u8)); 52 | 53 | impl_test!(hashes, vec![1_i8, 2, 3, 4, 5].as_slice()); 54 | 55 | dbg!(hashes); 56 | } 57 | -------------------------------------------------------------------------------- /epserde/tests/test_types.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | use maligned::A16; 9 | use std::iter; 10 | 11 | macro_rules! impl_test { 12 | ($ty:ty, $val:expr) => {{ 13 | let a = $val; 14 | let mut cursor = >::new(); 15 | 16 | let mut schema = a.serialize_with_schema(&mut cursor).unwrap(); 17 | schema.0.sort_by_key(|a| a.offset); 18 | println!("{}", schema.to_csv()); 19 | 20 | cursor.set_position(0); 21 | let a1 = <$ty>::deserialize_full(&mut cursor).unwrap(); 22 | assert_eq!(a, a1); 23 | 24 | let a2 = <$ty>::deserialize_eps(cursor.as_bytes()).unwrap(); 25 | assert_eq!(a, a2); 26 | }}; 27 | } 28 | 29 | #[test] 30 | fn test_array_usize() { 31 | let a = [1, 2, 3, 4, 5]; 32 | 33 | let mut cursor = >::new(); 34 | let mut schema = a.serialize_with_schema(&mut cursor).unwrap(); 35 | schema.0.sort_by_key(|a| a.offset); 36 | println!("{}", schema.to_csv()); 37 | 38 | cursor.set_position(0); 39 | let a1 = <[usize; 5]>::deserialize_full(&mut cursor).unwrap(); 40 | assert_eq!(a, a1); 41 | 42 | let a2 = <[usize; 5]>::deserialize_eps(cursor.as_bytes()).unwrap(); 43 | assert_eq!(a, *a2); 44 | } 45 | 46 | #[test] 47 | fn test_vec_usize() { 48 | impl_test!(Vec, vec![1, 2, 3, 4, 5]) 49 | } 50 | 51 | #[test] 52 | fn test_box_slice_usize() { 53 | let a = vec![1, 2, 3, 4, 5].into_boxed_slice(); 54 | 55 | let mut cursor = >::new(); 56 | let mut schema = a.serialize_with_schema(&mut cursor).unwrap(); 57 | schema.0.sort_by_key(|a| a.offset); 58 | println!("{}", schema.to_csv()); 59 | 60 | cursor.set_position(0); 61 | let a1 = >::deserialize_full(&mut cursor).unwrap(); 62 | assert_eq!(a, a1); 63 | 64 | let a2 = >::deserialize_eps(cursor.as_bytes()).unwrap(); 65 | assert_eq!(a, a2.into()); 66 | } 67 | 68 | #[test] 69 | fn test_box_slice_string() { 70 | let a = vec!["A".to_string(), "V".to_string()].into_boxed_slice(); 71 | 72 | let mut cursor = >::new(); 73 | let mut schema = a.serialize_with_schema(&mut cursor).unwrap(); 74 | schema.0.sort_by_key(|a| a.offset); 75 | println!("{}", schema.to_csv()); 76 | 77 | cursor.set_position(0); 78 | let a1 = >::deserialize_full(&mut cursor).unwrap(); 79 | assert_eq!(a, a1); 80 | 81 | let a2 = >::deserialize_eps(cursor.as_bytes()).unwrap(); 82 | assert_eq!(a.len(), a2.len()); 83 | a.iter().zip(a2.iter()).for_each(|(a, a2)| { 84 | assert_eq!(a, a2); 85 | }); 86 | } 87 | 88 | #[test] 89 | fn test_vec_vec_usize() { 90 | impl_test!(Vec>, vec![vec![1, 2, 3], vec![4, 5]]) 91 | } 92 | 93 | #[test] 94 | fn test_vec_array_string() { 95 | impl_test!( 96 | Vec<[String; 2]>, 97 | vec![ 98 | ["a".to_string(), "b".to_string()], 99 | ["c".to_string(), "aasfihjasomk".to_string()] 100 | ] 101 | ) 102 | } 103 | 104 | #[test] 105 | fn test_vec_vec_string() { 106 | impl_test!( 107 | Vec>, 108 | vec![ 109 | vec!["a".to_string(), "b".to_string()], 110 | vec!["c".to_string(), "aasfihjasomk".to_string()] 111 | ] 112 | ) 113 | } 114 | 115 | #[test] 116 | fn test_vec_vec_array_array_string() { 117 | impl_test!( 118 | Vec>, 119 | vec![ 120 | vec![[ 121 | ["a".to_string(), "b".to_string()], 122 | ["c".to_string(), "d".to_string()], 123 | ]], 124 | vec![[ 125 | ["a".to_string(), "b".to_string()], 126 | ["c".to_string(), "d".to_string()], 127 | ]], 128 | ] 129 | ) 130 | } 131 | 132 | #[test] 133 | fn test_vec_vec_array_array_usize() { 134 | impl_test!( 135 | Vec>, 136 | vec![vec![[[1, 2], [3, 4],]], vec![[[5, 6], [7, 8],]],] 137 | ) 138 | } 139 | 140 | #[test] 141 | fn test_struct_deep() { 142 | #[derive(Epserde, Copy, Clone, Debug, PartialEq)] 143 | struct Struct { 144 | a: usize, 145 | b: usize, 146 | c: i32, 147 | } 148 | let a = Struct { a: 0, b: 1, c: 2 }; 149 | let mut cursor = >::new(); 150 | // Serialize 151 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 152 | 153 | cursor.set_position(0); 154 | let full = ::deserialize_full(&mut cursor).unwrap(); 155 | assert_eq!(a, full); 156 | 157 | cursor.set_position(0); 158 | let eps = ::deserialize_full(&mut cursor).unwrap(); 159 | assert_eq!(a, eps); 160 | } 161 | 162 | #[test] 163 | fn test_struct_zero() { 164 | #[derive(Epserde, Copy, Clone, Debug, PartialEq)] 165 | #[repr(C)] 166 | #[zero_copy] 167 | struct Struct { 168 | a: usize, 169 | b: usize, 170 | c: i32, 171 | } 172 | let a = Struct { a: 0, b: 1, c: 2 }; 173 | let mut cursor = >::new(); 174 | // Serialize 175 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 176 | 177 | cursor.set_position(0); 178 | let full = ::deserialize_full(&mut cursor).unwrap(); 179 | assert_eq!(a, full); 180 | 181 | cursor.set_position(0); 182 | let eps = ::deserialize_full(&mut cursor).unwrap(); 183 | assert_eq!(a, eps); 184 | } 185 | 186 | #[test] 187 | fn test_tuple_struct_deep() { 188 | #[derive(Epserde, Copy, Clone, Debug, PartialEq)] 189 | struct Tuple(usize, usize, i32); 190 | let a = Tuple(0, 1, 2); 191 | let mut cursor = >::new(); 192 | // Serialize 193 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 194 | 195 | cursor.set_position(0); 196 | let full = ::deserialize_full(&mut cursor).unwrap(); 197 | assert_eq!(a, full); 198 | 199 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 200 | assert_eq!(a, eps); 201 | } 202 | 203 | #[test] 204 | fn test_tuple_struct_zero() { 205 | #[derive(Epserde, Copy, Clone, Debug, PartialEq)] 206 | #[repr(C)] 207 | #[zero_copy] 208 | struct Tuple(usize, usize, i32); 209 | let a = Tuple(0, 1, 2); 210 | let mut cursor = >::new(); 211 | // Serialize 212 | let _bytes_written = a.serialize(&mut cursor).unwrap(); 213 | 214 | cursor.set_position(0); 215 | let full = ::deserialize_full(&mut cursor).unwrap(); 216 | assert_eq!(a, full); 217 | 218 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 219 | assert_eq!(a, *eps); 220 | } 221 | 222 | #[test] 223 | fn test_enum_deep() { 224 | #[derive(Epserde, Clone, Debug, PartialEq)] 225 | enum Data> { 226 | A, 227 | B(u64), 228 | C(u64, Vec), 229 | D { a: i32, b: V }, 230 | E, 231 | } 232 | 233 | let mut cursor = >::new(); 234 | let a = Data::A; 235 | a.serialize(&mut cursor).unwrap(); 236 | cursor.set_position(0); 237 | let full = ::deserialize_full(&mut cursor).unwrap(); 238 | assert_eq!(a, full); 239 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 240 | assert!(matches!(eps, Data::A)); 241 | 242 | let mut cursor = >::new(); 243 | let a = Data::B(3); 244 | a.serialize(&mut cursor).unwrap(); 245 | cursor.set_position(0); 246 | let full = ::deserialize_full(&mut cursor).unwrap(); 247 | assert_eq!(a, full); 248 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 249 | assert!(matches!(eps, Data::B(3))); 250 | 251 | let mut cursor = >::new(); 252 | let a = Data::C(4, vec![1, 2, 3]); 253 | a.serialize(&mut cursor).unwrap(); 254 | cursor.set_position(0); 255 | let full = ::deserialize_full(&mut cursor).unwrap(); 256 | assert_eq!(a, full); 257 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 258 | assert!(matches!(eps, Data::C(4, _))); 259 | 260 | let mut cursor = >::new(); 261 | let a = Data::D { 262 | a: 1, 263 | b: vec![1, 2], 264 | }; 265 | a.serialize(&mut cursor).unwrap(); 266 | cursor.set_position(0); 267 | let full = >>::deserialize_full(&mut cursor).unwrap(); 268 | assert!(matches!(full, Data::D { a: 1, b: _ })); 269 | let eps = >>::deserialize_eps(cursor.as_bytes()).unwrap(); 270 | assert!(matches!(eps, Data::D { a: 1, b: [1, 2] })); 271 | 272 | let mut cursor = >::new(); 273 | let a = Data::E; 274 | a.serialize(&mut cursor).unwrap(); 275 | cursor.set_position(0); 276 | let full = ::deserialize_full(&mut cursor).unwrap(); 277 | assert_eq!(a, full); 278 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 279 | assert!(matches!(eps, Data::E)); 280 | 281 | let mut cursor = >::new(); 282 | let a = Vec::from_iter(iter::repeat(Data::A).take(10)); 283 | a.serialize(&mut cursor).unwrap(); 284 | cursor.set_position(0); 285 | let full = >::deserialize_full(&mut cursor).unwrap(); 286 | assert_eq!(a, full); 287 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 288 | for e in eps { 289 | assert!(matches!(e, Data::A)); 290 | } 291 | } 292 | 293 | #[test] 294 | fn test_enum_zero() { 295 | #[derive(Epserde, Clone, Copy, Debug, PartialEq)] 296 | #[repr(C)] 297 | #[zero_copy] 298 | enum Data { 299 | A, 300 | B(u64), 301 | C(u64), 302 | D { a: i32, b: i32 }, 303 | } 304 | 305 | let mut cursor = >::new(); 306 | let a = Data::A; 307 | a.serialize(&mut cursor).unwrap(); 308 | cursor.set_position(0); 309 | let full = ::deserialize_full(&mut cursor).unwrap(); 310 | assert_eq!(a, full); 311 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 312 | assert_eq!(a, *eps); 313 | 314 | let mut cursor = >::new(); 315 | let a = Data::B(3); 316 | a.serialize(&mut cursor).unwrap(); 317 | cursor.set_position(0); 318 | let full = ::deserialize_full(&mut cursor).unwrap(); 319 | assert_eq!(a, full); 320 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 321 | assert_eq!(a, *eps); 322 | 323 | let mut cursor = >::new(); 324 | let a = Data::C(4); 325 | a.serialize(&mut cursor).unwrap(); 326 | cursor.set_position(0); 327 | let full = ::deserialize_full(&mut cursor).unwrap(); 328 | assert_eq!(a, full); 329 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 330 | assert_eq!(a, *eps); 331 | 332 | let mut cursor = >::new(); 333 | let a = Data::D { a: 1, b: 2 }; 334 | a.serialize(&mut cursor).unwrap(); 335 | cursor.set_position(0); 336 | let full = ::deserialize_full(&mut cursor).unwrap(); 337 | assert_eq!(a, full); 338 | let eps = ::deserialize_eps(cursor.as_bytes()).unwrap(); 339 | assert_eq!(a, *eps); 340 | 341 | let mut cursor = >::new(); 342 | let a = Vec::from_iter(iter::repeat(Data::A).take(10)); 343 | a.serialize(&mut cursor).unwrap(); 344 | cursor.set_position(0); 345 | let full = >::deserialize_full(&mut cursor).unwrap(); 346 | assert_eq!(a, full); 347 | let eps = >::deserialize_eps(cursor.as_bytes()).unwrap(); 348 | assert_eq!(a, *eps); 349 | } 350 | -------------------------------------------------------------------------------- /epserde/tests/test_zero.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-FileCopyrightText: 2023 Inria 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later 5 | */ 6 | 7 | use epserde::prelude::*; 8 | use maligned::A16; 9 | 10 | macro_rules! impl_test { 11 | ($ty:ty, $data:expr) => {{ 12 | let mut cursor = >::new(); 13 | 14 | let _ = $data.serialize_with_schema(&mut cursor).unwrap(); 15 | 16 | cursor.set_position(0); 17 | let full_copy = <$ty>::deserialize_full(&mut cursor).unwrap(); 18 | assert_eq!($data, full_copy); 19 | 20 | let eps_copy = <$ty>::deserialize_eps(cursor.as_bytes()).unwrap(); 21 | assert_eq!($data, *eps_copy); 22 | } 23 | { 24 | let mut cursor = >::new(); 25 | $data.serialize(&mut cursor).unwrap(); 26 | 27 | cursor.set_position(0); 28 | let full_copy = <$ty>::deserialize_full(&mut cursor).unwrap(); 29 | assert_eq!($data, full_copy); 30 | 31 | let eps_copy = <$ty>::deserialize_eps(cursor.as_bytes()).unwrap(); 32 | assert_eq!($data, *eps_copy); 33 | }}; 34 | } 35 | 36 | macro_rules! test_zero { 37 | ($test_name:ident, $ty:ty, $data: expr) => { 38 | #[test] 39 | fn $test_name() { 40 | impl_test!($ty, $data); 41 | } 42 | }; 43 | } 44 | 45 | test_zero!(test_array_i32_2, [i32; 2], [-1, 1]); 46 | test_zero!(test_array_i64_2, [i64; 2], [-1_i64, 1]); 47 | test_zero!(test_tuple_0, (i32, i32), (-1_i32, 1_i32)); 48 | test_zero!(test_tuple_1, (i64, i64), (-1_i64, 1_i64)); 49 | test_zero!( 50 | test_tuple_2, 51 | ((i64, i64), (i64, i64)), 52 | ((-1_i64, 1_i64), (-1_i64, 1_i64)) 53 | ); 54 | test_zero!( 55 | test_tuple_3, 56 | ((i32, i32), (i32, i32)), 57 | ((-1_i32, 1_i32), (-1_i32, 1_i32)) 58 | ); 59 | --------------------------------------------------------------------------------