├── .github └── workflows │ └── test.yml ├── .gitignore ├── .gitlab-ci.yml ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── TODO.md ├── base ├── Cargo.toml └── src │ ├── emplacer.rs │ ├── error.rs │ ├── lib.rs │ ├── primitive.rs │ ├── traits.rs │ └── utils │ ├── iter.rs │ ├── mem.rs │ └── mod.rs ├── containers ├── Cargo.toml └── src │ ├── bytes.rs │ ├── flex.rs │ ├── lib.rs │ ├── string.rs │ ├── vec.rs │ └── wrap.rs ├── io ├── Cargo.toml └── src │ ├── async_ │ ├── io.rs │ ├── mod.rs │ ├── recv.rs │ └── send.rs │ ├── blocking │ ├── io.rs │ ├── mod.rs │ ├── recv.rs │ └── send.rs │ ├── common │ ├── error.rs │ ├── io.rs │ └── mod.rs │ ├── lib.rs │ └── tests │ ├── async_.rs │ ├── blocking.rs │ ├── common.rs │ └── mod.rs ├── macros ├── Cargo.toml └── src │ ├── context.rs │ ├── info.rs │ ├── items │ ├── align_as.rs │ ├── base.rs │ ├── cast.rs │ ├── enum_.rs │ ├── flat.rs │ ├── init.rs │ ├── mod.rs │ ├── portable.rs │ ├── tag.rs │ ├── unsized_.rs │ └── unsized_enum.rs │ ├── lib.rs │ └── utils │ ├── field_iter.rs │ ├── generic.rs │ └── mod.rs ├── portable ├── Cargo.toml └── src │ ├── bool_.rs │ ├── float.rs │ ├── impl_.rs │ ├── int.rs │ ├── lib.rs │ └── tests.rs ├── src └── lib.rs └── tests ├── Cargo.toml └── src ├── c_like_enum.rs ├── generics.rs ├── lib.rs ├── not_default.rs ├── portable.rs ├── sized_enum ├── auto.rs ├── manual.rs ├── mod.rs └── tests.rs ├── sized_struct ├── auto.rs ├── manual.rs ├── mod.rs └── tests.rs ├── unsized_enum ├── auto.rs ├── manual.rs ├── mod.rs └── tests.rs ├── unsized_sized_enum.rs └── unsized_struct ├── auto.rs ├── manual.rs ├── mod.rs └── tests.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | jobs: 4 | test: 5 | name: flatty 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v2 9 | - uses: actions-rs/toolchain@v1 10 | with: 11 | profile: minimal 12 | toolchain: stable 13 | override: true 14 | - uses: actions-rs/cargo@v1 15 | with: 16 | command: test 17 | args: --all 18 | - uses: actions-rs/cargo@v1 19 | with: 20 | command: check 21 | args: --no-default-features 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | 4 | .vscode 5 | *.code-workspace 6 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | default: 2 | image: rust 3 | 4 | test-all: 5 | stage: test 6 | script: 7 | - cargo test --all 8 | 9 | check-all-no-default-features: 10 | stage: test 11 | script: 12 | - cargo check --all --no-default-features 13 | 14 | check-alloc: 15 | stage: test 16 | script: 17 | - cargo check --no-default-features --features=alloc 18 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 127 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace.package] 2 | version = "0.1.0-rc.6" 3 | edition = "2021" 4 | rust-version = "1.69" 5 | 6 | authors = ["Alexey Gerasev "] 7 | homepage = "https://gitlab.com/agerasev/flatty" 8 | repository = "https://gitlab.com/agerasev/flatty.git" 9 | license = "MIT/Apache-2.0" 10 | readme = "README.md" 11 | 12 | [workspace.dependencies] 13 | flatty-macros = { path = "macros", version = "0.1.0-rc.6" } 14 | flatty-base = { path = "base", version = "0.1.0-rc.6", default-features = false } 15 | flatty-portable = { path = "portable", version = "0.1.0-rc.6", default-features = false } 16 | flatty-containers = { path = "containers", version = "0.1.0-rc.6", default-features = false } 17 | flatty = { path = ".", version = "0.1.0-rc.6", default-features = false } 18 | 19 | [package] 20 | name = "flatty" 21 | version.workspace = true 22 | edition.workspace = true 23 | rust-version.workspace = true 24 | 25 | description = "Flat message buffers" 26 | authors.workspace = true 27 | homepage.workspace = true 28 | repository.workspace = true 29 | license.workspace = true 30 | readme.workspace = true 31 | documentation = "https://docs.rs/flatty" 32 | 33 | [features] 34 | default = ["std"] 35 | std = ["alloc", "flatty-containers/std"] 36 | alloc = ["flatty-containers/alloc"] 37 | serde = ["flatty-portable/serde"] 38 | 39 | [dependencies] 40 | flatty-macros = { workspace = true } 41 | flatty-base = { workspace = true } 42 | flatty-portable = { workspace = true } 43 | flatty-containers = { workspace = true } 44 | 45 | [workspace] 46 | members = ["io", "tests"] 47 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Alexey Gerasev 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flatty 2 | 3 | [![Crates.io][crates_badge]][crates] 4 | [![Docs.rs][docs_badge]][docs] 5 | [![Gitlab CI][gitlab_badge]][gitlab] 6 | [![License][license_badge]][license] 7 | 8 | [crates_badge]: https://img.shields.io/crates/v/flatty.svg 9 | [docs_badge]: https://docs.rs/flatty/badge.svg 10 | [gitlab_badge]: https://gitlab.com/agerasev/flatty/badges/master/pipeline.svg 11 | [license_badge]: https://img.shields.io/crates/l/flatty.svg 12 | 13 | [crates]: https://crates.io/crates/flatty 14 | [docs]: https://docs.rs/flatty 15 | [gitlab]: https://gitlab.com/agerasev/flatty/-/pipelines?scope=branches&ref=master 16 | [license]: #license 17 | 18 | Flat message buffers with direct mapping to Rust types without packing/unpacking. 19 | 20 | ## Overview 21 | 22 | Type called flat when it occupies a single contiguous memory area. Such types is useful when you need to store or send such object as a binary data while having convenient way to access its contents without serializing/deserializing. 23 | 24 | This crate provides basic flat types and the way to create new user-defined composite flat types (using `#[flat]` attribute macro), which can be used almost like regular Rust `struct`s or `enum`s. 25 | 26 | Also the crate can be used without `std` and even `alloc`. 27 | 28 | ## Concepts 29 | 30 | ### Conversion 31 | 32 | Binary representation can be obtained from instances of flat type using `as_bytes` (and unsafe `as_mut_bytes`). Also bytes can be converted to flat types, see [in-place initialization](#in-place-initialization) and [validation](#validation). 33 | 34 | ### [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html) 35 | 36 | Flat type can be dynamically sized (like `FlatVec`), in that case it exploits Rust's ability to operate `?Sized` types. User-defined flat struct can also be unsized (only last field is allowed to be unsized). Even flat enum can be unsized, but Rust doesn't natively support them yet so its contents could be accessed only via `as_ref`/`as_mut` methods returning a regular enum containing references to original enum contents. 37 | 38 | ### In-place initialization 39 | 40 | Sized types can be instantiated as usual Rust type. But in case of DST Rust cannot construct it in usual manner on stack because its size isn't known at compile time. Instead we may initialize such types onto given memory area. 41 | 42 | To do this we can use so-called emplacer - something that can initialize object onto given memory. For sized types its instance is also emplacer. 43 | 44 | Emplacer could be applied to raw bytes using `new_in_place` or replace existing struct contents using `assign_in_place`. Also some types has default emplacer and could be initialized in default state by `default_in_place`. 45 | 46 | ### Validation 47 | 48 | Not any combination of bytes are valid representation of flat type. For example, `Bool` has only two valid states: `0` and `1`, or `FlatVec` length must not be greater than its capacity. `validate` can be used to check that data is valid for specific flat type, or `from_bytes`/`from_mut_bytes` also perform such check. 49 | 50 | When you trust your data, then you can omit validation using unsafe `from_bytes_unchecked`/`from_mut_bytes_unchecked`, but this will cause an UB if data is invalid. 51 | 52 | ### Portability 53 | 54 | Flat type guarantee that it has the same binary representation on the platforms with same byte order, alignment and address width. If you need stronger guarantees you may use `Portable` types - they have the same binary representation on *any* platform and always aligned to byte. To make own flat type portable use `#[flat(portable = true)]`. Also this can be used to created packed flat types without alignment issues. 55 | 56 | ## Examples 57 | 58 | You can find some examples on how to create and use flat types in [`tests`](tests/src/) directory. 59 | 60 | ## License 61 | 62 | Licensed under either of 63 | 64 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 65 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 66 | 67 | at your option. 68 | 69 | ### Contribution 70 | 71 | Unless you explicitly state otherwise, any contribution intentionally submitted 72 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any 73 | additional terms or conditions. 74 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | + Support for `Box`-ed flat types. 4 | + Add to README brief comparison to [Postcard](https://github.com/jamesmunns/postcard), [rkiv](https://github.com/rkyv/rkyv), [FlatBuffers](https://github.com/google/flatbuffers), [Cap'n Proto](https://github.com/capnproto/capnproto), etc. 5 | -------------------------------------------------------------------------------- /base/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-base" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | description = "Flatty traits and primitives" 7 | authors.workspace = true 8 | repository.workspace = true 9 | license.workspace = true 10 | -------------------------------------------------------------------------------- /base/src/emplacer.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::Error, 3 | traits::{FlatSized, FlatUnsized}, 4 | utils::mem::check_align_and_min_size, 5 | }; 6 | 7 | /// In-place initializer of flat type. 8 | pub unsafe trait Emplacer: Sized { 9 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut T, Error>; 10 | 11 | /// Apply initializer for uninitialized memory. 12 | fn emplace(self, bytes: &mut [u8]) -> Result<&mut T, Error> { 13 | check_align_and_min_size::(bytes)?; 14 | unsafe { self.emplace_unchecked(bytes) } 15 | } 16 | } 17 | 18 | /// Emplacer that cannot be instantiated and used as a placeholder for unused parameters. 19 | pub enum NeverEmplacer {} 20 | 21 | unsafe impl Emplacer for NeverEmplacer { 22 | unsafe fn emplace_unchecked(self, _: &mut [u8]) -> Result<&mut T, Error> { 23 | unreachable!() 24 | } 25 | } 26 | 27 | unsafe impl Emplacer for T { 28 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut T, Error> { 29 | let ptr = Self::ptr_from_bytes(bytes); 30 | unsafe { ptr.write(self) }; 31 | Ok(unsafe { &mut *ptr }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /base/src/error.rs: -------------------------------------------------------------------------------- 1 | /// Flat type operation error. 2 | #[derive(Clone, Debug, PartialEq, Eq)] 3 | pub struct Error { 4 | pub kind: ErrorKind, 5 | /// An offset from the beginning of flat type memory where error occur. 6 | pub pos: usize, 7 | } 8 | 9 | /// Error kinds that can occur while working with flat types. 10 | #[derive(Clone, Debug, PartialEq, Eq)] 11 | pub enum ErrorKind { 12 | /// The byte slice has insufficient size to be interpreted as required type. 13 | InsufficientSize, 14 | /// Memory isn't properly aligned to be interpreted as required type. 15 | BadAlign, 16 | /// Enum binary representation contains index that doesn't match any of possible enum states. 17 | InvalidEnumTag, 18 | /// Invalid binary representation of the type. 19 | InvalidData, 20 | /// Any other error. 21 | Other, 22 | } 23 | 24 | impl Error { 25 | /// Clone `self` and add `offset` to [`Self::pos`]. 26 | pub fn offset(mut self, offset: usize) -> Self { 27 | self.pos += offset; 28 | self 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /base/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![allow(clippy::missing_safety_doc)] 3 | 4 | /// Emplacer-related functionality. 5 | pub mod emplacer; 6 | /// Error type. 7 | pub mod error; 8 | /// Primitive types. 9 | mod primitive; 10 | /// Traits for flat types. 11 | pub mod traits; 12 | /// Utility functions used by macros, so they must be publicly available. 13 | /// 14 | /// *Please, don't use them by yourself because they aren't stable.* 15 | pub mod utils; 16 | -------------------------------------------------------------------------------- /base/src/primitive.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::Error, 3 | traits::{Flat, FlatSized, FlatValidate}, 4 | }; 5 | use core::marker::PhantomData; 6 | 7 | /// Macro for implementing [`Flat`] for primitive types. 8 | /// 9 | /// # Safety 10 | /// 11 | /// `$ty` must be [`Sized`]` + `[`Copy`]. 12 | /// 13 | /// Any possible memory state of the variable of the type must be valid. 14 | macro_rules! impl_flat_prim { 15 | ($ty:ty) => { 16 | unsafe impl FlatValidate for $ty { 17 | unsafe fn validate_unchecked(_: &[u8]) -> Result<(), Error> { 18 | Ok(()) 19 | } 20 | } 21 | unsafe impl Flat for $ty {} 22 | }; 23 | } 24 | 25 | impl_flat_prim!(()); 26 | 27 | impl_flat_prim!(u8); 28 | impl_flat_prim!(u16); 29 | impl_flat_prim!(u32); 30 | impl_flat_prim!(u64); 31 | impl_flat_prim!(u128); 32 | impl_flat_prim!(usize); 33 | 34 | impl_flat_prim!(i8); 35 | impl_flat_prim!(i16); 36 | impl_flat_prim!(i32); 37 | impl_flat_prim!(i64); 38 | impl_flat_prim!(i128); 39 | impl_flat_prim!(isize); 40 | 41 | impl_flat_prim!(f32); 42 | impl_flat_prim!(f64); 43 | 44 | unsafe impl FlatValidate for PhantomData { 45 | unsafe fn validate_unchecked(_: &[u8]) -> Result<(), Error> { 46 | Ok(()) 47 | } 48 | } 49 | unsafe impl Flat for PhantomData {} 50 | 51 | unsafe impl FlatValidate for [T; N] { 52 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 53 | for i in 0..N { 54 | T::validate_unchecked(bytes.get_unchecked((i * T::SIZE)..).get_unchecked(..T::SIZE))?; 55 | } 56 | Ok(()) 57 | } 58 | } 59 | unsafe impl Flat for [T; N] {} 60 | -------------------------------------------------------------------------------- /base/src/traits.rs: -------------------------------------------------------------------------------- 1 | use crate::{emplacer::Emplacer, error::Error, utils::mem::check_align_and_min_size}; 2 | use core::{ 3 | mem::{align_of, size_of}, 4 | ptr, 5 | }; 6 | 7 | /// Basic flat type properties. 8 | pub unsafe trait FlatBase: Send + Sync { 9 | /// Align of the type. 10 | const ALIGN: usize; 11 | /// Minimal size of an instance of the type. 12 | const MIN_SIZE: usize; 13 | 14 | /// Size of an instance of the type. 15 | fn size(&self) -> usize; 16 | } 17 | 18 | /// Dynamically-sized flat type. Like `?Sized` but for `Flat`. 19 | /// 20 | /// *For now has to be implemented for all [`Flat`] types because there is no mutually exclusive traits in Rust yet.* 21 | pub unsafe trait FlatUnsized: FlatBase { 22 | /// Sized type that has the same alignment as `Self`. 23 | type AlignAs: Sized + Send + Sync; 24 | 25 | unsafe fn ptr_from_bytes(bytes: *mut [u8]) -> *mut Self; 26 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8]; 27 | 28 | unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self { 29 | &*Self::ptr_from_bytes(bytes as *const _ as *mut _) 30 | } 31 | unsafe fn from_mut_bytes_unchecked(bytes: &mut [u8]) -> &mut Self { 32 | &mut *Self::ptr_from_bytes(bytes) 33 | } 34 | fn as_bytes(&self) -> &[u8] { 35 | unsafe { &*Self::ptr_to_bytes(self as *const _ as *mut _) } 36 | } 37 | /// # Safety 38 | /// 39 | /// Modification of returned bytes must not make `self` invalid. 40 | unsafe fn as_mut_bytes(&mut self) -> &mut [u8] { 41 | unsafe { &mut *Self::ptr_to_bytes(self as *mut _) } 42 | } 43 | 44 | /// Create a new instance of `Self` initializing raw memory into default state of `Self`. 45 | fn new_in_place>(bytes: &mut [u8], emplacer: I) -> Result<&mut Self, Error> { 46 | emplacer.emplace(bytes)?; 47 | Ok(unsafe { Self::from_mut_bytes_unchecked(bytes) }) 48 | } 49 | fn assign_in_place>(&mut self, emplacer: I) -> Result<&mut Self, Error> { 50 | unsafe { 51 | let bytes = self.as_mut_bytes(); 52 | emplacer.emplace_unchecked(bytes)?; 53 | Ok(Self::from_mut_bytes_unchecked(bytes)) 54 | } 55 | } 56 | } 57 | 58 | /// Flat type runtime checking. 59 | pub unsafe trait FlatValidate: FlatUnsized { 60 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error>; 61 | 62 | unsafe fn validate_ptr(this: *const Self) -> Result<(), Error> { 63 | unsafe { Self::validate_unchecked(&*Self::ptr_to_bytes(this as *mut _)) } 64 | } 65 | 66 | /// Check that memory contents of `this` is valid for `Self`. 67 | fn validate(bytes: &[u8]) -> Result<(), Error> { 68 | check_align_and_min_size::(bytes)?; 69 | unsafe { Self::validate_unchecked(bytes) } 70 | } 71 | 72 | fn from_bytes(bytes: &[u8]) -> Result<&Self, Error> { 73 | Self::validate(bytes)?; 74 | Ok(unsafe { Self::from_bytes_unchecked(bytes) }) 75 | } 76 | fn from_mut_bytes(bytes: &mut [u8]) -> Result<&mut Self, Error> { 77 | Self::validate(bytes)?; 78 | Ok(unsafe { Self::from_mut_bytes_unchecked(bytes) }) 79 | } 80 | } 81 | 82 | /// Flat type. 83 | /// 84 | /// *If you want to implement this type for your custom type it's recommended to use safe `#[flat]` attribute macro instead.* 85 | /// 86 | /// # Safety 87 | /// 88 | /// By implementing this trait by yourself you guarantee: 89 | /// 90 | /// + `Self` has stable binary representation that will not change in future. 91 | /// (But the representation could be differ across different platforms. If you need stronger guarantees consider using `Portable` types.) 92 | /// + `Self` don't own any resources outside of it. 93 | /// + `Self` could be trivially copied as bytes. (We cannot require `Self: `[`Copy`] because it `?Sized`.) 94 | /// + All methods of dependent traits have proper implementation and will not cause an UB. 95 | pub unsafe trait Flat: FlatBase + FlatUnsized + FlatValidate {} 96 | 97 | /// Statically-sized flat type. 98 | /// 99 | /// # Safety 100 | /// 101 | /// `SIZE` must match `Self` size. 102 | pub unsafe trait FlatSized: FlatUnsized + Sized { 103 | /// Static size of the type. 104 | const SIZE: usize = size_of::(); 105 | } 106 | 107 | unsafe impl FlatSized for T {} 108 | 109 | unsafe impl FlatBase for T { 110 | const ALIGN: usize = align_of::(); 111 | 112 | const MIN_SIZE: usize = Self::SIZE; 113 | 114 | fn size(&self) -> usize { 115 | Self::SIZE 116 | } 117 | } 118 | 119 | unsafe impl FlatUnsized for T { 120 | type AlignAs = T; 121 | 122 | unsafe fn ptr_from_bytes(bytes: *mut [u8]) -> *mut Self { 123 | bytes as *mut Self 124 | } 125 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 126 | ptr::slice_from_raw_parts_mut(this as *mut u8, Self::SIZE) 127 | } 128 | } 129 | 130 | /// Flat types that can be initialized to default state. 131 | /// 132 | /// # Safety 133 | /// 134 | /// Methods must properly initialize memory. 135 | pub trait FlatDefault: Flat { 136 | type DefaultEmplacer: Emplacer; 137 | 138 | /// Initialize uninitialized memory into valid default state. 139 | /// 140 | /// This method returned `Ok` must guaratee that `this` could be safely transmuted to `Self`. 141 | fn default_emplacer() -> Self::DefaultEmplacer; 142 | 143 | /// Create a new instance of `Self` initializing raw memory into default state of `Self`. 144 | fn default_in_place(bytes: &mut [u8]) -> Result<&mut Self, Error> { 145 | Self::new_in_place(bytes, Self::default_emplacer()) 146 | } 147 | } 148 | 149 | impl FlatDefault for T { 150 | type DefaultEmplacer = Self; 151 | 152 | fn default_emplacer() -> Self::DefaultEmplacer { 153 | Self::default() 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /base/src/utils/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{Error, ErrorKind}, 3 | traits::*, 4 | utils::{ceil_mul, max}, 5 | }; 6 | use core::marker::PhantomData; 7 | 8 | pub trait TypeIter: Clone { 9 | type Item: Flat + ?Sized; 10 | fn align(&self) -> usize; 11 | fn min_size(&self, pos: usize) -> usize; 12 | 13 | fn check_align_and_min_size(&self, data: &[u8]) -> Result<(), Error> { 14 | if data.as_ptr().align_offset(self.align()) != 0 { 15 | Err(Error { 16 | kind: ErrorKind::BadAlign, 17 | pos: 0, 18 | }) 19 | } else if data.len() < self.min_size(0) { 20 | Err(Error { 21 | kind: ErrorKind::InsufficientSize, 22 | pos: 0, 23 | }) 24 | } else { 25 | Ok(()) 26 | } 27 | } 28 | } 29 | 30 | #[derive(Debug)] 31 | pub struct SingleType { 32 | _phantom: PhantomData, 33 | } 34 | impl Clone for SingleType { 35 | fn clone(&self) -> Self { 36 | Self { _phantom: PhantomData } 37 | } 38 | } 39 | impl SingleType { 40 | #[allow(clippy::new_without_default)] 41 | pub fn new() -> Self { 42 | Self { _phantom: PhantomData } 43 | } 44 | } 45 | impl TypeIter for SingleType { 46 | type Item = T; 47 | fn align(&self) -> usize { 48 | T::ALIGN 49 | } 50 | fn min_size(&self, pos: usize) -> usize { 51 | ceil_mul(pos, T::ALIGN) + T::MIN_SIZE 52 | } 53 | } 54 | 55 | #[derive(Debug)] 56 | pub struct TwoOrMoreTypes { 57 | _phantom: PhantomData, 58 | next: I, 59 | } 60 | impl Clone for TwoOrMoreTypes { 61 | fn clone(&self) -> Self { 62 | Self { 63 | _phantom: PhantomData, 64 | next: self.next.clone(), 65 | } 66 | } 67 | } 68 | impl TwoOrMoreTypes { 69 | pub fn new(next: I) -> Self { 70 | Self { 71 | _phantom: PhantomData, 72 | next, 73 | } 74 | } 75 | pub fn next(self) -> I { 76 | self.next 77 | } 78 | } 79 | impl TypeIter for TwoOrMoreTypes { 80 | type Item = T; 81 | fn align(&self) -> usize { 82 | max(T::ALIGN, self.next.align()) 83 | } 84 | fn min_size(&self, pos: usize) -> usize { 85 | self.next.min_size(ceil_mul(pos, T::ALIGN) + T::SIZE) 86 | } 87 | } 88 | 89 | #[derive(Clone, Debug)] 90 | pub struct PosIter { 91 | pos: usize, 92 | iter: I, 93 | } 94 | impl PosIter { 95 | pub fn new(iter: I) -> Self { 96 | Self { pos: 0, iter } 97 | } 98 | pub fn pos(&self) -> usize { 99 | self.pos 100 | } 101 | } 102 | impl PosIter> { 103 | pub fn next(self) -> PosIter { 104 | PosIter { 105 | pos: ceil_mul(self.pos + T::SIZE, I::Item::ALIGN), 106 | iter: self.iter.next(), 107 | } 108 | } 109 | } 110 | impl PosIter> { 111 | pub fn assert_last(&self) { 112 | // Nothing to do here, this is a static assert. 113 | } 114 | } 115 | 116 | pub trait Data<'a>: Sized + 'a { 117 | fn bytes(&self) -> &'_ [u8]; 118 | type Output: Sized; 119 | fn split(self, pos: usize) -> (Self, Self); 120 | fn value(self) -> Self::Output; 121 | } 122 | 123 | impl<'a> Data<'a> for &'a [u8] { 124 | fn bytes(&self) -> &'_ [u8] { 125 | self 126 | } 127 | type Output = &'a [u8]; 128 | fn split(self, pos: usize) -> (Self, Self) { 129 | self.split_at(pos) 130 | } 131 | fn value(self) -> Self::Output { 132 | self 133 | } 134 | } 135 | 136 | impl<'a> Data<'a> for &'a mut [u8] { 137 | fn bytes(&self) -> &'_ [u8] { 138 | self 139 | } 140 | type Output = &'a mut [u8]; 141 | fn split(self, pos: usize) -> (Self, Self) { 142 | self.split_at_mut(pos) 143 | } 144 | fn value(self) -> Self::Output { 145 | self 146 | } 147 | } 148 | 149 | #[derive(Clone, Debug)] 150 | pub struct RefData<'a>(pub &'a [u8]); 151 | impl<'a> Data<'a> for RefData<'a> { 152 | fn bytes(&self) -> &'_ [u8] { 153 | self.0.bytes() 154 | } 155 | type Output = Result<&'a T, Error>; 156 | fn split(self, pos: usize) -> (Self, Self) { 157 | let (a, b) = Data::split(self.0, pos); 158 | (Self(a), Self(b)) 159 | } 160 | fn value(self) -> Self::Output { 161 | T::from_bytes(self.0.value::()) 162 | } 163 | } 164 | 165 | #[derive(Debug)] 166 | pub struct MutData<'a>(pub &'a mut [u8]); 167 | impl<'a> Data<'a> for MutData<'a> { 168 | fn bytes(&self) -> &'_ [u8] { 169 | self.0.bytes() 170 | } 171 | type Output = Result<&'a mut T, Error>; 172 | fn split(self, pos: usize) -> (Self, Self) { 173 | let (a, b) = Data::split(self.0, pos); 174 | (Self(a), Self(b)) 175 | } 176 | fn value(self) -> Self::Output { 177 | T::from_mut_bytes(self.0.value::()) 178 | } 179 | } 180 | 181 | #[derive(Clone, Debug)] 182 | pub struct UncheckedRefData<'a>(&'a [u8]); 183 | impl<'a> UncheckedRefData<'a> { 184 | pub unsafe fn new(data: &'a [u8]) -> Self { 185 | Self(data) 186 | } 187 | } 188 | impl<'a> Data<'a> for UncheckedRefData<'a> { 189 | fn bytes(&self) -> &'_ [u8] { 190 | self.0.bytes() 191 | } 192 | type Output = &'a T; 193 | fn split(self, pos: usize) -> (Self, Self) { 194 | let (a, b) = Data::split(self.0, pos); 195 | (Self(a), Self(b)) 196 | } 197 | fn value(self) -> Self::Output { 198 | unsafe { T::from_bytes_unchecked(self.0.value::()) } 199 | } 200 | } 201 | 202 | #[derive(Debug)] 203 | pub struct UncheckedMutData<'a>(&'a mut [u8]); 204 | impl<'a> UncheckedMutData<'a> { 205 | pub unsafe fn new(data: &'a mut [u8]) -> Self { 206 | Self(data) 207 | } 208 | } 209 | impl<'a> Data<'a> for UncheckedMutData<'a> { 210 | fn bytes(&self) -> &'_ [u8] { 211 | self.0.bytes() 212 | } 213 | type Output = &'a mut T; 214 | fn split(self, pos: usize) -> (Self, Self) { 215 | let (a, b) = Data::split(self.0, pos); 216 | (Self(a), Self(b)) 217 | } 218 | fn value(self) -> Self::Output { 219 | unsafe { T::from_mut_bytes_unchecked(self.0.value::()) } 220 | } 221 | } 222 | 223 | #[derive(Debug)] 224 | pub struct DataIter<'a, D: Data<'a>, I: TypeIter> { 225 | _ghost: PhantomData<&'a ()>, 226 | data: D, 227 | iter: PosIter, 228 | } 229 | impl<'a, D: Data<'a> + Clone, I: TypeIter> Clone for DataIter<'a, D, I> { 230 | fn clone(&self) -> Self { 231 | Self { 232 | _ghost: self._ghost, 233 | data: self.data.clone(), 234 | iter: self.iter.clone(), 235 | } 236 | } 237 | } 238 | impl<'a, D: Data<'a>, I: TypeIter> DataIter<'a, D, I> { 239 | pub fn new(data: D, iter: I) -> Result { 240 | iter.check_align_and_min_size(data.bytes())?; 241 | Ok(unsafe { Self::new_unchecked(data, iter) }) 242 | } 243 | /// # Safety 244 | /// 245 | /// `data` must be aligned and have sufficient size. 246 | pub unsafe fn new_unchecked(data: D, iter: I) -> Self { 247 | Self { 248 | _ghost: PhantomData, 249 | data, 250 | iter: PosIter::new(iter), 251 | } 252 | } 253 | pub fn pos(&self) -> usize { 254 | self.iter.pos() 255 | } 256 | pub fn value(self) -> D::Output { 257 | self.data.value() 258 | } 259 | } 260 | impl<'a, D: Data<'a>, T: Flat + Sized, I: TypeIter> DataIter<'a, D, TwoOrMoreTypes> { 261 | pub fn next(self) -> (DataIter<'a, D, I>, D::Output) { 262 | let prev_pos = self.iter.pos(); 263 | let iter = self.iter.next(); 264 | let next_pos = iter.pos(); 265 | let (prev_data, next_data) = self.data.split(next_pos - prev_pos); 266 | ( 267 | DataIter { 268 | _ghost: PhantomData, 269 | data: next_data, 270 | iter, 271 | }, 272 | prev_data.value(), 273 | ) 274 | } 275 | } 276 | impl<'a, D: Data<'a>, T: Flat + ?Sized> DataIter<'a, D, SingleType> { 277 | pub fn assert_last(&self) { 278 | self.iter.assert_last() 279 | } 280 | pub fn finalize(self) -> D::Output { 281 | self.data.value() 282 | } 283 | } 284 | 285 | pub type BytesIter<'a, I> = DataIter<'a, &'a [u8], I>; 286 | pub type BytesMutIter<'a, I> = DataIter<'a, &'a mut [u8], I>; 287 | pub type RefIter<'a, I> = DataIter<'a, UncheckedRefData<'a>, I>; 288 | pub type MutIter<'a, I> = DataIter<'a, UncheckedMutData<'a>, I>; 289 | 290 | pub trait ValidateIter { 291 | fn validate_all(self) -> Result<(), Error>; 292 | } 293 | impl<'a, T: Flat + Sized + 'a, I: TypeIter> ValidateIter for BytesIter<'a, TwoOrMoreTypes> 294 | where 295 | BytesIter<'a, I>: ValidateIter, 296 | I::Item: 'a, 297 | { 298 | fn validate_all(self) -> Result<(), Error> { 299 | unsafe { T::validate_unchecked(self.clone().value()) }.map_err(|e| e.offset(self.pos()))?; 300 | self.next().0.validate_all() 301 | } 302 | } 303 | 304 | impl<'a, T: Flat + ?Sized> ValidateIter for BytesIter<'a, SingleType> { 305 | fn validate_all(self) -> Result<(), Error> { 306 | self.assert_last(); 307 | unsafe { T::validate_unchecked(self.clone().value()) }.map_err(|e| e.offset(self.pos()))?; 308 | Ok(()) 309 | } 310 | } 311 | 312 | pub trait FoldSizeIter { 313 | /// # Safety 314 | /// 315 | /// Internal data must be valid. 316 | unsafe fn fold_size(self, size: usize) -> usize; 317 | } 318 | impl<'a, T: Flat + Sized + 'a, I: TypeIter> FoldSizeIter for BytesIter<'a, TwoOrMoreTypes> 319 | where 320 | BytesIter<'a, I>: FoldSizeIter, 321 | I::Item: 'a, 322 | { 323 | unsafe fn fold_size(self, size: usize) -> usize { 324 | self.next().0.fold_size(ceil_mul(size, T::ALIGN) + T::SIZE) 325 | } 326 | } 327 | impl<'a, T: Flat + ?Sized> FoldSizeIter for BytesIter<'a, SingleType> { 328 | unsafe fn fold_size(self, size: usize) -> usize { 329 | ceil_mul(size, T::ALIGN) + (*T::ptr_from_bytes(self.finalize() as *const _ as *mut _)).size() 330 | } 331 | } 332 | 333 | pub mod prelude { 334 | pub use super::{FoldSizeIter, TypeIter, ValidateIter}; 335 | } 336 | 337 | #[doc(hidden)] 338 | #[macro_export] 339 | macro_rules! type_list { 340 | ($first_type:ty, $($types:ty),+ $(,)?) => { 341 | $crate::utils::iter::TwoOrMoreTypes::<$first_type, _>::new($crate::utils::iter::type_list!($( $types ),*)) 342 | }; 343 | ($type:ty $(,)?) => { 344 | $crate::utils::iter::SingleType::<$type>::new() 345 | }; 346 | } 347 | 348 | #[doc(hidden)] 349 | #[macro_export] 350 | macro_rules! fold_size { 351 | ($accum:expr; $first_type:ty, $($types:ty),+ $(,)?) => { 352 | $crate::utils::iter::fold_size!( 353 | $crate::utils::ceil_mul($accum, <$first_type as $crate::traits::FlatBase>::ALIGN) + <$first_type as $crate::traits::FlatSized>::SIZE; 354 | $( $types ),* 355 | ) 356 | }; 357 | ($accum:expr; $type:ty $(,)?) => { 358 | $crate::utils::ceil_mul($accum, <$type as $crate::traits::FlatBase>::ALIGN) + <$type as $crate::traits::FlatSized>::SIZE 359 | }; 360 | } 361 | 362 | #[doc(hidden)] 363 | #[macro_export] 364 | macro_rules! fold_min_size { 365 | ($accum:expr; $first_type:ty, $($types:ty),+ $(,)?) => { 366 | $crate::utils::iter::fold_min_size!( 367 | $crate::utils::ceil_mul($accum, <$first_type as $crate::traits::FlatBase>::ALIGN) + <$first_type as $crate::traits::FlatSized>::SIZE; 368 | $( $types ),* 369 | ) 370 | }; 371 | ($accum:expr; $type:ty $(,)?) => { 372 | $crate::utils::ceil_mul($accum, <$type as $crate::traits::FlatBase>::ALIGN) + <$type as $crate::traits::FlatBase>::MIN_SIZE 373 | }; 374 | } 375 | 376 | pub use {fold_min_size, fold_size, type_list}; 377 | 378 | #[cfg(test)] 379 | mod tests { 380 | use super::{type_list, PosIter}; 381 | 382 | #[test] 383 | fn pos() { 384 | let iter = PosIter::new(type_list!(u8, u16, u32)); 385 | assert_eq!(iter.pos(), 0); 386 | let iter = iter.next(); 387 | assert_eq!(iter.pos(), 2); 388 | let iter = iter.next(); 389 | assert_eq!(iter.pos(), 4); 390 | iter.assert_last(); 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /base/src/utils/mem.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{Error, ErrorKind}, 3 | traits::FlatBase, 4 | }; 5 | use core::{ptr, ptr::NonNull}; 6 | 7 | /// Check that memory size and alignment are suitable for `Self`. 8 | pub fn check_align_and_min_size(bytes: &[u8]) -> Result<(), Error> { 9 | if bytes.as_ptr().align_offset(T::ALIGN) != 0 { 10 | Err(Error { 11 | kind: ErrorKind::BadAlign, 12 | pos: 0, 13 | }) 14 | } else if bytes.len() < T::MIN_SIZE { 15 | Err(Error { 16 | kind: ErrorKind::InsufficientSize, 17 | pos: 0, 18 | }) 19 | } else { 20 | Ok(()) 21 | } 22 | } 23 | 24 | pub unsafe fn slice_ptr_len(slice: *mut [T]) -> usize { 25 | unsafe { NonNull::new_unchecked(slice) }.len() 26 | } 27 | 28 | pub unsafe fn set_slice_ptr_len(bytes: *mut [T], len: usize) -> *mut [T] { 29 | ptr::slice_from_raw_parts_mut(bytes as *mut T, len) 30 | } 31 | 32 | pub unsafe fn offset_slice_ptr_start(slice: *mut [T], count: isize) -> *mut [T] { 33 | let (ptr, len) = (slice as *mut T, slice_ptr_len(slice)); 34 | ptr::slice_from_raw_parts_mut(ptr.offset(count), (len as isize - count) as usize) 35 | } 36 | 37 | #[doc(hidden)] 38 | #[macro_export] 39 | macro_rules! cast_wide_ptr_with_offset { 40 | ($Type:ty, $ptr:expr, $count:expr $(,)?) => {{ 41 | let wptr = $ptr as *mut [u8]; 42 | let (ptr, len) = (wptr as *mut u8, ::core::ptr::NonNull::new_unchecked(wptr as *mut [u8]).len()); 43 | ::core::ptr::slice_from_raw_parts_mut(ptr.offset($count as isize), len) as *mut $Type 44 | }}; 45 | } 46 | pub use cast_wide_ptr_with_offset; 47 | -------------------------------------------------------------------------------- /base/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod iter; 2 | pub mod mem; 3 | 4 | /// `const` version of [`usize::max`]. 5 | pub const fn max(a: usize, b: usize) -> usize { 6 | if a >= b { 7 | a 8 | } else { 9 | b 10 | } 11 | } 12 | 13 | /// `const` version of [`usize::min`]. 14 | pub const fn min(a: usize, b: usize) -> usize { 15 | if a <= b { 16 | a 17 | } else { 18 | b 19 | } 20 | } 21 | 22 | /// Smallest number that is both greater or equal to `x` and a multiple of `m`. 23 | pub const fn ceil_mul(x: usize, m: usize) -> usize { 24 | ((x + m - 1) / m) * m 25 | } 26 | 27 | /// Biggest number that is both lower or equal to `x` and a multiple of `m`. 28 | pub const fn floor_mul(x: usize, m: usize) -> usize { 29 | (x / m) * m 30 | } 31 | -------------------------------------------------------------------------------- /containers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-containers" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | description = "Flat collections and wrappers" 7 | authors.workspace = true 8 | repository.workspace = true 9 | license.workspace = true 10 | 11 | [features] 12 | default = ["std"] 13 | std = ["alloc", "stavec/std"] 14 | alloc = [] 15 | 16 | [dependencies] 17 | flatty-base.workspace = true 18 | stavec = { version = "0.4.2", default-features = false, features = ["repr-c"] } 19 | -------------------------------------------------------------------------------- /containers/src/bytes.rs: -------------------------------------------------------------------------------- 1 | use alloc::alloc::{alloc, dealloc, Layout}; 2 | use core::{ 3 | ops::{Deref, DerefMut}, 4 | slice, 5 | }; 6 | 7 | pub struct AlignedBytes { 8 | data: *mut u8, 9 | layout: Layout, 10 | } 11 | 12 | impl AlignedBytes { 13 | pub fn new(size: usize, align: usize) -> Self { 14 | let layout = Layout::from_size_align(size, align).unwrap(); 15 | let data = unsafe { alloc(layout) }; 16 | assert!(!data.is_null()); 17 | Self { data, layout } 18 | } 19 | pub fn from_slice(src: &[u8], align: usize) -> Self { 20 | let mut this = Self::new(src.len(), align); 21 | this.copy_from_slice(src); 22 | this 23 | } 24 | pub fn layout(&self) -> Layout { 25 | self.layout 26 | } 27 | } 28 | 29 | impl Drop for AlignedBytes { 30 | fn drop(&mut self) { 31 | unsafe { dealloc(self.data, self.layout) }; 32 | } 33 | } 34 | 35 | impl AsRef<[u8]> for AlignedBytes { 36 | fn as_ref(&self) -> &[u8] { 37 | unsafe { slice::from_raw_parts(self.data, self.layout.size()) } 38 | } 39 | } 40 | impl AsMut<[u8]> for AlignedBytes { 41 | fn as_mut(&mut self) -> &mut [u8] { 42 | unsafe { slice::from_raw_parts_mut(self.data, self.layout.size()) } 43 | } 44 | } 45 | 46 | impl Deref for AlignedBytes { 47 | type Target = [u8]; 48 | fn deref(&self) -> &[u8] { 49 | self.as_ref() 50 | } 51 | } 52 | impl DerefMut for AlignedBytes { 53 | fn deref_mut(&mut self) -> &mut [u8] { 54 | self.as_mut() 55 | } 56 | } 57 | 58 | unsafe impl Send for AlignedBytes {} 59 | unsafe impl Sync for AlignedBytes {} 60 | -------------------------------------------------------------------------------- /containers/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[cfg(feature = "alloc")] 4 | extern crate alloc; 5 | #[cfg(feature = "std")] 6 | extern crate std; 7 | 8 | #[cfg(feature = "alloc")] 9 | pub mod bytes; 10 | /// Flexible collections that can store `?Sized` items. 11 | pub mod flex; 12 | /// Flat string. 13 | pub mod string; 14 | /// Flat vector itself and its helper types. 15 | pub mod vec; 16 | /// Smart pointer wrapping. 17 | pub mod wrap; 18 | 19 | pub use stavec::error; 20 | -------------------------------------------------------------------------------- /containers/src/string.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | ops::{Deref, DerefMut}, 3 | ptr, 4 | str::from_utf8, 5 | }; 6 | use flatty_base::{ 7 | emplacer::Emplacer, 8 | error::{Error, ErrorKind}, 9 | traits::{Flat, FlatBase, FlatDefault, FlatSized, FlatUnsized, FlatValidate}, 10 | utils::{floor_mul, mem::slice_ptr_len}, 11 | }; 12 | use stavec::GenericString; 13 | 14 | pub use stavec::traits::{Length, Slot}; 15 | 16 | /// Growable flat vector of sized items. 17 | /// 18 | /// It doesn't allocate memory on the heap but instead stores its contents in the same memory behind itself. 19 | /// 20 | /// Obviously, this type is DST. 21 | #[repr(transparent)] 22 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] 23 | pub struct FlatString(GenericString<[u8], L>); 24 | 25 | impl Deref for FlatString { 26 | type Target = GenericString<[u8], L>; 27 | 28 | fn deref(&self) -> &Self::Target { 29 | &self.0 30 | } 31 | } 32 | impl DerefMut for FlatString { 33 | fn deref_mut(&mut self) -> &mut Self::Target { 34 | &mut self.0 35 | } 36 | } 37 | 38 | trait DataOffset { 39 | const DATA_OFFSET: usize = L::SIZE; 40 | } 41 | 42 | impl DataOffset for FlatString {} 43 | 44 | unsafe impl FlatBase for FlatString { 45 | const ALIGN: usize = L::ALIGN; 46 | const MIN_SIZE: usize = Self::DATA_OFFSET; 47 | 48 | fn size(&self) -> usize { 49 | Self::DATA_OFFSET + self.len() 50 | } 51 | } 52 | 53 | unsafe impl FlatUnsized for FlatString { 54 | type AlignAs = L; 55 | 56 | unsafe fn ptr_from_bytes(bytes: *mut [u8]) -> *mut Self { 57 | let meta = floor_mul(slice_ptr_len(bytes) - Self::DATA_OFFSET, Self::ALIGN); 58 | ptr::slice_from_raw_parts_mut(bytes as *mut u8, meta) as *mut Self 59 | } 60 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 61 | let len = Self::DATA_OFFSET + slice_ptr_len(this as *mut [u8]); 62 | ptr::slice_from_raw_parts_mut(this as *mut u8, len) 63 | } 64 | } 65 | 66 | pub struct Empty; 67 | pub struct FromStr>(pub S); 68 | 69 | unsafe impl Emplacer> for Empty { 70 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut FlatString, Error> { 71 | unsafe { (bytes.as_mut_ptr() as *mut L).write(L::zero()) }; 72 | Ok(unsafe { FlatString::from_mut_bytes_unchecked(bytes) }) 73 | } 74 | } 75 | 76 | unsafe impl> Emplacer> for FromStr { 77 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut FlatString, Error> { 78 | unsafe { >>::emplace_unchecked(Empty, bytes) }?; 79 | let vec = unsafe { FlatString::::from_mut_bytes_unchecked(bytes) }; 80 | vec.push_str(self.0.as_ref()).map_err(|_| Error { 81 | kind: ErrorKind::InsufficientSize, 82 | pos: 0, 83 | })?; 84 | Ok(vec) 85 | } 86 | } 87 | 88 | impl FlatDefault for FlatString { 89 | type DefaultEmplacer = Empty; 90 | 91 | fn default_emplacer() -> Empty { 92 | Empty 93 | } 94 | } 95 | 96 | unsafe impl FlatValidate for FlatString { 97 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 98 | unsafe { L::validate_unchecked(bytes) }?; 99 | let this = unsafe { Self::from_bytes_unchecked(bytes) }; 100 | if this.len() > this.capacity() { 101 | return Err(Error { 102 | kind: ErrorKind::InsufficientSize, 103 | pos: Self::DATA_OFFSET, 104 | }); 105 | } 106 | match from_utf8(unsafe { this.0.as_vec().data().as_ref().get_unchecked(..this.len()) }) { 107 | Ok(_) => Ok(()), 108 | Err(e) => Err(Error { 109 | kind: ErrorKind::InvalidData, 110 | pos: Self::DATA_OFFSET + e.valid_up_to(), 111 | }), 112 | } 113 | } 114 | } 115 | 116 | unsafe impl Flat for FlatString {} 117 | 118 | /// Creates [`FlatString`] emplacer from given array. 119 | #[macro_export] 120 | macro_rules! flat_string { 121 | () => { 122 | $crate::string::FromStr("") 123 | }; 124 | ($s:literal) => { 125 | $crate::string::FromStr($s) 126 | }; 127 | } 128 | pub use flat_string; 129 | 130 | #[cfg(all(test, feature = "std"))] 131 | mod tests { 132 | use super::*; 133 | use crate::bytes::AlignedBytes; 134 | 135 | #[test] 136 | fn push_str() { 137 | let mut bytes = AlignedBytes::new(4 + 8, 4); 138 | let string = FlatString::::default_in_place(&mut bytes).unwrap(); 139 | assert_eq!(string.capacity(), 8); 140 | assert_eq!(string.len(), 0); 141 | assert_eq!(string.remaining(), 8); 142 | 143 | string.push_str("abc").unwrap(); 144 | assert_eq!(string.len(), 3); 145 | assert_eq!(string.remaining(), 5); 146 | assert_eq!(string.as_str(), "abc"); 147 | 148 | string.push_str("defgh").unwrap(); 149 | assert_eq!(string.len(), 8); 150 | assert_eq!(string.remaining(), 0); 151 | assert_eq!(string.as_str(), "abcdefgh"); 152 | 153 | assert!(string.push_str("i").is_err()); 154 | } 155 | 156 | #[test] 157 | fn eq() { 158 | let mut mem_a = AlignedBytes::new(2 + 4, 2); 159 | let string_a = FlatString::::new_in_place(&mut mem_a, flat_string!("abcd")).unwrap(); 160 | 161 | let mut mem_b = AlignedBytes::new(2 + 4, 2); 162 | let string_b = FlatString::::new_in_place(&mut mem_b, flat_string!("abcd")).unwrap(); 163 | 164 | let mut mem_c = AlignedBytes::new(2 + 2, 2); 165 | let string_c = FlatString::::new_in_place(&mut mem_c, flat_string!("ab")).unwrap(); 166 | 167 | assert_eq!(string_a, string_b); 168 | assert_ne!(string_a, string_c); 169 | assert_ne!(string_b, string_c); 170 | 171 | string_b.clear(); 172 | assert_ne!(string_a, string_b); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /containers/src/vec.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | mem::MaybeUninit, 3 | ops::{Deref, DerefMut}, 4 | ptr, 5 | }; 6 | use flatty_base::{ 7 | emplacer::Emplacer, 8 | error::{Error, ErrorKind}, 9 | traits::{Flat, FlatBase, FlatDefault, FlatSized, FlatUnsized, FlatValidate}, 10 | utils::{floor_mul, max, mem::slice_ptr_len}, 11 | }; 12 | use stavec::GenericVec; 13 | 14 | pub use stavec::traits::{Length, Slot}; 15 | 16 | #[repr(transparent)] 17 | pub struct MaybeInvalid(MaybeUninit); 18 | impl MaybeInvalid { 19 | fn as_ptr(&self) -> *const T { 20 | self.0.as_ptr() 21 | } 22 | } 23 | unsafe impl Slot for MaybeInvalid { 24 | type Item = T; 25 | 26 | fn new(item: Self::Item) -> Self { 27 | Self(MaybeUninit::new(item)) 28 | } 29 | unsafe fn assume_init(self) -> Self::Item { 30 | self.0.assume_init() 31 | } 32 | unsafe fn assume_init_read(&self) -> Self::Item { 33 | self.0.assume_init_read() 34 | } 35 | } 36 | 37 | /// Growable flat vector of sized items. 38 | /// 39 | /// It doesn't allocate memory on the heap but instead stores its contents in the same memory behind itself. 40 | /// 41 | /// Obviously, this type is DST. 42 | #[repr(transparent)] 43 | #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] 44 | pub struct FlatVec(GenericVec<[MaybeInvalid], L>) 45 | where 46 | T: Flat + Sized, 47 | L: Flat + Length; 48 | 49 | impl Deref for FlatVec 50 | where 51 | T: Flat + Sized, 52 | L: Flat + Length, 53 | { 54 | type Target = GenericVec<[MaybeInvalid], L>; 55 | 56 | fn deref(&self) -> &Self::Target { 57 | &self.0 58 | } 59 | } 60 | impl DerefMut for FlatVec 61 | where 62 | T: Flat + Sized, 63 | L: Flat + Length, 64 | { 65 | fn deref_mut(&mut self) -> &mut Self::Target { 66 | &mut self.0 67 | } 68 | } 69 | 70 | trait DataOffset 71 | where 72 | T: Flat + Sized, 73 | L: Flat + Length, 74 | { 75 | const DATA_OFFSET: usize = max(L::SIZE, T::ALIGN); 76 | } 77 | 78 | impl DataOffset for FlatVec 79 | where 80 | T: Flat + Sized, 81 | L: Flat + Length, 82 | { 83 | } 84 | 85 | /// Sized type that has same alignment as [`FlatVec`](`FlatVec`). 86 | #[repr(C)] 87 | pub struct FlatVecAlignAs(T, L) 88 | where 89 | T: Flat + Sized, 90 | L: Flat + Length; 91 | 92 | unsafe impl FlatBase for FlatVec 93 | where 94 | T: Flat + Sized, 95 | L: Flat + Length, 96 | { 97 | const ALIGN: usize = max(L::ALIGN, T::ALIGN); 98 | const MIN_SIZE: usize = Self::DATA_OFFSET; 99 | 100 | fn size(&self) -> usize { 101 | Self::DATA_OFFSET + T::SIZE * self.len() 102 | } 103 | } 104 | 105 | unsafe impl FlatUnsized for FlatVec 106 | where 107 | T: Flat + Sized, 108 | L: Flat + Length, 109 | { 110 | type AlignAs = FlatVecAlignAs; 111 | 112 | unsafe fn ptr_from_bytes(bytes: *mut [u8]) -> *mut Self { 113 | let meta = floor_mul(slice_ptr_len(bytes) - Self::DATA_OFFSET, Self::ALIGN) / T::SIZE; 114 | ptr::slice_from_raw_parts_mut(bytes as *mut u8, meta) as *mut Self 115 | } 116 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 117 | let len = Self::DATA_OFFSET + slice_ptr_len(this as *mut [T]) * T::SIZE; 118 | ptr::slice_from_raw_parts_mut(this as *mut u8, len) 119 | } 120 | } 121 | 122 | pub struct Empty; 123 | pub struct FromArray(pub [T; N]); 124 | pub struct FromIterator>(pub I); 125 | 126 | unsafe impl Emplacer> for Empty 127 | where 128 | T: Flat + Sized, 129 | L: Flat + Length, 130 | { 131 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut FlatVec, Error> { 132 | unsafe { (bytes.as_mut_ptr() as *mut L).write(L::zero()) }; 133 | // Now it's safe to assume that `Self` is initialized, because vector data is `[MaybeInvalid]`. 134 | Ok(unsafe { FlatVec::from_mut_bytes_unchecked(bytes) }) 135 | } 136 | } 137 | 138 | unsafe impl Emplacer> for FromArray 139 | where 140 | T: Flat + Sized, 141 | L: Flat + Length, 142 | { 143 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut FlatVec, Error> { 144 | unsafe { >>::emplace_unchecked(Empty, bytes) }?; 145 | let vec = unsafe { FlatVec::::from_mut_bytes_unchecked(bytes) }; 146 | if vec.capacity() < N { 147 | return Err(Error { 148 | kind: ErrorKind::InsufficientSize, 149 | pos: 0, 150 | }); 151 | } 152 | vec.extend_until_full(self.0); 153 | Ok(vec) 154 | } 155 | } 156 | 157 | unsafe impl> Emplacer> for FromIterator 158 | where 159 | T: Flat + Sized, 160 | L: Flat + Length, 161 | { 162 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut FlatVec, Error> { 163 | unsafe { >>::emplace_unchecked(Empty, bytes) }?; 164 | let vec = unsafe { FlatVec::::from_mut_bytes_unchecked(bytes) }; 165 | for x in self.0 { 166 | if vec.push(x).is_err() { 167 | return Err(Error { 168 | kind: ErrorKind::InsufficientSize, 169 | pos: 0, 170 | }); 171 | } 172 | } 173 | Ok(vec) 174 | } 175 | } 176 | 177 | impl FlatDefault for FlatVec 178 | where 179 | T: Flat + Sized, 180 | L: Flat + Length, 181 | { 182 | type DefaultEmplacer = Empty; 183 | 184 | fn default_emplacer() -> Empty { 185 | Empty 186 | } 187 | } 188 | 189 | unsafe impl FlatValidate for FlatVec 190 | where 191 | T: Flat + Sized, 192 | L: Flat + Length, 193 | { 194 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 195 | unsafe { L::validate_unchecked(bytes) }?; 196 | // Now it's safe to assume that `Self` is initialized, because vector data is `[MaybeInvalid]`. 197 | let this = unsafe { Self::from_bytes_unchecked(bytes) }; 198 | if this.len() > this.capacity() { 199 | return Err(Error { 200 | kind: ErrorKind::InsufficientSize, 201 | pos: Self::DATA_OFFSET, 202 | }); 203 | } 204 | for x in unsafe { this.data().get_unchecked(..this.len()) } { 205 | unsafe { T::validate_ptr(x.as_ptr()) }?; 206 | } 207 | Ok(()) 208 | } 209 | } 210 | 211 | unsafe impl Flat for FlatVec 212 | where 213 | T: Flat + Sized, 214 | L: Flat + Length, 215 | { 216 | } 217 | 218 | /// Creates [`FlatVec`] emplacer from given array. 219 | #[macro_export] 220 | macro_rules! flat_vec { 221 | () => { 222 | $crate::vec::FromArray([]) 223 | }; 224 | ($elem:expr; $n:expr) => { 225 | $crate::vec::FromArray([$elem; $n]) 226 | }; 227 | ($($x:expr),+ $(,)?) => { 228 | $crate::vec::FromArray([$($x),+]) 229 | }; 230 | } 231 | pub use flat_vec; 232 | 233 | #[cfg(all(test, feature = "std"))] 234 | mod tests { 235 | use super::*; 236 | use crate::bytes::AlignedBytes; 237 | use std::mem::{align_of_val, size_of_val}; 238 | 239 | #[test] 240 | fn data_offset() { 241 | let mut bytes = AlignedBytes::new(4 + 3 * 4, 4); 242 | let flat_vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 243 | 244 | assert_eq!(align_of_val(flat_vec), FlatVec::::ALIGN); 245 | } 246 | 247 | #[test] 248 | fn align() { 249 | let mut bytes = AlignedBytes::new(4 + 3 * 2, 4); 250 | let flat_vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 251 | 252 | assert_eq!(align_of_val(flat_vec), 4); 253 | assert_eq!(flat_vec.capacity(), 2); 254 | assert_eq!(size_of_val(flat_vec), 8); 255 | } 256 | 257 | #[test] 258 | fn len_cap() { 259 | let mut bytes = AlignedBytes::new(4 + 3 * 4, 4); 260 | let flat_vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 261 | assert_eq!(flat_vec.capacity(), 3); 262 | assert_eq!(flat_vec.len(), 0); 263 | } 264 | 265 | #[test] 266 | fn size() { 267 | let mut bytes = AlignedBytes::new(4 + 3 * 4, 4); 268 | let flat_vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 269 | assert_eq!(FlatVec::::DATA_OFFSET, flat_vec.size()); 270 | 271 | for i in 0.. { 272 | if flat_vec.push(i).is_err() { 273 | break; 274 | } 275 | } 276 | assert_eq!(flat_vec.len(), 3); 277 | assert_eq!(size_of_val(flat_vec), flat_vec.size()); 278 | } 279 | 280 | #[test] 281 | fn push_slice() { 282 | let mut bytes = AlignedBytes::new(4 * 6, 4); 283 | let vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 284 | assert_eq!(vec.capacity(), 5); 285 | assert_eq!(vec.len(), 0); 286 | assert_eq!(vec.remaining(), 5); 287 | 288 | vec.push_slice(&[1, 2]).unwrap(); 289 | assert_eq!(vec.len(), 2); 290 | assert_eq!(vec.remaining(), 3); 291 | assert_eq!(vec.as_slice(), &[1, 2][..]); 292 | 293 | vec.push_slice(&[3, 4]).unwrap(); 294 | assert_eq!(vec.len(), 4); 295 | assert_eq!(vec.remaining(), 1); 296 | assert_eq!(vec.as_slice(), &[1, 2, 3, 4][..]); 297 | 298 | assert!(vec.push_slice(&[5, 6]).is_err()); 299 | assert_eq!(vec.len(), 4); 300 | 301 | vec.push_slice(&[5]).unwrap(); 302 | assert_eq!(vec.len(), 5); 303 | assert_eq!(vec.remaining(), 0); 304 | assert_eq!(vec.as_slice(), &[1, 2, 3, 4, 5][..]); 305 | } 306 | 307 | #[test] 308 | fn eq() { 309 | let mut mem_a = AlignedBytes::new(4 * 5, 4); 310 | let vec_a = FlatVec::::new_in_place(&mut mem_a, flat_vec![1, 2, 3, 4]).unwrap(); 311 | 312 | let mut mem_b = AlignedBytes::new(4 * 5, 4); 313 | let vec_b = FlatVec::::new_in_place(&mut mem_b, flat_vec![1, 2, 3, 4]).unwrap(); 314 | 315 | let mut mem_c = AlignedBytes::new(4 * 3, 4); 316 | let vec_c = FlatVec::::new_in_place(&mut mem_c, flat_vec![1, 2]).unwrap(); 317 | 318 | assert_eq!(vec_a, vec_b); 319 | assert_ne!(vec_a, vec_c); 320 | assert_ne!(vec_b, vec_c); 321 | 322 | vec_b[3] = 5; 323 | assert_ne!(vec_a, vec_b); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /containers/src/wrap.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "alloc")] 2 | use crate::bytes::AlignedBytes; 3 | #[cfg(feature = "alloc")] 4 | use alloc::{boxed::Box, ffi::CString, rc::Rc, string::String, sync::Arc, vec::Vec}; 5 | use core::{ 6 | cell::{Ref, RefMut}, 7 | marker::PhantomData, 8 | mem::ManuallyDrop, 9 | ops::{Deref, DerefMut}, 10 | pin::Pin, 11 | }; 12 | use flatty_base::{ 13 | emplacer::Emplacer, 14 | error::Error, 15 | traits::{Flat, FlatDefault}, 16 | }; 17 | use stavec::{ 18 | traits::{Container, Length}, 19 | GenericVec, 20 | }; 21 | 22 | /// Extra guarantees for [`AsRef<[u8]>`] and [`AsMut<[u8]>`]. 23 | /// 24 | /// # Safety 25 | /// 26 | /// Types implementing this trait must guarantee that data referred by `as_ref` and `as_mut` (and reference metadata) 27 | /// will not change internally between these calls (excluding interior mutability). 28 | pub unsafe trait TrustedRef {} 29 | 30 | /// Wrapper for smart pointer to byte slice that maps it to flat type. 31 | pub struct FlatWrap + TrustedRef> { 32 | pointer: P, 33 | _ghost: PhantomData, 34 | } 35 | 36 | impl + TrustedRef> FlatWrap { 37 | /// # Safety 38 | /// 39 | /// Data behind pointer must be valid. 40 | pub unsafe fn from_wrapped_bytes_unchecked(pointer: P) -> Self { 41 | Self { 42 | pointer, 43 | _ghost: PhantomData, 44 | } 45 | } 46 | pub fn into_inner(self) -> P { 47 | self.pointer 48 | } 49 | 50 | pub fn from_wrapped_bytes(pointer: P) -> Result { 51 | F::validate(pointer.as_ref())?; 52 | Ok(unsafe { Self::from_wrapped_bytes_unchecked(pointer) }) 53 | } 54 | } 55 | 56 | impl + AsMut<[u8]> + TrustedRef> FlatWrap { 57 | pub fn new_in_place(mut pointer: P, emplacer: impl Emplacer) -> Result { 58 | F::new_in_place(pointer.as_mut(), emplacer)?; 59 | Ok(unsafe { Self::from_wrapped_bytes_unchecked(pointer) }) 60 | } 61 | 62 | pub fn default_in_place(pointer: P) -> Result 63 | where 64 | F: FlatDefault, 65 | { 66 | Self::new_in_place(pointer, F::default_emplacer()) 67 | } 68 | } 69 | 70 | impl + TrustedRef> Deref for FlatWrap { 71 | type Target = F; 72 | fn deref(&self) -> &Self::Target { 73 | unsafe { F::from_bytes_unchecked(self.pointer.as_ref()) } 74 | } 75 | } 76 | impl + AsMut<[u8]> + TrustedRef> DerefMut for FlatWrap { 77 | fn deref_mut(&mut self) -> &mut Self::Target { 78 | unsafe { F::from_mut_bytes_unchecked(self.pointer.as_mut()) } 79 | } 80 | } 81 | 82 | unsafe impl<'a, T: ?Sized> TrustedRef for &'a T {} 83 | unsafe impl<'a, T: ?Sized> TrustedRef for &'a mut T {} 84 | 85 | unsafe impl TrustedRef for Pin

{} 86 | unsafe impl TrustedRef for ManuallyDrop {} 87 | unsafe impl<'a, T: ?Sized> TrustedRef for Ref<'a, T> {} 88 | unsafe impl<'a, T: ?Sized> TrustedRef for RefMut<'a, T> {} 89 | #[cfg(feature = "alloc")] 90 | unsafe impl TrustedRef for Box {} 91 | #[cfg(feature = "alloc")] 92 | unsafe impl TrustedRef for Rc {} 93 | #[cfg(feature = "alloc")] 94 | unsafe impl TrustedRef for Arc {} 95 | 96 | #[cfg(feature = "alloc")] 97 | unsafe impl TrustedRef for Vec {} 98 | #[cfg(feature = "alloc")] 99 | unsafe impl TrustedRef for String {} 100 | #[cfg(feature = "alloc")] 101 | unsafe impl TrustedRef for CString {} 102 | 103 | unsafe impl TrustedRef for GenericVec {} 104 | #[cfg(feature = "alloc")] 105 | unsafe impl TrustedRef for AlignedBytes {} 106 | 107 | #[cfg(all(test, feature = "std"))] 108 | mod tests { 109 | use super::*; 110 | use crate::{bytes::AlignedBytes, vec::FlatVec}; 111 | 112 | #[test] 113 | fn wrapped_vec() { 114 | let mut wrap = FlatWrap::, _>::default_in_place(AlignedBytes::new(2 + 4, 2)).unwrap(); 115 | 116 | assert_eq!(wrap.capacity(), 4); 117 | assert_eq!(wrap.len(), 0); 118 | assert_eq!(wrap.remaining(), 4); 119 | 120 | wrap.push_slice(&[1, 2]).unwrap(); 121 | assert_eq!(wrap.len(), 2); 122 | assert_eq!(wrap.remaining(), 2); 123 | assert_eq!(wrap.as_slice(), [1, 2].as_slice()); 124 | 125 | wrap.push_slice(&[3, 4]).unwrap(); 126 | assert_eq!(wrap.len(), 4); 127 | assert_eq!(wrap.remaining(), 0); 128 | assert_eq!(wrap.as_slice(), [1, 2, 3, 4].as_slice()); 129 | 130 | let bytes = wrap.into_inner(); 131 | assert_eq!(bytes.deref(), [4, 0, 1, 2, 3, 4].as_slice()) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /io/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-io" 3 | version = "0.1.0-rc.6" 4 | edition.workspace = true 5 | 6 | description = "Flat message transfer" 7 | authors.workspace = true 8 | repository.workspace = true 9 | license.workspace = true 10 | 11 | [features] 12 | default = ["async", "blocking", "io"] 13 | async = ["futures"] 14 | blocking = [] 15 | io = ["std"] 16 | std = ["flatty/std"] 17 | 18 | [dependencies] 19 | flatty = { workspace = true, default-features = false } 20 | futures = { version = "0.3.25", optional = true } 21 | 22 | [dev-dependencies] 23 | async-std = { version = "1.12.0", features = ["attributes"] } 24 | async-ringbuf = "0.2.0-rc.4" 25 | ringbuf-blocking = "0.1.0-rc.2" 26 | -------------------------------------------------------------------------------- /io/src/async_/io.rs: -------------------------------------------------------------------------------- 1 | use super::{AsyncReadBuffer, AsyncWriteBuffer, IoBuffer}; 2 | use futures::{ 3 | io::{AsyncRead, AsyncWrite}, 4 | ready, Future, 5 | }; 6 | use std::{ 7 | io, 8 | pin::Pin, 9 | task::{Context, Poll}, 10 | }; 11 | 12 | impl AsyncWriteBuffer for IoBuffer

{ 13 | type Error = io::Error; 14 | 15 | fn poll_alloc(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { 16 | let n = self.buffer.vacant_len(); 17 | if n > 0 { 18 | self.buffer.advance(n); 19 | } 20 | Poll::Ready(Ok(())) 21 | } 22 | 23 | type WriteAll<'a> = WriteAll<'a, P> where P: 'a; 24 | 25 | fn write_all(&mut self, count: usize) -> Self::WriteAll<'_> { 26 | WriteAll { 27 | owner: self, 28 | pos: 0, 29 | count, 30 | } 31 | } 32 | } 33 | 34 | pub struct WriteAll<'a, P: AsyncWrite + Unpin + 'a> { 35 | owner: &'a mut IoBuffer

, 36 | pos: usize, 37 | count: usize, 38 | } 39 | 40 | impl<'a, P: AsyncWrite + Unpin + 'a> Unpin for WriteAll<'a, P> {} 41 | 42 | impl<'a, P: AsyncWrite + Unpin + 'a> Future for WriteAll<'a, P> { 43 | type Output = io::Result<()>; 44 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 45 | assert!(!self.owner.poisoned); 46 | while self.pos < self.count { 47 | let (pos, count) = (self.pos, self.count); 48 | let (pipe, buffer) = self.owner.split_mut(); 49 | match ready!(Pin::new(pipe).poll_write(cx, &buffer.occupied()[pos..count])) { 50 | Ok(n) => { 51 | if n == 0 { 52 | if self.pos != 0 { 53 | self.owner.poisoned = true; 54 | } 55 | return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into())); 56 | } else { 57 | self.pos += n; 58 | } 59 | } 60 | Err(e) => { 61 | if self.pos != 0 { 62 | self.owner.poisoned = true; 63 | return Poll::Ready(Err(e)); 64 | } 65 | } 66 | } 67 | } 68 | ready!(Pin::new(&mut self.owner.pipe).poll_flush(cx))?; 69 | self.owner.buffer.clear(); 70 | Poll::Ready(Ok(())) 71 | } 72 | } 73 | 74 | impl AsyncReadBuffer for IoBuffer

{ 75 | type Error = io::Error; 76 | 77 | fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { 78 | assert!(!self.poisoned); 79 | if self.buffer.vacant_len() == 0 { 80 | if self.buffer.preceding_len() > 0 { 81 | self.buffer.make_contiguous(); 82 | } else { 83 | return Poll::Ready(Err(io::ErrorKind::OutOfMemory.into())); 84 | } 85 | } 86 | let (pipe, buffer) = self.split_mut(); 87 | let res = ready!(Pin::new(pipe).poll_read(cx, buffer.vacant_mut())); 88 | if let Ok(n) = res { 89 | self.buffer.advance(n); 90 | } 91 | Poll::Ready(res) 92 | } 93 | 94 | fn skip(&mut self, count: usize) { 95 | self.buffer.skip(count); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /io/src/async_/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "io")] 2 | mod io; 3 | mod recv; 4 | mod send; 5 | 6 | pub use crate::common::*; 7 | 8 | #[cfg(feature = "io")] 9 | pub use io::*; 10 | pub use recv::*; 11 | pub use send::*; 12 | -------------------------------------------------------------------------------- /io/src/async_/recv.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "io")] 2 | use super::IoBuffer; 3 | use super::RecvError; 4 | use core::{ 5 | future::Future, 6 | marker::PhantomData, 7 | mem::forget, 8 | ops::Deref, 9 | pin::Pin, 10 | task::{Context, Poll}, 11 | }; 12 | use flatty::{error::ErrorKind, Flat}; 13 | #[cfg(feature = "io")] 14 | use futures::io::AsyncRead; 15 | 16 | pub trait AsyncReadBuffer: Deref + Unpin { 17 | type Error; 18 | 19 | /// Receive more bytes and put them in the buffer. 20 | /// Returns the number of received bytes, zero means that channel is closed. 21 | fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 22 | 23 | fn read(&mut self) -> Read<'_, Self> { 24 | Read(self) 25 | } 26 | 27 | /// Skip first `count` bytes. Remaining bytes *may* be discarded. 28 | fn skip(&mut self, count: usize); 29 | } 30 | 31 | pub struct Read<'a, B: AsyncReadBuffer + ?Sized>(&'a mut B); 32 | 33 | impl<'a, B: AsyncReadBuffer + ?Sized> Future for Read<'a, B> { 34 | type Output = Result; 35 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 36 | Pin::new(&mut *self.0).poll_read(cx) 37 | } 38 | } 39 | 40 | pub struct Receiver { 41 | pub(crate) buffer: B, 42 | _ghost: PhantomData, 43 | } 44 | 45 | impl Receiver { 46 | pub fn new(buffer: B) -> Self { 47 | Self { 48 | buffer, 49 | _ghost: PhantomData, 50 | } 51 | } 52 | } 53 | 54 | #[cfg(feature = "io")] 55 | pub type IoReceiver = Receiver>; 56 | 57 | #[cfg(feature = "io")] 58 | impl IoReceiver { 59 | pub fn io(pipe: P, max_msg_len: usize) -> Self { 60 | Self::new(IoBuffer::new(pipe, 2 * max_msg_len.max(M::MIN_SIZE), M::ALIGN)) 61 | } 62 | } 63 | 64 | impl Receiver { 65 | pub async fn recv(&mut self) -> Result, RecvError> { 66 | while let Err(e) = M::validate(&self.buffer) { 67 | match e.kind { 68 | ErrorKind::InsufficientSize => (), 69 | _ => return Err(RecvError::Parse(e)), 70 | } 71 | if self.buffer.read().await.map_err(RecvError::Read)? == 0 { 72 | return Err(RecvError::Closed); 73 | } 74 | } 75 | Ok(RecvGuard::new(&mut self.buffer)) 76 | } 77 | } 78 | 79 | pub struct RecvGuard<'a, M: Flat + ?Sized, B: AsyncReadBuffer + 'a> { 80 | pub(crate) buffer: &'a mut B, 81 | _ghost: PhantomData, 82 | } 83 | 84 | impl<'a, M: Flat + ?Sized, B: AsyncReadBuffer + 'a> RecvGuard<'a, M, B> { 85 | pub(crate) fn new(buffer: &'a mut B) -> Self { 86 | Self { 87 | buffer, 88 | _ghost: PhantomData, 89 | } 90 | } 91 | /// Destroy guard but do not remove message from receiver. 92 | /// 93 | /// Effect of this call is the same as leak of the guard. 94 | pub fn retain(self) { 95 | forget(self); 96 | } 97 | } 98 | 99 | impl<'a, M: Flat + ?Sized, B: AsyncReadBuffer + 'a> Drop for RecvGuard<'a, M, B> { 100 | fn drop(&mut self) { 101 | let size = self.size(); 102 | self.buffer.skip(size); 103 | } 104 | } 105 | 106 | impl<'a, M: Flat + ?Sized, B: AsyncReadBuffer + 'a> Deref for RecvGuard<'a, M, B> { 107 | type Target = M; 108 | fn deref(&self) -> &M { 109 | unsafe { M::from_bytes_unchecked(self.buffer) } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /io/src/async_/send.rs: -------------------------------------------------------------------------------- 1 | use super::SendError; 2 | #[cfg(feature = "io")] 3 | use crate::IoBuffer; 4 | use core::{ 5 | future::Future, 6 | marker::PhantomData, 7 | ops::{Deref, DerefMut}, 8 | pin::Pin, 9 | task::{Context, Poll}, 10 | }; 11 | use flatty::{self, prelude::*, Emplacer}; 12 | #[cfg(feature = "io")] 13 | use futures::io::AsyncWrite; 14 | 15 | pub trait AsyncWriteBuffer: DerefMut + Unpin { 16 | type Error; 17 | 18 | /// Allocate some fixed amount of bytes in the buffer. 19 | fn poll_alloc(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; 20 | 21 | /// Allocate some fixed amount of bytes in the buffer. 22 | fn alloc(&mut self) -> Alloc<'_, Self> { 23 | Alloc(self) 24 | } 25 | 26 | type WriteAll<'a>: Future> 27 | where 28 | Self: 'a; 29 | 30 | /// Send exactly `count` bytes from buffer. 31 | /// Remaining bytes are discarded. 32 | fn write_all(&mut self, count: usize) -> Self::WriteAll<'_>; 33 | } 34 | 35 | pub struct Alloc<'a, B: AsyncWriteBuffer + ?Sized>(&'a mut B); 36 | 37 | impl<'a, B: AsyncWriteBuffer + ?Sized> Future for Alloc<'a, B> { 38 | type Output = Result<(), B::Error>; 39 | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 40 | Pin::new(&mut *self.0).poll_alloc(cx) 41 | } 42 | } 43 | 44 | pub struct Sender { 45 | pub(crate) buffer: B, 46 | _ghost: PhantomData, 47 | } 48 | 49 | impl Sender { 50 | pub fn new(buf_send: B) -> Self { 51 | Self { 52 | buffer: buf_send, 53 | _ghost: PhantomData, 54 | } 55 | } 56 | } 57 | 58 | #[cfg(feature = "io")] 59 | pub type IoSender = Sender>; 60 | 61 | #[cfg(feature = "io")] 62 | impl IoSender { 63 | pub fn io(pipe: P, max_msg_len: usize) -> Self { 64 | Self::new(IoBuffer::new(pipe, 2 * max_msg_len.max(M::MIN_SIZE), M::ALIGN)) 65 | } 66 | } 67 | 68 | impl Sender { 69 | pub async fn alloc(&mut self) -> Result, SendError> { 70 | self.buffer.alloc().await?; 71 | Ok(UninitSendGuard::new(&mut self.buffer)) 72 | } 73 | } 74 | 75 | impl<'a, M: Flat + ?Sized, B: AsyncWriteBuffer> SendGuard<'a, M, B> { 76 | pub fn send(self) -> B::WriteAll<'a> { 77 | let size = self.size(); 78 | self.buffer.write_all(size) 79 | } 80 | } 81 | 82 | impl<'a, M: Flat + ?Sized, B: AsyncWriteBuffer, const INIT: bool> Unpin for SendGuard<'a, M, B, INIT> {} 83 | 84 | pub struct SendGuard<'a, M: Flat + ?Sized, B: AsyncWriteBuffer + 'a, const INIT: bool = true> { 85 | pub(crate) buffer: &'a mut B, 86 | _ghost: PhantomData, 87 | } 88 | 89 | pub type UninitSendGuard<'a, M, B> = SendGuard<'a, M, B, false>; 90 | 91 | impl<'a, M: Flat + ?Sized, B: AsyncWriteBuffer + 'a> UninitSendGuard<'a, M, B> { 92 | pub(crate) fn new(buffer: &'a mut B) -> Self { 93 | Self { 94 | buffer, 95 | _ghost: PhantomData, 96 | } 97 | } 98 | 99 | pub fn as_bytes(&self) -> &[u8] { 100 | self.buffer 101 | } 102 | pub fn as_mut_bytes(&mut self) -> &mut [u8] { 103 | self.buffer 104 | } 105 | 106 | /// # Safety 107 | /// 108 | /// Underlying message data must be initialized. 109 | pub unsafe fn assume_init(self) -> SendGuard<'a, M, B> { 110 | SendGuard { 111 | buffer: self.buffer, 112 | _ghost: PhantomData, 113 | } 114 | } 115 | 116 | pub fn new_in_place(self, emplacer: impl Emplacer) -> Result, flatty::Error> { 117 | M::new_in_place(self.buffer, emplacer)?; 118 | Ok(unsafe { self.assume_init() }) 119 | } 120 | } 121 | 122 | impl<'a, M: Flat + FlatDefault + ?Sized, B: AsyncWriteBuffer + 'a> UninitSendGuard<'a, M, B> { 123 | pub fn default_in_place(self) -> Result, flatty::Error> { 124 | M::default_in_place(self.buffer)?; 125 | Ok(unsafe { self.assume_init() }) 126 | } 127 | } 128 | 129 | impl<'a, M: Flat + ?Sized, B: AsyncWriteBuffer + 'a> Deref for SendGuard<'a, M, B> { 130 | type Target = M; 131 | fn deref(&self) -> &M { 132 | unsafe { M::from_bytes_unchecked(self.buffer) } 133 | } 134 | } 135 | 136 | impl<'a, M: Flat + ?Sized, B: AsyncWriteBuffer + 'a> DerefMut for SendGuard<'a, M, B> { 137 | fn deref_mut(&mut self) -> &mut M { 138 | unsafe { M::from_mut_bytes_unchecked(self.buffer) } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /io/src/blocking/io.rs: -------------------------------------------------------------------------------- 1 | use super::{ReadBuffer, WriteBuffer}; 2 | use crate::IoBuffer; 3 | use std::io::{self, Read, Write}; 4 | 5 | impl WriteBuffer for IoBuffer

{ 6 | type Error = io::Error; 7 | 8 | fn alloc(&mut self) -> Result<(), Self::Error> { 9 | let n = self.buffer.vacant_len(); 10 | if n > 0 { 11 | self.buffer.advance(n); 12 | } 13 | Ok(()) 14 | } 15 | fn write_all(&mut self, count: usize) -> Result<(), Self::Error> { 16 | assert!(!self.poisoned); 17 | let mut pos = 0; 18 | while pos < count { 19 | match self.pipe.write(&self.buffer.occupied()[pos..count]) { 20 | Ok(n) => { 21 | if n == 0 { 22 | if pos != 0 { 23 | self.poisoned = true; 24 | } 25 | return Err(io::ErrorKind::BrokenPipe.into()); 26 | } else { 27 | pos += n; 28 | } 29 | } 30 | Err(e) => { 31 | if pos != 0 { 32 | self.poisoned = true; 33 | return Err(e); 34 | } 35 | } 36 | } 37 | } 38 | self.buffer.clear(); 39 | Ok(()) 40 | } 41 | } 42 | 43 | impl ReadBuffer for IoBuffer

{ 44 | type Error = io::Error; 45 | 46 | fn read(&mut self) -> Result { 47 | assert!(!self.poisoned); 48 | if self.buffer.vacant_len() == 0 { 49 | if self.buffer.preceding_len() > 0 { 50 | self.buffer.make_contiguous(); 51 | } else { 52 | return Err(io::ErrorKind::OutOfMemory.into()); 53 | } 54 | } 55 | let res = self.pipe.read(self.buffer.vacant_mut()); 56 | if let Ok(n) = res { 57 | self.buffer.advance(n); 58 | } 59 | res 60 | } 61 | 62 | fn skip(&mut self, count: usize) { 63 | self.buffer.skip(count); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /io/src/blocking/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "io")] 2 | mod io; 3 | mod recv; 4 | mod send; 5 | 6 | pub use recv::*; 7 | pub use send::*; 8 | -------------------------------------------------------------------------------- /io/src/blocking/recv.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "io")] 2 | use crate::IoBuffer; 3 | use crate::RecvError; 4 | use core::{marker::PhantomData, mem::forget, ops::Deref}; 5 | use flatty::{error::ErrorKind, Flat}; 6 | #[cfg(feature = "io")] 7 | use std::io::Read; 8 | 9 | pub trait ReadBuffer: Deref { 10 | type Error; 11 | 12 | /// Receive more bytes and put them in the buffer. 13 | /// Returns the number of received bytes, zero means that channel is closed. 14 | fn read(&mut self) -> Result; 15 | 16 | /// Skip first `count` bytes. Remaining bytes *may* be discarded. 17 | fn skip(&mut self, count: usize); 18 | } 19 | 20 | pub struct Receiver { 21 | pub(crate) buffer: B, 22 | _ghost: PhantomData, 23 | } 24 | 25 | impl Receiver { 26 | pub fn new(buffer: B) -> Self { 27 | Self { 28 | buffer, 29 | _ghost: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | #[cfg(feature = "io")] 35 | pub type IoReceiver = Receiver>; 36 | 37 | #[cfg(feature = "io")] 38 | impl IoReceiver { 39 | pub fn io(pipe: P, max_msg_len: usize) -> Self { 40 | Self::new(IoBuffer::new(pipe, 2 * max_msg_len.max(M::MIN_SIZE), M::ALIGN)) 41 | } 42 | } 43 | 44 | impl Receiver { 45 | pub fn recv(&mut self) -> Result, RecvError> { 46 | while let Err(e) = M::validate(&self.buffer) { 47 | match e.kind { 48 | ErrorKind::InsufficientSize => (), 49 | _ => return Err(RecvError::Parse(e)), 50 | } 51 | if self.buffer.read().map_err(RecvError::Read)? == 0 { 52 | return Err(RecvError::Closed); 53 | } 54 | } 55 | Ok(RecvGuard::new(&mut self.buffer)) 56 | } 57 | } 58 | 59 | pub struct RecvGuard<'a, M: Flat + ?Sized, B: ReadBuffer + 'a> { 60 | pub(crate) buffer: &'a mut B, 61 | _ghost: PhantomData, 62 | } 63 | 64 | impl<'a, M: Flat + ?Sized, B: ReadBuffer + 'a> RecvGuard<'a, M, B> { 65 | pub(crate) fn new(buffer: &'a mut B) -> Self { 66 | Self { 67 | buffer, 68 | _ghost: PhantomData, 69 | } 70 | } 71 | /// Destroy guard but do not remove message from receiver. 72 | /// 73 | /// Effect of this call is the same as leak of the guard. 74 | pub fn retain(self) { 75 | forget(self); 76 | } 77 | } 78 | 79 | impl<'a, M: Flat + ?Sized, B: ReadBuffer + 'a> Drop for RecvGuard<'a, M, B> { 80 | fn drop(&mut self) { 81 | let size = self.size(); 82 | self.buffer.skip(size); 83 | } 84 | } 85 | 86 | impl<'a, M: Flat + ?Sized, B: ReadBuffer + 'a> Deref for RecvGuard<'a, M, B> { 87 | type Target = M; 88 | fn deref(&self) -> &M { 89 | unsafe { M::from_bytes_unchecked(self.buffer) } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /io/src/blocking/send.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "io")] 2 | use crate::IoBuffer; 3 | use crate::SendError; 4 | use core::{ 5 | marker::PhantomData, 6 | ops::{Deref, DerefMut}, 7 | }; 8 | use flatty::{self, prelude::*, Emplacer}; 9 | #[cfg(feature = "io")] 10 | use std::io::Write; 11 | 12 | pub trait WriteBuffer: DerefMut { 13 | type Error; 14 | 15 | /// Allocate some fixed amount of bytes in the buffer. 16 | fn alloc(&mut self) -> Result<(), Self::Error>; 17 | 18 | /// Send exactly `count` bytes from buffer. 19 | /// Remaining bytes are discarded. 20 | fn write_all(&mut self, count: usize) -> Result<(), Self::Error>; 21 | } 22 | 23 | pub struct Sender { 24 | pub(crate) buffer: B, 25 | _ghost: PhantomData, 26 | } 27 | 28 | impl Sender { 29 | pub fn new(buf_send: B) -> Self { 30 | Self { 31 | buffer: buf_send, 32 | _ghost: PhantomData, 33 | } 34 | } 35 | } 36 | 37 | #[cfg(feature = "io")] 38 | pub type IoSender = Sender>; 39 | 40 | #[cfg(feature = "io")] 41 | impl IoSender { 42 | pub fn io(pipe: P, max_msg_len: usize) -> Self { 43 | Self::new(IoBuffer::new(pipe, 2 * max_msg_len.max(M::MIN_SIZE), M::ALIGN)) 44 | } 45 | } 46 | 47 | impl Sender { 48 | pub fn alloc(&mut self) -> Result, SendError> { 49 | self.buffer.alloc()?; 50 | Ok(UninitSendGuard::new(&mut self.buffer)) 51 | } 52 | } 53 | 54 | impl<'a, M: Flat + ?Sized, B: WriteBuffer> SendGuard<'a, M, B> { 55 | pub fn send(self) -> Result<(), SendError> { 56 | let size = self.size(); 57 | self.buffer.write_all(size) 58 | } 59 | } 60 | 61 | pub struct SendGuard<'a, M: Flat + ?Sized, B: WriteBuffer + 'a, const INIT: bool = true> { 62 | pub(crate) buffer: &'a mut B, 63 | _ghost: PhantomData, 64 | } 65 | 66 | pub type UninitSendGuard<'a, M, B> = SendGuard<'a, M, B, false>; 67 | 68 | impl<'a, M: Flat + ?Sized, B: WriteBuffer + 'a> UninitSendGuard<'a, M, B> { 69 | pub(crate) fn new(buffer: &'a mut B) -> Self { 70 | Self { 71 | buffer, 72 | _ghost: PhantomData, 73 | } 74 | } 75 | 76 | pub fn as_bytes(&self) -> &[u8] { 77 | self.buffer 78 | } 79 | pub fn as_mut_bytes(&mut self) -> &mut [u8] { 80 | self.buffer 81 | } 82 | 83 | /// # Safety 84 | /// 85 | /// Underlying message data must be initialized. 86 | pub unsafe fn assume_init(self) -> SendGuard<'a, M, B> { 87 | SendGuard { 88 | buffer: self.buffer, 89 | _ghost: PhantomData, 90 | } 91 | } 92 | 93 | pub fn new_in_place(self, emplacer: impl Emplacer) -> Result, flatty::Error> { 94 | M::new_in_place(self.buffer, emplacer)?; 95 | Ok(unsafe { self.assume_init() }) 96 | } 97 | } 98 | 99 | impl<'a, M: Flat + FlatDefault + ?Sized, B: WriteBuffer + 'a> UninitSendGuard<'a, M, B> { 100 | pub fn default_in_place(self) -> Result, flatty::Error> { 101 | M::default_in_place(self.buffer)?; 102 | Ok(unsafe { self.assume_init() }) 103 | } 104 | } 105 | 106 | impl<'a, M: Flat + ?Sized, B: WriteBuffer + 'a> Deref for SendGuard<'a, M, B> { 107 | type Target = M; 108 | fn deref(&self) -> &M { 109 | unsafe { M::from_bytes_unchecked(self.buffer) } 110 | } 111 | } 112 | 113 | impl<'a, M: Flat + ?Sized, B: WriteBuffer + 'a> DerefMut for SendGuard<'a, M, B> { 114 | fn deref_mut(&mut self) -> &mut M { 115 | unsafe { M::from_mut_bytes_unchecked(self.buffer) } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /io/src/common/error.rs: -------------------------------------------------------------------------------- 1 | pub type SendError = E; 2 | 3 | #[derive(Debug)] 4 | pub enum RecvError { 5 | Read(E), 6 | Parse(flatty::Error), 7 | Closed, 8 | } 9 | -------------------------------------------------------------------------------- /io/src/common/io.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use flatty::AlignedBytes; 4 | use std::ops::{Deref, DerefMut, Range}; 5 | 6 | pub(crate) struct Buffer { 7 | data: AlignedBytes, 8 | window: Range, 9 | } 10 | 11 | impl Buffer { 12 | pub(crate) fn new(capacity: usize, align: usize) -> Self { 13 | Self { 14 | data: AlignedBytes::new(capacity, align), 15 | window: 0..0, 16 | } 17 | } 18 | 19 | pub fn capacity(&self) -> usize { 20 | self.data.len() 21 | } 22 | 23 | pub(crate) fn preceding_len(&self) -> usize { 24 | self.window.start 25 | } 26 | pub(crate) fn occupied_len(&self) -> usize { 27 | self.window.end - self.window.start 28 | } 29 | pub(crate) fn vacant_len(&self) -> usize { 30 | self.capacity() - self.window.end 31 | } 32 | pub(crate) fn free_len(&self) -> usize { 33 | self.preceding_len() + self.vacant_len() 34 | } 35 | 36 | pub(crate) fn occupied(&self) -> &[u8] { 37 | &self.data[self.window.clone()] 38 | } 39 | pub(crate) fn occupied_mut(&mut self) -> &mut [u8] { 40 | &mut self.data[self.window.clone()] 41 | } 42 | pub(crate) fn vacant_mut(&mut self) -> &mut [u8] { 43 | &mut self.data[self.window.end..] 44 | } 45 | 46 | pub(crate) fn clear(&mut self) { 47 | self.window = 0..0; 48 | } 49 | /// Skip first `count` occupied bytes. 50 | pub(crate) fn skip(&mut self, count: usize) { 51 | self.window.start += count; 52 | assert!(self.window.start <= self.window.end); 53 | if self.window.is_empty() { 54 | self.window = 0..0; 55 | } 56 | } 57 | /// Make first `count` vacant bytes occupied. 58 | pub(crate) fn advance(&mut self, count: usize) { 59 | self.window.end += count; 60 | assert!(self.window.end <= self.capacity()); 61 | } 62 | /// Move data to the beginning of buffer to get free room for next data. 63 | pub(crate) fn make_contiguous(&mut self) { 64 | self.data.copy_within(self.window.clone(), 0); 65 | self.window = 0..(self.window.end - self.window.start); 66 | } 67 | } 68 | 69 | pub struct IoBuffer

{ 70 | pub(crate) pipe: P, 71 | pub(crate) buffer: Buffer, 72 | pub(crate) poisoned: bool, 73 | } 74 | 75 | impl

IoBuffer

{ 76 | pub fn new(pipe: P, capacity: usize, align: usize) -> Self { 77 | Self { 78 | pipe, 79 | buffer: Buffer::new(capacity, align), 80 | poisoned: false, 81 | } 82 | } 83 | pub(crate) fn split_mut(&mut self) -> (&mut P, &mut Buffer) { 84 | (&mut self.pipe, &mut self.buffer) 85 | } 86 | } 87 | 88 | impl

Deref for IoBuffer

{ 89 | type Target = [u8]; 90 | fn deref(&self) -> &Self::Target { 91 | self.buffer.occupied() 92 | } 93 | } 94 | impl

DerefMut for IoBuffer

{ 95 | fn deref_mut(&mut self) -> &mut Self::Target { 96 | self.buffer.occupied_mut() 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /io/src/common/mod.rs: -------------------------------------------------------------------------------- 1 | mod error; 2 | pub use error::*; 3 | 4 | #[cfg(feature = "io")] 5 | mod io; 6 | #[cfg(feature = "io")] 7 | pub use io::*; 8 | -------------------------------------------------------------------------------- /io/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | #[cfg(feature = "std")] 4 | extern crate std; 5 | 6 | pub mod common; 7 | #[doc(inline)] 8 | pub use common::*; 9 | 10 | #[cfg(feature = "blocking")] 11 | pub mod blocking; 12 | #[cfg(all(feature = "blocking", feature = "io"))] 13 | #[doc(inline)] 14 | pub use blocking::{IoReceiver, IoSender}; 15 | #[cfg(feature = "blocking")] 16 | #[doc(inline)] 17 | pub use blocking::{ReadBuffer, Receiver, Sender, WriteBuffer}; 18 | 19 | #[cfg(feature = "async")] 20 | pub mod async_; 21 | #[cfg(feature = "async")] 22 | #[doc(inline)] 23 | pub use async_::{AsyncReadBuffer, AsyncWriteBuffer, Receiver as AsyncReceiver, Sender as AsyncSender}; 24 | #[cfg(all(feature = "async", feature = "io"))] 25 | #[doc(inline)] 26 | pub use async_::{IoReceiver as AsyncIoReceiver, IoSender as AsyncIoSender}; 27 | 28 | #[cfg(all(test, feature = "io"))] 29 | mod tests; 30 | -------------------------------------------------------------------------------- /io/src/tests/async_.rs: -------------------------------------------------------------------------------- 1 | use super::common::*; 2 | use crate::{AsyncReceiver, AsyncSender, RecvError}; 3 | use async_ringbuf::{traits::*, AsyncHeapRb}; 4 | use async_std::{task::spawn, test as async_test}; 5 | use flatty::vec::FromIterator; 6 | use futures::join; 7 | 8 | #[async_test] 9 | async fn unique() { 10 | let (prod, cons) = AsyncHeapRb::::new(17).split(); 11 | join!( 12 | spawn(async move { 13 | let mut sender = AsyncSender::::io(prod, MAX_SIZE); 14 | 15 | sender 16 | .alloc() 17 | .await 18 | .unwrap() 19 | .default_in_place() 20 | .unwrap() 21 | .send() 22 | .await 23 | .unwrap(); 24 | 25 | sender 26 | .alloc() 27 | .await 28 | .unwrap() 29 | .new_in_place(TestMsgInitB(123456)) 30 | .unwrap() 31 | .send() 32 | .await 33 | .unwrap(); 34 | 35 | sender 36 | .alloc() 37 | .await 38 | .unwrap() 39 | .new_in_place(TestMsgInitC(FromIterator(0..7))) 40 | .unwrap() 41 | .send() 42 | .await 43 | .unwrap(); 44 | }), 45 | spawn(async move { 46 | let mut receiver = AsyncReceiver::::io(cons, MAX_SIZE); 47 | 48 | { 49 | let guard = receiver.recv().await.unwrap(); 50 | match guard.as_ref() { 51 | TestMsgRef::A => (), 52 | _ => panic!(), 53 | } 54 | } 55 | 56 | { 57 | let guard = receiver.recv().await.unwrap(); 58 | match guard.as_ref() { 59 | TestMsgRef::B(x) => assert_eq!(*x, 123456), 60 | _ => panic!(), 61 | } 62 | } 63 | 64 | { 65 | let guard = receiver.recv().await.unwrap(); 66 | match guard.as_ref() { 67 | TestMsgRef::C(v) => { 68 | assert!(v.iter().copied().eq(0..7)); 69 | } 70 | _ => panic!(), 71 | } 72 | } 73 | 74 | match receiver.recv().await.err().unwrap() { 75 | RecvError::Closed => (), 76 | _ => panic!(), 77 | } 78 | }) 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /io/src/tests/blocking.rs: -------------------------------------------------------------------------------- 1 | use super::common::*; 2 | use crate::{Receiver, RecvError, Sender}; 3 | use flatty::vec::FromIterator; 4 | use ringbuf_blocking::{traits::*, BlockingHeapRb}; 5 | use std::thread::spawn; 6 | 7 | #[test] 8 | fn unique() { 9 | let (prod, cons) = BlockingHeapRb::::new(17).split(); 10 | let (send, recv) = ( 11 | spawn(move || { 12 | let mut sender = Sender::::io(prod, MAX_SIZE); 13 | 14 | sender.alloc().unwrap().default_in_place().unwrap().send().unwrap(); 15 | 16 | sender 17 | .alloc() 18 | .unwrap() 19 | .new_in_place(TestMsgInitB(123456)) 20 | .unwrap() 21 | .send() 22 | .unwrap(); 23 | 24 | sender 25 | .alloc() 26 | .unwrap() 27 | .new_in_place(TestMsgInitC(FromIterator(0..7))) 28 | .unwrap() 29 | .send() 30 | .unwrap(); 31 | }), 32 | spawn(move || { 33 | let mut receiver = Receiver::::io(cons, MAX_SIZE); 34 | 35 | match receiver.recv().unwrap().as_ref() { 36 | TestMsgRef::A => (), 37 | _ => panic!(), 38 | } 39 | 40 | match receiver.recv().unwrap().as_ref() { 41 | TestMsgRef::B(x) => assert_eq!(*x, 123456), 42 | _ => panic!(), 43 | } 44 | 45 | match receiver.recv().unwrap().as_ref() { 46 | TestMsgRef::C(v) => { 47 | assert!(v.iter().copied().eq(0..7)); 48 | } 49 | _ => panic!(), 50 | } 51 | 52 | match receiver.recv().err().unwrap() { 53 | RecvError::Closed => (), 54 | other => panic!("{:?}", other), 55 | } 56 | }), 57 | ); 58 | recv.join().unwrap(); 59 | send.join().unwrap(); 60 | } 61 | -------------------------------------------------------------------------------- /io/src/tests/common.rs: -------------------------------------------------------------------------------- 1 | use flatty::{flat, FlatVec}; 2 | 3 | #[flat(sized = false, default = true)] 4 | pub enum TestMsg { 5 | #[default] 6 | A, 7 | B(i32), 8 | C(FlatVec), 9 | } 10 | 11 | pub const MAX_SIZE: usize = 36; 12 | -------------------------------------------------------------------------------- /io/src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | #[cfg(feature = "async")] 4 | mod async_; 5 | #[cfg(feature = "blocking")] 6 | mod blocking; 7 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-macros" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | description = "Flatty procedural macros" 7 | authors.workspace = true 8 | repository.workspace = true 9 | license.workspace = true 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | syn = "1.0" 16 | quote = "1.0" 17 | proc-macro2 = "1.0" 18 | convert_case = "0.6.0" 19 | -------------------------------------------------------------------------------- /macros/src/context.rs: -------------------------------------------------------------------------------- 1 | use crate::Info; 2 | use proc_macro2::Ident; 3 | 4 | #[derive(Clone, Default, Debug)] 5 | pub struct AssocIdents { 6 | pub align_as: Option, 7 | pub tag: Option, 8 | pub ref_: Option, 9 | pub mut_: Option, 10 | pub init: Option, 11 | } 12 | 13 | #[derive(Clone, Debug)] 14 | pub struct Context { 15 | pub info: Info, 16 | pub idents: AssocIdents, 17 | pub c_like_enum: Option, 18 | } 19 | -------------------------------------------------------------------------------- /macros/src/info.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use syn::{ 3 | parse::{Error as ParseError, Parse, ParseStream}, 4 | punctuated::Punctuated, 5 | spanned::Spanned, 6 | Ident, Lit, Meta, Token, 7 | }; 8 | 9 | #[derive(Clone, Debug)] 10 | pub struct Info { 11 | pub sized: bool, 12 | pub tag_type: Option, 13 | pub portable: bool, 14 | pub default: bool, 15 | } 16 | 17 | fn parse_lit_bool(lit: &Lit) -> Result { 18 | if let Lit::Bool(lit_bool) = lit { 19 | Ok(lit_bool.value) 20 | } else { 21 | Err(ParseError::new(lit.span(), "keyword requires bool value")) 22 | } 23 | } 24 | 25 | fn parse_lit_ident(lit: &Lit) -> Result { 26 | if let Lit::Str(lit_str) = lit { 27 | Ok(Ident::new(&lit_str.value(), lit_str.span())) 28 | } else { 29 | Err(ParseError::new(lit.span(), "str value required")) 30 | } 31 | } 32 | 33 | impl Parse for Info { 34 | fn parse(input: ParseStream) -> Result { 35 | let items = Punctuated::::parse_terminated(input)?; 36 | 37 | let mut params = items 38 | .iter() 39 | .map(|meta| { 40 | if let Meta::NameValue(nv) = meta { 41 | Ok((format!("{}", nv.path.get_ident().unwrap()), &nv.lit)) 42 | } else { 43 | Err(ParseError::new(meta.span(), "`name = value` format required")) 44 | } 45 | }) 46 | .collect::, ParseError>>()?; 47 | 48 | let info = Info { 49 | sized: if let Some(lit) = params.remove("sized") { 50 | parse_lit_bool(lit)? 51 | } else { 52 | true 53 | }, 54 | tag_type: if let Some(lit) = params.remove("tag_type") { 55 | Some(parse_lit_ident(lit)?) 56 | } else { 57 | None 58 | }, 59 | portable: if let Some(lit) = params.remove("portable") { 60 | parse_lit_bool(lit)? 61 | } else { 62 | false 63 | }, 64 | default: if let Some(lit) = params.remove("default") { 65 | parse_lit_bool(lit)? 66 | } else { 67 | false 68 | }, 69 | }; 70 | 71 | if !params.is_empty() { 72 | return Err(ParseError::new( 73 | items.span(), 74 | format!("Unknown macro arguments: {:?}", params.keys().collect::>()), 75 | )); 76 | } 77 | 78 | Ok(info) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /macros/src/items/align_as.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils::FieldIter, Context}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use syn::{Data, DeriveInput}; 5 | 6 | pub fn struct_(ctx: &Context, input: &DeriveInput) -> TokenStream { 7 | pub fn collect_fields(fields: &I) -> TokenStream { 8 | fields.iter().fold(quote! {}, |a, f| { 9 | let ty = &f.ty; 10 | quote! { #a <#ty as ::flatty::traits::FlatUnsized>::AlignAs, } 11 | }) 12 | } 13 | 14 | let type_list = match &input.data { 15 | Data::Struct(data) => collect_fields(&data.fields), 16 | Data::Enum(data) => { 17 | let tag_type = ctx.info.tag_type.as_ref().unwrap(); 18 | data.variants.iter().fold(quote! { #tag_type, }, |accum, variant| { 19 | let var_type_list = collect_fields(&variant.fields); 20 | quote! { #accum #var_type_list } 21 | }) 22 | } 23 | Data::Union(..) => unimplemented!(), 24 | }; 25 | 26 | let vis = &input.vis; 27 | let align_as_type = ctx.idents.align_as.as_ref().unwrap(); 28 | 29 | let generic_params = &input.generics.params; 30 | let where_clause = &input.generics.where_clause; 31 | 32 | quote! { 33 | #[allow(dead_code)] 34 | #[repr(C)] 35 | #vis struct #align_as_type<#generic_params>( 36 | #type_list 37 | ) #where_clause; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /macros/src/items/base.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::{generic, type_list, FieldIter}, 3 | Context, 4 | }; 5 | use proc_macro2::TokenStream; 6 | use quote::{quote, ToTokens}; 7 | use syn::{Data, DeriveInput, Index}; 8 | 9 | pub fn align_const(ctx: &Context, input: &DeriveInput) -> TokenStream { 10 | let generic_args = generic::args(&input.generics); 11 | let align_as_ident = ctx.idents.align_as.as_ref().unwrap(); 12 | let align_as_type = quote! { #align_as_ident<#generic_args> }; 13 | quote! { const ALIGN: usize = ::core::mem::align_of::<#align_as_type>(); } 14 | } 15 | 16 | pub fn min_size_collect_fields(fields: &I) -> TokenStream { 17 | let iter = fields.iter(); 18 | if iter.len() > 0 { 19 | let type_list = type_list(iter); 20 | quote! { ::flatty::utils::iter::fold_min_size!(0; #type_list) } 21 | } else { 22 | quote! { 0 } 23 | } 24 | } 25 | 26 | pub fn min_size_const(_ctx: &Context, input: &DeriveInput) -> TokenStream { 27 | let value = match &input.data { 28 | Data::Struct(struct_data) => min_size_collect_fields(&struct_data.fields), 29 | Data::Enum(enum_data) => { 30 | let contents = enum_data.variants.iter().enumerate().fold(quote! {}, |accum, (index, _var)| { 31 | let var_min_size = quote! { Self::DATA_MIN_SIZES[#index] }; 32 | if accum.is_empty() { 33 | quote! { #var_min_size } 34 | } else { 35 | quote! { ::flatty::utils::min(#accum, #var_min_size) } 36 | } 37 | }); 38 | quote! { 39 | ::flatty::utils::ceil_mul(Self::DATA_OFFSET + #contents, ::ALIGN) 40 | } 41 | } 42 | Data::Union(..) => unimplemented!(), 43 | }; 44 | 45 | quote! { const MIN_SIZE: usize = #value; } 46 | } 47 | 48 | fn size_method(ctx: &Context, input: &DeriveInput) -> TokenStream { 49 | let value = match &input.data { 50 | Data::Struct(struct_data) => { 51 | let last = struct_data.fields.iter().enumerate().last().map(|(i, f)| match &f.ident { 52 | Some(ident) => ident.to_token_stream(), 53 | None => Index::from(i).to_token_stream(), 54 | }); 55 | match last { 56 | Some(last) => { 57 | quote! { Self::LAST_FIELD_OFFSET + self.#last.size() } 58 | } 59 | None => quote! { 0 }, 60 | } 61 | } 62 | Data::Enum(enum_data) => { 63 | let variants = enum_data.variants.iter().fold(quote! {}, |accum, variant| { 64 | let tag_type = ctx.idents.tag.as_ref().unwrap(); 65 | let var_name = &variant.ident; 66 | let value = if !variant.fields.is_empty() { 67 | let type_list = type_list(variant.fields.iter()); 68 | quote! { unsafe { iter::BytesIter::new_unchecked(&self.data, iter::type_list!(#type_list)).fold_size(0) } } 69 | } else { 70 | quote! { 0 } 71 | }; 72 | quote! { 73 | #accum 74 | #tag_type::#var_name => { #value } 75 | } 76 | }); 77 | quote! { 78 | { 79 | use ::flatty::utils::iter::{prelude::*, self}; 80 | Self::DATA_OFFSET + match self.tag { 81 | #variants 82 | } 83 | } 84 | } 85 | } 86 | Data::Union(_union_data) => unimplemented!(), 87 | }; 88 | quote! { 89 | fn size(&self) -> usize { 90 | use ::flatty::{traits::*, utils::ceil_mul}; 91 | ceil_mul(#value, Self::ALIGN) 92 | } 93 | } 94 | } 95 | 96 | pub fn self_impl(ctx: &Context, input: &DeriveInput) -> TokenStream { 97 | let self_ident = &input.ident; 98 | 99 | let generic_params = generic::without_defaults(&input.generics).params; 100 | let generic_args = generic::args(&input.generics); 101 | let where_clause = &input.generics.where_clause; 102 | 103 | let mut items = quote! {}; 104 | 105 | match &input.data { 106 | Data::Enum(data) => { 107 | let tag_type = ctx.info.tag_type.as_ref().unwrap(); 108 | 109 | items = quote! { 110 | #items 111 | const DATA_OFFSET: usize = ::flatty::utils::ceil_mul( 112 | <#tag_type as ::flatty::traits::FlatSized>::SIZE, 113 | ::ALIGN, 114 | ); 115 | }; 116 | 117 | if !ctx.info.sized { 118 | let var_count = data.variants.len(); 119 | let values = data.variants.iter().fold(quote! {}, |accum, variant| { 120 | let var_min_size = min_size_collect_fields(&variant.fields); 121 | quote! { #accum #var_min_size, } 122 | }); 123 | items = quote! { 124 | #items 125 | const DATA_MIN_SIZES: [usize; #var_count] = [ #values ]; 126 | } 127 | } 128 | } 129 | Data::Struct(data) => { 130 | if !data.fields.is_empty() && !ctx.info.sized { 131 | let value = if data.fields.len() > 1 { 132 | let len = data.fields.len(); 133 | let type_list = type_list(data.fields.iter().take(len - 1)); 134 | let last_ty = &data.fields.iter().last().unwrap().ty; 135 | quote! { 136 | ::flatty::utils::ceil_mul( 137 | ::flatty::utils::iter::fold_size!(0; #type_list), 138 | <#last_ty as ::flatty::traits::FlatBase>::ALIGN, 139 | ) 140 | } 141 | } else { 142 | quote! { 0 } 143 | }; 144 | items = quote! { 145 | #items 146 | const LAST_FIELD_OFFSET: usize = #value; 147 | } 148 | } 149 | } 150 | Data::Union(..) => unimplemented!(), 151 | } 152 | 153 | quote! { 154 | impl<#generic_params> #self_ident<#generic_args> 155 | #where_clause 156 | { 157 | #items 158 | } 159 | } 160 | } 161 | 162 | pub fn impl_(ctx: &Context, input: &DeriveInput) -> TokenStream { 163 | assert!(!ctx.info.sized); 164 | 165 | let self_ident = &input.ident; 166 | 167 | let generic_params = generic::without_defaults(&input.generics).params; 168 | let generic_args = generic::args(&input.generics); 169 | let where_clause = generic::where_clause( 170 | input, 171 | quote! { ::flatty::traits::FlatBase + Sized }, 172 | if ctx.info.sized { 173 | None 174 | } else { 175 | Some(quote! { ::flatty::traits::FlatBase }) 176 | }, 177 | ); 178 | 179 | let align_const = align_const(ctx, input); 180 | let min_size_const = min_size_const(ctx, input); 181 | let size_method = size_method(ctx, input); 182 | 183 | quote! { 184 | unsafe impl<#generic_params> ::flatty::traits::FlatBase for #self_ident<#generic_args> 185 | #where_clause 186 | { 187 | #align_const 188 | #min_size_const 189 | 190 | #size_method 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /macros/src/items/cast.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | items::tag, 3 | utils::{generic, type_list, FieldIter}, 4 | Context, 5 | }; 6 | use proc_macro2::TokenStream; 7 | use quote::quote; 8 | use syn::{Data, DeriveInput}; 9 | 10 | fn validate_method(ctx: &Context, input: &DeriveInput) -> TokenStream { 11 | fn collect_fields(fields: &I, bytes: TokenStream) -> TokenStream { 12 | let iter = fields.iter(); 13 | if iter.len() == 0 { 14 | return quote! { Ok(()) }; 15 | } 16 | let type_list = type_list(iter); 17 | quote! { 18 | iter::BytesIter::new_unchecked(#bytes, iter::type_list!(#type_list)).validate_all() 19 | } 20 | } 21 | 22 | let body = match &input.data { 23 | Data::Struct(struct_data) => collect_fields(&struct_data.fields, quote! { __flatty_bytes }), 24 | Data::Enum(enum_data) => { 25 | if !ctx.c_like_enum.unwrap() { 26 | let tag_type = ctx.idents.tag.as_ref().unwrap(); 27 | let validate_tag = quote! { 28 | <#tag_type>::validate_unchecked(__flatty_bytes)?; 29 | <#tag_type>::from_bytes_unchecked(__flatty_bytes) 30 | }; 31 | let variants = enum_data.variants.iter().fold(quote! {}, |accum, variant| { 32 | let items = collect_fields(&variant.fields, quote! { data }); 33 | let var_name = &variant.ident; 34 | quote! { 35 | #accum 36 | #tag_type::#var_name => { #items } 37 | } 38 | }); 39 | let size_check = if !ctx.info.sized { 40 | quote! { 41 | if data.len() < Self::DATA_MIN_SIZES[*tag as usize] { 42 | return Err(Error { 43 | kind: ErrorKind::InsufficientSize, 44 | pos: Self::DATA_OFFSET, 45 | }); 46 | } 47 | } 48 | } else { 49 | quote! {} 50 | }; 51 | 52 | quote! { 53 | use ::flatty::error::{Error, ErrorKind}; 54 | 55 | let tag = { #validate_tag }; 56 | let data = unsafe { __flatty_bytes.get_unchecked(Self::DATA_OFFSET..) }; 57 | 58 | #size_check 59 | 60 | match tag { 61 | #variants 62 | }.map_err(|e| e.offset(Self::DATA_OFFSET)) 63 | } 64 | } else { 65 | tag::validate_code(ctx, input, "e! {__flatty_bytes}) 66 | } 67 | } 68 | Data::Union(_union_data) => unimplemented!(), 69 | }; 70 | quote! { 71 | unsafe fn validate_unchecked(__flatty_bytes: &[u8]) -> Result<(), ::flatty::Error> { 72 | use ::flatty::{traits::*, utils::iter::{prelude::*, self}}; 73 | #body 74 | } 75 | } 76 | } 77 | 78 | pub fn impl_(ctx: &Context, input: &DeriveInput) -> TokenStream { 79 | let self_ident = &input.ident; 80 | 81 | let generic_params = generic::without_defaults(&input.generics).params; 82 | let generic_args = generic::args(&input.generics); 83 | let where_clause = generic::where_clause( 84 | input, 85 | quote! { ::flatty::traits::FlatValidate + Sized }, 86 | if ctx.info.sized { 87 | None 88 | } else { 89 | Some(quote! { ::flatty::traits::FlatValidate }) 90 | }, 91 | ); 92 | 93 | let validate_method = validate_method(ctx, input); 94 | 95 | quote! { 96 | unsafe impl<#generic_params> ::flatty::traits::FlatValidate for #self_ident<#generic_args> 97 | #where_clause 98 | { 99 | #validate_method 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /macros/src/items/enum_.rs: -------------------------------------------------------------------------------- 1 | use syn::{Data, DeriveInput, Fields}; 2 | 3 | pub fn is_c_like(input: &DeriveInput) -> bool { 4 | if let Data::Enum(data) = &input.data { 5 | data.variants.iter().all(|var| matches!(var.fields, Fields::Unit)) 6 | } else { 7 | panic!("Enum is expected") 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macros/src/items/flat.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils::generic, Context}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use syn::DeriveInput; 5 | 6 | pub fn impl_(_ctx: &Context, input: &DeriveInput) -> TokenStream { 7 | let self_ident = &input.ident; 8 | 9 | let generic_params = generic::without_defaults(&input.generics).params; 10 | let generic_args = generic::args(&input.generics); 11 | let where_clause = generic::where_clause(input, quote! { ::flatty::Flat + Sized }, Some(quote! { ::flatty::Flat })); 12 | 13 | quote! { 14 | unsafe impl<#generic_params> ::flatty::Flat for #self_ident<#generic_args> 15 | #where_clause 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macros/src/items/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod align_as; 2 | pub mod base; 3 | pub mod cast; 4 | pub mod enum_; 5 | pub mod flat; 6 | pub mod init; 7 | pub mod portable; 8 | pub mod tag; 9 | pub mod unsized_; 10 | pub mod unsized_enum; 11 | -------------------------------------------------------------------------------- /macros/src/items/portable.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils::generic, Context}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use syn::DeriveInput; 5 | 6 | pub fn impl_(_ctx: &Context, input: &DeriveInput) -> TokenStream { 7 | let self_ident = &input.ident; 8 | 9 | let generic_params = generic::without_defaults(&input.generics).params; 10 | let generic_args = generic::args(&input.generics); 11 | let where_clause = generic::where_clause(input, quote! { ::flatty::Portable }, None); 12 | 13 | quote! { 14 | unsafe impl<#generic_params> ::flatty::Portable for #self_ident<#generic_args> 15 | #where_clause 16 | { 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /macros/src/items/tag.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils::generic, Context}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use syn::{Data, DeriveInput, Index}; 5 | 6 | pub fn validate_code(ctx: &Context, input: &DeriveInput, bytes_ident: &TokenStream) -> TokenStream { 7 | if let Data::Enum(data) = &input.data { 8 | let tag_type = ctx.info.tag_type.as_ref().unwrap(); 9 | let var_count = Index::from(data.variants.len()); 10 | quote! { 11 | use ::flatty::{traits::*, error::{Error, ErrorKind}}; 12 | <#tag_type>::validate_unchecked(#bytes_ident)?; 13 | let tag = <#tag_type>::from_bytes_unchecked(#bytes_ident); 14 | if *tag < #var_count { 15 | Ok(()) 16 | } else { 17 | Err(Error { 18 | kind: ErrorKind::InvalidEnumTag, 19 | pos: 0, 20 | }) 21 | } 22 | } 23 | } else { 24 | panic!("Only enum has tag"); 25 | } 26 | } 27 | 28 | pub fn struct_(ctx: &Context, input: &DeriveInput, local: bool) -> TokenStream { 29 | if let Data::Enum(data) = &input.data { 30 | let tag_type = ctx.info.tag_type.as_ref().unwrap(); 31 | let vis = if !local { Some(&input.vis) } else { None }; 32 | let tag = ctx.idents.tag.as_ref().unwrap(); 33 | let variants = data.variants.iter().fold(quote! {}, |accum, var| { 34 | let ident = &var.ident; 35 | quote! { 36 | #accum 37 | #ident, 38 | } 39 | }); 40 | let bytes_ident = quote! {__flatty_bytes}; 41 | let code = validate_code(ctx, input, &bytes_ident); 42 | quote! { 43 | #[allow(dead_code)] 44 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 45 | #[repr(#tag_type)] 46 | #vis enum #tag { 47 | #variants 48 | } 49 | 50 | unsafe impl ::flatty::traits::FlatValidate for #tag { 51 | unsafe fn validate_unchecked(#bytes_ident: &[u8]) -> Result<(), ::flatty::Error> { 52 | #code 53 | } 54 | } 55 | 56 | unsafe impl ::flatty::Flat for #tag {} 57 | } 58 | } else { 59 | panic!("Only enum has tag"); 60 | } 61 | } 62 | 63 | pub fn impl_(ctx: &Context, input: &DeriveInput) -> TokenStream { 64 | if let Data::Enum(..) = &input.data { 65 | let self_ident = &input.ident; 66 | 67 | let generic_params = generic::without_defaults(&input.generics).params; 68 | let generic_args = generic::args(&input.generics); 69 | let where_clause = &input.generics.where_clause; 70 | 71 | let tag_type = ctx.idents.tag.as_ref().unwrap(); 72 | 73 | assert!(!ctx.info.sized); 74 | quote! { 75 | impl<#generic_params> #self_ident<#generic_args> 76 | #where_clause 77 | { 78 | pub fn tag(&self) -> #tag_type { 79 | self.tag 80 | } 81 | } 82 | } 83 | } else { 84 | panic!("Only enum has tag"); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /macros/src/items/unsized_.rs: -------------------------------------------------------------------------------- 1 | use crate::{utils::generic, Context}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use syn::{Data, DeriveInput}; 5 | 6 | fn ptr_from_bytes_method(_ctx: &Context, input: &DeriveInput) -> TokenStream { 7 | let body = match &input.data { 8 | Data::Struct(struct_data) => { 9 | assert!(!struct_data.fields.is_empty()); 10 | let last_ty = &struct_data.fields.iter().last().unwrap().ty; 11 | quote! { 12 | use ::flatty::utils::mem::{offset_slice_ptr_start, cast_wide_ptr_with_offset}; 13 | cast_wide_ptr_with_offset!( 14 | Self, 15 | <#last_ty as FlatUnsized>::ptr_from_bytes(offset_slice_ptr_start(__flatty_bytes, Self::LAST_FIELD_OFFSET as isize)), 16 | -(Self::LAST_FIELD_OFFSET as isize), 17 | ) 18 | } 19 | } 20 | Data::Enum(..) => quote! { 21 | use ::flatty::utils::{floor_mul, mem::{set_slice_ptr_len, slice_ptr_len}}; 22 | set_slice_ptr_len(__flatty_bytes, floor_mul(slice_ptr_len(__flatty_bytes) - Self::DATA_OFFSET, Self::ALIGN)) as *mut Self 23 | }, 24 | Data::Union(..) => unimplemented!(), 25 | }; 26 | quote! { 27 | unsafe fn ptr_from_bytes(__flatty_bytes: *mut [u8]) -> *mut Self { 28 | use ::flatty::traits::*; 29 | #body 30 | } 31 | } 32 | } 33 | 34 | fn ptr_to_bytes_method(_ctx: &Context, input: &DeriveInput) -> TokenStream { 35 | let body = match &input.data { 36 | Data::Struct(struct_data) => { 37 | assert!(!struct_data.fields.is_empty()); 38 | let last_ty = &struct_data.fields.iter().enumerate().last().unwrap().1.ty; 39 | quote! { 40 | use ::flatty::utils::mem::{offset_slice_ptr_start, cast_wide_ptr_with_offset}; 41 | offset_slice_ptr_start( 42 | <#last_ty as FlatUnsized>::ptr_to_bytes(cast_wide_ptr_with_offset!(#last_ty, this, Self::LAST_FIELD_OFFSET as isize)), 43 | -(Self::LAST_FIELD_OFFSET as isize), 44 | ) 45 | } 46 | } 47 | Data::Enum(..) => quote! { 48 | use ::flatty::utils::mem::{set_slice_ptr_len, slice_ptr_len}; 49 | let __flatty_bytes = this as *mut [u8]; 50 | set_slice_ptr_len(__flatty_bytes, Self::DATA_OFFSET + slice_ptr_len(__flatty_bytes)) 51 | }, 52 | Data::Union(..) => unimplemented!(), 53 | }; 54 | quote! { 55 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 56 | use ::flatty::traits::*; 57 | #body 58 | } 59 | } 60 | } 61 | 62 | pub fn impl_(ctx: &Context, input: &DeriveInput) -> TokenStream { 63 | assert!(!ctx.info.sized); 64 | 65 | let self_ident = &input.ident; 66 | 67 | let generic_params = generic::without_defaults(&input.generics).params; 68 | let generic_args = generic::args(&input.generics); 69 | let where_clause = generic::where_clause( 70 | input, 71 | quote! { ::flatty::traits::FlatBase + Sized }, 72 | if ctx.info.sized { 73 | None 74 | } else { 75 | Some(quote! { ::flatty::traits::FlatUnsized }) 76 | }, 77 | ); 78 | 79 | let align_as_ident = ctx.idents.align_as.as_ref().unwrap(); 80 | let align_as_type = quote! { #align_as_ident<#generic_args> }; 81 | let ptr_from_bytes_method = ptr_from_bytes_method(ctx, input); 82 | let ptr_to_bytes_method = ptr_to_bytes_method(ctx, input); 83 | 84 | quote! { 85 | unsafe impl<#generic_params> ::flatty::traits::FlatUnsized for #self_ident<#generic_args> 86 | #where_clause 87 | { 88 | type AlignAs = #align_as_type; 89 | 90 | #ptr_from_bytes_method 91 | #ptr_to_bytes_method 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /macros/src/items/unsized_enum.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | utils::{generic, type_list}, 3 | Context, 4 | }; 5 | use proc_macro2::TokenStream; 6 | use quote::quote; 7 | use syn::{spanned::Spanned, Data, DeriveInput, Field, Fields, Ident}; 8 | 9 | pub fn struct_(ctx: &Context, input: &DeriveInput) -> TokenStream { 10 | let vis = &input.vis; 11 | let self_ident = &input.ident; 12 | 13 | let generic_params = &input.generics.params; 14 | let generic_args = generic::args(&input.generics); 15 | let where_clause = &input.generics.where_clause; 16 | 17 | let tag_type = ctx.idents.tag.as_ref().unwrap(); 18 | let align_as_ident = ctx.idents.align_as.as_ref().unwrap(); 19 | let align_as_type = quote! { #align_as_ident<#generic_args> }; 20 | 21 | quote! { 22 | #[repr(C)] 23 | #vis struct #self_ident<#generic_params> 24 | #where_clause 25 | { 26 | tag: #tag_type, 27 | _align: [#align_as_type; 0], 28 | data: [u8], 29 | } 30 | } 31 | } 32 | 33 | fn gen_ref_struct(_ctx: &Context, input: &DeriveInput, mutable: bool, ref_ident: &Ident) -> TokenStream { 34 | let mut_ = if mutable { Some(quote! { mut }) } else { None }; 35 | let map_field = |field: &Field| { 36 | let ty = &field.ty; 37 | let prefix = match &field.ident { 38 | Some(ident) => quote! { #ident: }, 39 | None => quote! {}, 40 | }; 41 | quote! { #prefix &'__flatty_a #mut_ #ty } 42 | }; 43 | 44 | let vis = &input.vis; 45 | 46 | let generic_params = &input.generics.params; 47 | let where_clause = &input.generics.where_clause; 48 | 49 | let variants = if let Data::Enum(data) = &input.data { 50 | data.variants.iter().fold(quote! {}, |accum, var| { 51 | let var_ident = &var.ident; 52 | let contents = var.fields.iter().fold(quote! {}, |a, f| { 53 | let item = map_field(f); 54 | quote! { #a #item, } 55 | }); 56 | let fields = match &var.fields { 57 | Fields::Unit => quote! {}, 58 | Fields::Named(..) => quote! { { #contents } }, 59 | Fields::Unnamed(..) => quote! { (#contents) }, 60 | }; 61 | quote! { 62 | #accum 63 | #var_ident #fields, 64 | } 65 | }) 66 | } else { 67 | unreachable!(); 68 | }; 69 | 70 | quote! { 71 | #[allow(dead_code)] 72 | #vis enum #ref_ident<'__flatty_a, #generic_params> 73 | #where_clause 74 | { 75 | #variants 76 | } 77 | } 78 | } 79 | 80 | fn gen_ref_impl( 81 | ctx: &Context, 82 | input: &DeriveInput, 83 | mutable: bool, 84 | ref_ident: &Ident, 85 | ref_method_name: TokenStream, 86 | iter_data_type: TokenStream, 87 | ) -> TokenStream { 88 | let mut_ = if mutable { Some(quote! { mut }) } else { None }; 89 | let self_ident = &input.ident; 90 | let tag_type = ctx.idents.tag.as_ref().unwrap(); 91 | 92 | let generic_params = generic::without_defaults(&input.generics).params; 93 | let generic_args = generic::args(&input.generics); 94 | let where_clause = &input.generics.where_clause; 95 | 96 | let match_body = if let Data::Enum(data) = &input.data { 97 | let bind = |i: usize, f: &Field| -> Ident { 98 | match &f.ident { 99 | Some(ident) => ident.clone(), 100 | None => Ident::new(&format!("b{}", i), f.span()), 101 | } 102 | }; 103 | data.variants.iter().fold(quote! {}, |accum, var| { 104 | let var_ident = &var.ident; 105 | let bindings = if !var.fields.is_empty() { 106 | let preface = { 107 | let type_list = type_list(var.fields.iter()); 108 | quote! { 109 | let iter = unsafe { 110 | iter::DataIter::new_unchecked( 111 | iter::#iter_data_type::new(& #mut_ self.data), 112 | iter::type_list!(#type_list), 113 | ) 114 | }; 115 | } 116 | }; 117 | let bindings = { 118 | let iter = var.fields.iter(); 119 | let len = iter.len(); 120 | iter.enumerate().fold(quote! {}, |a, (i, f)| { 121 | let binding = bind(i, f); 122 | let value = if i + 1 < len { 123 | quote! { let (iter, #binding) = iter.next(); } 124 | } else { 125 | quote! { let #binding = iter.finalize(); } 126 | }; 127 | quote! { 128 | #a 129 | #value 130 | } 131 | }) 132 | }; 133 | quote! { 134 | #preface 135 | #bindings 136 | } 137 | } else { 138 | quote! {} 139 | }; 140 | let result = { 141 | let pattern = { 142 | let contents = var.fields.iter().enumerate().fold(quote! {}, |a, (i, f)| { 143 | let binding = bind(i, f); 144 | quote! { #a #binding, } 145 | }); 146 | match &var.fields { 147 | Fields::Unit => quote! {}, 148 | Fields::Named(..) => quote! { { #contents } }, 149 | Fields::Unnamed(..) => quote! { (#contents) }, 150 | } 151 | }; 152 | quote! { #ref_ident::#var_ident #pattern } 153 | }; 154 | quote! { 155 | #accum 156 | #tag_type::#var_ident => { 157 | #bindings 158 | #result 159 | } 160 | } 161 | }) 162 | } else { 163 | unreachable!(); 164 | }; 165 | 166 | quote! { 167 | impl<#generic_params> #self_ident<#generic_args> 168 | #where_clause 169 | { 170 | pub fn #ref_method_name(& #mut_ self) -> #ref_ident<'_, #generic_args> { 171 | use ::flatty::{traits::*, utils::iter::{prelude::*, self}}; 172 | match self.tag { 173 | #match_body 174 | } 175 | } 176 | } 177 | } 178 | } 179 | 180 | pub fn ref_struct(ctx: &Context, input: &DeriveInput) -> TokenStream { 181 | gen_ref_struct(ctx, input, false, ctx.idents.ref_.as_ref().unwrap()) 182 | } 183 | 184 | pub fn ref_impl(ctx: &Context, input: &DeriveInput) -> TokenStream { 185 | gen_ref_impl( 186 | ctx, 187 | input, 188 | false, 189 | ctx.idents.ref_.as_ref().unwrap(), 190 | quote! { as_ref }, 191 | quote! { UncheckedRefData }, 192 | ) 193 | } 194 | 195 | pub fn mut_struct(ctx: &Context, input: &DeriveInput) -> TokenStream { 196 | gen_ref_struct(ctx, input, true, ctx.idents.mut_.as_ref().unwrap()) 197 | } 198 | 199 | pub fn mut_impl(ctx: &Context, input: &DeriveInput) -> TokenStream { 200 | gen_ref_impl( 201 | ctx, 202 | input, 203 | true, 204 | ctx.idents.mut_.as_ref().unwrap(), 205 | quote! { as_mut }, 206 | quote! { UncheckedMutData }, 207 | ) 208 | } 209 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod info; 3 | mod items; 4 | mod utils; 5 | 6 | use context::{AssocIdents, Context}; 7 | use info::Info; 8 | 9 | use proc_macro::TokenStream; 10 | use proc_macro2::Span; 11 | use quote::quote; 12 | use syn::{parse_macro_input, Data, DeriveInput, Ident}; 13 | 14 | /// Attribute macro that creates a flat type from `struct` or `enum` declaration. 15 | /// 16 | /// # Usage examples 17 | /// 18 | /// ```rust_no_check 19 | /// #[flatty::flat(sized = false)] 20 | /// struct ... { ... } 21 | /// ``` 22 | /// 23 | /// or 24 | /// 25 | /// ```rust_no_check 26 | /// #[flatty::flat(sized = false, tag_type = "u32")] 27 | /// enum ... { ... } 28 | /// ``` 29 | /// 30 | /// # Arguments 31 | /// 32 | /// + `sized: bool`, optional, `true` by default. Whether structure is sized or not. 33 | /// + `tag_type: str`, for `enum` declaration only, optional, `"u8"` by default. 34 | /// The type used for enum variant index. Possible values: `"u8"`, `"u16"`, `"u32"`. 35 | /// + `portable: bool`, optional, `false` by default. Whether structure should implement `Portable`. 36 | /// + `default: bool`, optional, `false` by default. Whether to create default constructors (see `FlatDefault`). 37 | #[proc_macro_attribute] 38 | pub fn flat(attr: TokenStream, item: TokenStream) -> TokenStream { 39 | let mut ctx = Context { 40 | info: parse_macro_input!(attr as Info), 41 | idents: AssocIdents::default(), 42 | c_like_enum: None, 43 | }; 44 | let input = parse_macro_input!(item as DeriveInput); 45 | 46 | match &input.data { 47 | Data::Struct(_) => { 48 | assert!(ctx.info.tag_type.is_none(), "`tag_type` is not allowed for `struct`",); 49 | } 50 | Data::Enum(_) => { 51 | if ctx.info.tag_type.is_none() { 52 | ctx.info.tag_type = Some(Ident::new("u8", Span::call_site())); 53 | } 54 | if items::enum_::is_c_like(&input) { 55 | ctx.c_like_enum = Some(true); 56 | ctx.idents.tag = Some(input.ident.clone()); 57 | } else { 58 | ctx.c_like_enum = Some(false); 59 | ctx.idents.tag = Some(Ident::new(&format!("{}Tag", input.ident), input.ident.span())); 60 | }; 61 | if !ctx.info.sized { 62 | ctx.idents.ref_ = Some(Ident::new(&format!("{}Ref", input.ident), input.ident.span())); 63 | ctx.idents.mut_ = Some(Ident::new(&format!("{}Mut", input.ident), input.ident.span())); 64 | } 65 | } 66 | Data::Union(_) => unimplemented!(), 67 | }; 68 | if !ctx.info.sized { 69 | ctx.idents.align_as = Some(Ident::new(&format!("{}AlignAs", input.ident), input.ident.span())); 70 | ctx.idents.init = Some(Ident::new(&format!("{}Init", input.ident), input.ident.span())); 71 | } 72 | 73 | let specific = match &input.data { 74 | Data::Struct(_) => { 75 | if ctx.info.sized { 76 | let derive_default = if ctx.info.default { 77 | quote! { #[derive(Default)] } 78 | } else { 79 | quote! {} 80 | }; 81 | 82 | quote! { 83 | #derive_default 84 | #[repr(C)] 85 | #input 86 | } 87 | } else { 88 | let align_as_struct = items::align_as::struct_(&ctx, &input); 89 | let init_struct = items::init::struct_(&ctx, &input); 90 | 91 | let init_impl = items::init::impl_(&ctx, &input); 92 | 93 | let base_impl = items::base::impl_(&ctx, &input); 94 | let unsized_impl = items::unsized_::impl_(&ctx, &input); 95 | let default_impl = if ctx.info.default { 96 | items::init::impl_default(&ctx, &input) 97 | } else { 98 | quote! {} 99 | }; 100 | 101 | quote! { 102 | #[repr(C)] 103 | #input 104 | 105 | #align_as_struct 106 | #init_struct 107 | 108 | #init_impl 109 | 110 | #base_impl 111 | #unsized_impl 112 | #default_impl 113 | } 114 | } 115 | } 116 | Data::Enum(_) => { 117 | if ctx.info.sized { 118 | let tag_type = ctx.info.tag_type.as_ref().unwrap(); 119 | let derive_default = if ctx.info.default { 120 | quote! { #[derive(Default)] } 121 | } else { 122 | quote! {} 123 | }; 124 | let (repr, tag_struct) = if !ctx.c_like_enum.unwrap() { 125 | (quote! { #[repr(C, #tag_type)] }, items::tag::struct_(&ctx, &input, true)) 126 | } else { 127 | (quote! { #[repr(#tag_type)] }, quote! {}) 128 | }; 129 | 130 | quote! { 131 | #derive_default 132 | #repr 133 | #input 134 | 135 | #tag_struct 136 | } 137 | } else { 138 | assert!(!ctx.c_like_enum.unwrap(), "C-like enums cannot be unsized"); 139 | 140 | let struct_ = items::unsized_enum::struct_(&ctx, &input); 141 | let align_as_struct = items::align_as::struct_(&ctx, &input); 142 | let tag_struct = items::tag::struct_(&ctx, &input, false); 143 | let ref_struct = items::unsized_enum::ref_struct(&ctx, &input); 144 | let mut_struct = items::unsized_enum::mut_struct(&ctx, &input); 145 | let init_struct = items::init::struct_(&ctx, &input); 146 | 147 | let tag_impl = items::tag::impl_(&ctx, &input); 148 | let ref_impl = items::unsized_enum::ref_impl(&ctx, &input); 149 | let mut_impl = items::unsized_enum::mut_impl(&ctx, &input); 150 | let init_impl = items::init::impl_(&ctx, &input); 151 | 152 | let base_impl = items::base::impl_(&ctx, &input); 153 | let unsized_impl = items::unsized_::impl_(&ctx, &input); 154 | let default_impl = if ctx.info.default { 155 | items::init::impl_default(&ctx, &input) 156 | } else { 157 | quote! {} 158 | }; 159 | 160 | quote! { 161 | #struct_ 162 | 163 | #align_as_struct 164 | #tag_struct 165 | #ref_struct 166 | #mut_struct 167 | #init_struct 168 | 169 | #tag_impl 170 | #ref_impl 171 | #mut_impl 172 | #init_impl 173 | 174 | #base_impl 175 | #unsized_impl 176 | #default_impl 177 | } 178 | } 179 | } 180 | Data::Union(_) => unimplemented!(), 181 | }; 182 | 183 | let base_self_impl = items::base::self_impl(&ctx, &input); 184 | let cast_impl = items::cast::impl_(&ctx, &input); 185 | let flat_impl = items::flat::impl_(&ctx, &input); 186 | let portable_impl = if ctx.info.portable { 187 | items::portable::impl_(&ctx, &input) 188 | } else { 189 | quote! {} 190 | }; 191 | 192 | TokenStream::from(quote! { 193 | #specific 194 | 195 | #base_self_impl 196 | #cast_impl 197 | #flat_impl 198 | #portable_impl 199 | }) 200 | } 201 | -------------------------------------------------------------------------------- /macros/src/utils/field_iter.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::quote; 3 | use std::iter::{self, ExactSizeIterator}; 4 | use syn::{self, Field, Fields, FieldsNamed, FieldsUnnamed}; 5 | 6 | pub type FieldIterator<'a> = Box + 'a>; 7 | 8 | pub trait FieldIter { 9 | fn iter(&self) -> FieldIterator<'_>; 10 | } 11 | 12 | impl FieldIter for Fields { 13 | fn iter(&self) -> FieldIterator<'_> { 14 | match self { 15 | Fields::Named(named_fields) => named_fields.iter(), 16 | Fields::Unnamed(unnamed_fields) => unnamed_fields.iter(), 17 | Fields::Unit => Box::new(iter::empty()), 18 | } 19 | } 20 | } 21 | 22 | impl FieldIter for FieldsNamed { 23 | fn iter(&self) -> FieldIterator<'_> { 24 | Box::new(self.named.iter()) 25 | } 26 | } 27 | 28 | impl FieldIter for FieldsUnnamed { 29 | fn iter(&self) -> FieldIterator<'_> { 30 | Box::new(self.unnamed.iter()) 31 | } 32 | } 33 | 34 | pub fn type_list<'a, I: Iterator>(field_iter: I) -> TokenStream { 35 | field_iter.fold(quote! {}, |a, f| { 36 | let ty = &f.ty; 37 | quote! { #a #ty, } 38 | }) 39 | } 40 | -------------------------------------------------------------------------------- /macros/src/utils/generic.rs: -------------------------------------------------------------------------------- 1 | use super::field_iter::FieldIter; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use std::iter::Iterator; 5 | use syn::{self, Data, DeriveInput, GenericParam, Generics}; 6 | 7 | pub fn without_defaults(generics: &Generics) -> Generics { 8 | let mut generics = generics.clone(); 9 | for param in generics.params.iter_mut() { 10 | match param { 11 | GenericParam::Type(p) => { 12 | p.eq_token = None; 13 | p.default = None; 14 | } 15 | GenericParam::Const(p) => { 16 | p.eq_token = None; 17 | p.default = None; 18 | } 19 | GenericParam::Lifetime(_) => (), 20 | } 21 | } 22 | generics 23 | } 24 | 25 | pub fn args(generics: &Generics) -> TokenStream { 26 | generics.params.iter().fold(quote! {}, |accum, param| { 27 | let param = match param { 28 | GenericParam::Type(type_param) => { 29 | let param = &type_param.ident; 30 | quote! { #param } 31 | } 32 | GenericParam::Lifetime(lifetime_def) => { 33 | let param = &lifetime_def.lifetime; 34 | quote! { #param } 35 | } 36 | GenericParam::Const(const_param) => { 37 | let param = &const_param.ident; 38 | quote! { #param } 39 | } 40 | }; 41 | quote! { #accum #param, } 42 | }) 43 | } 44 | 45 | pub fn where_clause(input: &DeriveInput, bound: TokenStream, last_bound: Option) -> TokenStream { 46 | let existing = input.generics.where_clause.as_ref().map_or(quote! {}, |w| { 47 | let wp = &w.predicates; 48 | let comma = if wp.trailing_punct() { 49 | quote! {} 50 | } else { 51 | quote! {,} 52 | }; 53 | quote! { #wp #comma } 54 | }); 55 | 56 | fn collect_fields(fields: &I, bound: &TokenStream, last_bound: Option<&TokenStream>) -> TokenStream { 57 | let iter = fields.iter(); 58 | let len = iter.len(); 59 | iter.enumerate().fold(quote! {}, |accum, (index, field)| { 60 | let ty = &field.ty; 61 | let b = if index + 1 < len { bound } else { last_bound.unwrap_or(bound) }; 62 | quote! { 63 | #accum 64 | #ty: #b, 65 | } 66 | }) 67 | } 68 | 69 | let generated = match &input.data { 70 | Data::Struct(struct_data) => collect_fields(&struct_data.fields, &bound, last_bound.as_ref()), 71 | Data::Enum(enum_data) => enum_data.variants.iter().fold(quote! {}, |accum, variant| { 72 | let variant_clause = collect_fields(&variant.fields, &bound, last_bound.as_ref()); 73 | quote! { #accum #variant_clause } 74 | }), 75 | Data::Union(union_data) => collect_fields(&union_data.fields, &bound, last_bound.as_ref()), 76 | }; 77 | 78 | quote! { where #existing #generated } 79 | } 80 | -------------------------------------------------------------------------------- /macros/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | mod field_iter; 2 | 3 | pub mod generic; 4 | 5 | pub use field_iter::{type_list, FieldIter}; 6 | -------------------------------------------------------------------------------- /portable/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-portable" 3 | version.workspace = true 4 | edition.workspace = true 5 | 6 | description = "Flatty portable trait and primitives" 7 | authors.workspace = true 8 | repository.workspace = true 9 | license.workspace = true 10 | 11 | [dependencies] 12 | flatty-base.workspace = true 13 | num-traits = { version = "0.2", default-features = false } 14 | flatty-containers.workspace = true 15 | serde = { version = "1.0", optional = true, default-features = false } 16 | -------------------------------------------------------------------------------- /portable/src/bool_.rs: -------------------------------------------------------------------------------- 1 | use crate::Portable; 2 | use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not}; 3 | use flatty_base::{ 4 | error::{Error, ErrorKind}, 5 | traits::{Flat, FlatValidate}, 6 | }; 7 | 8 | /// Boolean type that has portable binary representation. 9 | #[derive(Clone, Copy, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 10 | #[repr(u8)] 11 | pub enum Bool { 12 | #[default] 13 | False = 0, 14 | True = 1, 15 | } 16 | 17 | impl From for Bool { 18 | fn from(value: bool) -> Self { 19 | match value { 20 | true => Bool::True, 21 | false => Bool::False, 22 | } 23 | } 24 | } 25 | 26 | impl From for bool { 27 | fn from(value: Bool) -> Self { 28 | match value { 29 | Bool::True => true, 30 | Bool::False => false, 31 | } 32 | } 33 | } 34 | 35 | unsafe impl FlatValidate for Bool { 36 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 37 | match bytes.get_unchecked(0) { 38 | 0..=1 => Ok(()), 39 | _ => Err(Error { 40 | kind: ErrorKind::InvalidData, 41 | pos: 0, 42 | }), 43 | } 44 | } 45 | } 46 | 47 | unsafe impl Flat for Bool {} 48 | 49 | unsafe impl Portable for Bool {} 50 | 51 | impl Not for Bool { 52 | type Output = Self; 53 | fn not(self) -> Self::Output { 54 | bool::from(self).not().into() 55 | } 56 | } 57 | 58 | impl BitAnd for Bool { 59 | type Output = Self; 60 | fn bitand(self, rhs: Self) -> Self::Output { 61 | bool::from(self).bitand(bool::from(rhs)).into() 62 | } 63 | } 64 | 65 | impl BitOr for Bool { 66 | type Output = Self; 67 | fn bitor(self, rhs: Self) -> Self::Output { 68 | bool::from(self).bitor(bool::from(rhs)).into() 69 | } 70 | } 71 | 72 | impl BitXor for Bool { 73 | type Output = Self; 74 | fn bitxor(self, rhs: Self) -> Self::Output { 75 | bool::from(self).bitxor(bool::from(rhs)).into() 76 | } 77 | } 78 | 79 | impl BitAndAssign for Bool { 80 | fn bitand_assign(&mut self, rhs: Self) { 81 | *self = self.bitand(rhs); 82 | } 83 | } 84 | 85 | impl BitOrAssign for Bool { 86 | fn bitor_assign(&mut self, rhs: Self) { 87 | *self = self.bitor(rhs); 88 | } 89 | } 90 | 91 | impl BitXorAssign for Bool { 92 | fn bitxor_assign(&mut self, rhs: Self) { 93 | *self = self.bitxor(rhs); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /portable/src/float.rs: -------------------------------------------------------------------------------- 1 | use crate::{impl_traits_for_native, Portable}; 2 | use core::{ 3 | cmp::{Ordering, PartialOrd}, 4 | ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, 5 | }; 6 | use flatty_base::{ 7 | error::Error, 8 | traits::{Flat, FlatValidate}, 9 | }; 10 | use num_traits::{Bounded, FromPrimitive, Num, NumCast, One, ToPrimitive, Zero}; 11 | 12 | /// Generic portable floating-point number. Has alignment == 1. 13 | /// 14 | /// Parameters: 15 | /// + `BE`: Endianness. `false` => little-endian, `true` => big-endian. 16 | /// + `N`: Width in bytes. 17 | #[repr(C)] 18 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 19 | pub struct Float { 20 | bytes: [u8; N], 21 | } 22 | 23 | impl Default for Float { 24 | fn default() -> Self { 25 | Self { bytes: [0; N] } 26 | } 27 | } 28 | 29 | impl Float { 30 | pub fn from_bytes(bytes: [u8; N]) -> Self { 31 | Self { bytes } 32 | } 33 | pub fn to_bytes(self) -> [u8; N] { 34 | self.bytes 35 | } 36 | } 37 | 38 | unsafe impl FlatValidate for Float { 39 | unsafe fn validate_unchecked(_: &[u8]) -> Result<(), Error> { 40 | Ok(()) 41 | } 42 | } 43 | 44 | unsafe impl Flat for Float {} 45 | 46 | unsafe impl Portable for Float {} 47 | 48 | macro_rules! derive_float { 49 | ($self:ty, $native:ty, $from_bytes:ident, $to_bytes:ident$(,)?) => { 50 | impl $self { 51 | fn from_native(n: $native) -> Self { 52 | Float::from_bytes(n.$to_bytes()) 53 | } 54 | fn to_native(self) -> $native { 55 | <$native>::$from_bytes(self.to_bytes()) 56 | } 57 | } 58 | 59 | impl From<$native> for $self { 60 | fn from(n: $native) -> Self { 61 | Self::from_native(n) 62 | } 63 | } 64 | impl From<$self> for $native { 65 | fn from(s: $self) -> Self { 66 | s.to_native() 67 | } 68 | } 69 | 70 | impl NumCast for $self { 71 | fn from(n: T) -> Option { 72 | Some(Self::from_native(<$native as NumCast>::from::(n)?)) 73 | } 74 | } 75 | impl ToPrimitive for $self { 76 | fn to_u64(&self) -> Option { 77 | self.to_native().to_u64() 78 | } 79 | fn to_i64(&self) -> Option { 80 | self.to_native().to_i64() 81 | } 82 | } 83 | impl FromPrimitive for $self { 84 | fn from_u64(n: u64) -> Option { 85 | Some(Self::from_native(<$native>::from_u64(n)?)) 86 | } 87 | fn from_i64(n: i64) -> Option { 88 | Some(Self::from_native(<$native>::from_i64(n)?)) 89 | } 90 | } 91 | 92 | impl Num for $self { 93 | type FromStrRadixErr = <$native as Num>::FromStrRadixErr; 94 | fn from_str_radix(str: &str, radix: u32) -> Result { 95 | Ok(Self::from_native(<$native as Num>::from_str_radix(str, radix)?)) 96 | } 97 | } 98 | 99 | impl Bounded for $self { 100 | fn min_value() -> Self { 101 | Self::from_native(<$native>::MIN) 102 | } 103 | fn max_value() -> Self { 104 | Self::from_native(<$native>::MAX) 105 | } 106 | } 107 | 108 | impl One for $self { 109 | fn one() -> Self { 110 | Self::from_native(<$native>::one()) 111 | } 112 | } 113 | impl Zero for $self { 114 | fn zero() -> Self { 115 | Self::from_native(<$native>::zero()) 116 | } 117 | fn is_zero(&self) -> bool { 118 | self.to_native().is_zero() 119 | } 120 | } 121 | 122 | impl Add for $self { 123 | type Output = Self; 124 | fn add(self, rhs: Self) -> Self { 125 | Self::from_native(self.to_native() + rhs.to_native()) 126 | } 127 | } 128 | impl Sub for $self { 129 | type Output = Self; 130 | fn sub(self, rhs: Self) -> Self { 131 | Self::from_native(self.to_native() - rhs.to_native()) 132 | } 133 | } 134 | impl Mul for $self { 135 | type Output = Self; 136 | fn mul(self, rhs: Self) -> Self { 137 | Self::from_native(self.to_native() * rhs.to_native()) 138 | } 139 | } 140 | impl Div for $self { 141 | type Output = Self; 142 | fn div(self, rhs: Self) -> Self { 143 | Self::from_native(self.to_native() / rhs.to_native()) 144 | } 145 | } 146 | impl Rem for $self { 147 | type Output = Self; 148 | fn rem(self, rhs: Self) -> Self { 149 | Self::from_native(self.to_native() % rhs.to_native()) 150 | } 151 | } 152 | impl Neg for $self { 153 | type Output = Self; 154 | fn neg(self) -> Self::Output { 155 | Self::from_native(-self.to_native()) 156 | } 157 | } 158 | 159 | impl PartialOrd for $self { 160 | fn partial_cmp(&self, other: &Self) -> Option { 161 | self.to_native().partial_cmp(&other.to_native()) 162 | } 163 | } 164 | 165 | impl AddAssign for $self { 166 | fn add_assign(&mut self, rhs: Self) { 167 | *self = self.add(rhs); 168 | } 169 | } 170 | impl SubAssign for $self { 171 | fn sub_assign(&mut self, rhs: Self) { 172 | *self = self.sub(rhs); 173 | } 174 | } 175 | impl MulAssign for $self { 176 | fn mul_assign(&mut self, rhs: Self) { 177 | *self = self.mul(rhs); 178 | } 179 | } 180 | impl DivAssign for $self { 181 | fn div_assign(&mut self, rhs: Self) { 182 | *self = self.div(rhs); 183 | } 184 | } 185 | impl RemAssign for $self { 186 | fn rem_assign(&mut self, rhs: Self) { 187 | *self = self.rem(rhs); 188 | } 189 | } 190 | 191 | impl_traits_for_native!($self, $native); 192 | }; 193 | } 194 | 195 | macro_rules! derive_le_float { 196 | ($self:ty, $native:ty $(,)?) => { 197 | derive_float!($self, $native, from_le_bytes, to_le_bytes); 198 | }; 199 | } 200 | 201 | macro_rules! derive_be_float { 202 | ($self:ty, $native:ty $(,)?) => { 203 | derive_float!($self, $native, from_be_bytes, to_be_bytes); 204 | }; 205 | } 206 | 207 | derive_le_float!(Float, f32); 208 | derive_le_float!(Float, f64); 209 | 210 | derive_be_float!(Float, f32); 211 | derive_be_float!(Float, f64); 212 | 213 | pub mod le { 214 | use super::Float; 215 | 216 | pub type F32 = Float; 217 | pub type F64 = Float; 218 | } 219 | 220 | pub mod be { 221 | use super::Float; 222 | 223 | pub type F32 = Float; 224 | pub type F64 = Float; 225 | } 226 | -------------------------------------------------------------------------------- /portable/src/impl_.rs: -------------------------------------------------------------------------------- 1 | use crate::Portable; 2 | use core::marker::PhantomData; 3 | use flatty_containers::{ 4 | flex::FlexVec, 5 | string::FlatString, 6 | vec::{FlatVec, Length}, 7 | }; 8 | 9 | unsafe impl Portable for PhantomData {} 10 | 11 | unsafe impl Portable for [T; N] {} 12 | 13 | unsafe impl Portable for FlatVec 14 | where 15 | T: Portable + Sized, 16 | L: Portable + Length, 17 | { 18 | } 19 | 20 | unsafe impl Portable for FlatString where L: Portable + Length {} 21 | 22 | unsafe impl Portable for FlexVec 23 | where 24 | T: Portable + ?Sized, 25 | L: Portable + Length, 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /portable/src/int.rs: -------------------------------------------------------------------------------- 1 | use crate::{impl_traits_for_native, Portable}; 2 | use core::{ 3 | cmp::{Ord, Ordering, PartialOrd}, 4 | ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign}, 5 | }; 6 | use flatty_base::{ 7 | error::Error, 8 | traits::{Flat, FlatValidate}, 9 | }; 10 | use num_traits::{Bounded, FromPrimitive, Num, NumCast, One, Signed, ToPrimitive, Unsigned, Zero}; 11 | 12 | /// Generic portable integer. Has alignment == 1. 13 | /// 14 | /// Parameters: 15 | /// + `BE`: Endianness. `false` => little-endian, `true` => big-endian. 16 | /// + `N`: Width in bytes. 17 | /// + `S`: Whether this type is signed (`true`) or unsigned (`false`). 18 | #[repr(C)] 19 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 20 | pub struct Int { 21 | bytes: [u8; N], 22 | } 23 | 24 | impl Default for Int { 25 | fn default() -> Self { 26 | Self { bytes: [0; N] } 27 | } 28 | } 29 | 30 | impl Int { 31 | pub fn from_bytes(bytes: [u8; N]) -> Self { 32 | Self { bytes } 33 | } 34 | pub fn to_bytes(self) -> [u8; N] { 35 | self.bytes 36 | } 37 | } 38 | 39 | unsafe impl FlatValidate for Int { 40 | unsafe fn validate_unchecked(_: &[u8]) -> Result<(), Error> { 41 | Ok(()) 42 | } 43 | } 44 | 45 | unsafe impl Flat for Int {} 46 | 47 | unsafe impl Portable for Int {} 48 | 49 | macro_rules! derive_int { 50 | ($self:ty, $native:ty, $from_bytes:ident, $to_bytes:ident$(,)?) => { 51 | impl $self { 52 | fn from_native(n: $native) -> Self { 53 | Int::from_bytes(n.$to_bytes()) 54 | } 55 | fn to_native(self) -> $native { 56 | <$native>::$from_bytes(self.to_bytes()) 57 | } 58 | } 59 | 60 | impl From<$native> for $self { 61 | fn from(n: $native) -> Self { 62 | Self::from_native(n) 63 | } 64 | } 65 | impl From<$self> for $native { 66 | fn from(s: $self) -> Self { 67 | s.to_native() 68 | } 69 | } 70 | 71 | impl NumCast for $self { 72 | fn from(n: T) -> Option { 73 | Some(Self::from_native(<$native as NumCast>::from::(n)?)) 74 | } 75 | } 76 | impl ToPrimitive for $self { 77 | fn to_u64(&self) -> Option { 78 | self.to_native().to_u64() 79 | } 80 | fn to_i64(&self) -> Option { 81 | self.to_native().to_i64() 82 | } 83 | } 84 | impl FromPrimitive for $self { 85 | fn from_u64(n: u64) -> Option { 86 | Some(Self::from_native(<$native>::from_u64(n)?)) 87 | } 88 | fn from_i64(n: i64) -> Option { 89 | Some(Self::from_native(<$native>::from_i64(n)?)) 90 | } 91 | } 92 | 93 | impl Num for $self { 94 | type FromStrRadixErr = <$native as Num>::FromStrRadixErr; 95 | fn from_str_radix(str: &str, radix: u32) -> Result { 96 | Ok(Self::from_native(<$native as Num>::from_str_radix(str, radix)?)) 97 | } 98 | } 99 | 100 | impl Bounded for $self { 101 | fn min_value() -> Self { 102 | Self::from_native(<$native>::MIN) 103 | } 104 | fn max_value() -> Self { 105 | Self::from_native(<$native>::MAX) 106 | } 107 | } 108 | 109 | impl One for $self { 110 | fn one() -> Self { 111 | Self::from_native(<$native>::one()) 112 | } 113 | } 114 | impl Zero for $self { 115 | fn zero() -> Self { 116 | Self::from_native(<$native>::zero()) 117 | } 118 | fn is_zero(&self) -> bool { 119 | self.to_native().is_zero() 120 | } 121 | } 122 | 123 | impl Add for $self { 124 | type Output = Self; 125 | fn add(self, rhs: Self) -> Self { 126 | Self::from_native(self.to_native() + rhs.to_native()) 127 | } 128 | } 129 | impl Sub for $self { 130 | type Output = Self; 131 | fn sub(self, rhs: Self) -> Self { 132 | Self::from_native(self.to_native() - rhs.to_native()) 133 | } 134 | } 135 | impl Mul for $self { 136 | type Output = Self; 137 | fn mul(self, rhs: Self) -> Self { 138 | Self::from_native(self.to_native() * rhs.to_native()) 139 | } 140 | } 141 | impl Div for $self { 142 | type Output = Self; 143 | fn div(self, rhs: Self) -> Self { 144 | Self::from_native(self.to_native() / rhs.to_native()) 145 | } 146 | } 147 | impl Rem for $self { 148 | type Output = Self; 149 | fn rem(self, rhs: Self) -> Self { 150 | Self::from_native(self.to_native() % rhs.to_native()) 151 | } 152 | } 153 | 154 | impl Ord for $self { 155 | fn cmp(&self, other: &Self) -> Ordering { 156 | self.to_native().cmp(&other.to_native()) 157 | } 158 | } 159 | impl PartialOrd for $self { 160 | fn partial_cmp(&self, other: &Self) -> Option { 161 | Some(self.to_native().cmp(&other.to_native())) 162 | } 163 | } 164 | 165 | impl AddAssign for $self { 166 | fn add_assign(&mut self, rhs: Self) { 167 | *self = self.add(rhs); 168 | } 169 | } 170 | impl SubAssign for $self { 171 | fn sub_assign(&mut self, rhs: Self) { 172 | *self = self.sub(rhs); 173 | } 174 | } 175 | impl MulAssign for $self { 176 | fn mul_assign(&mut self, rhs: Self) { 177 | *self = self.mul(rhs); 178 | } 179 | } 180 | impl DivAssign for $self { 181 | fn div_assign(&mut self, rhs: Self) { 182 | *self = self.div(rhs); 183 | } 184 | } 185 | impl RemAssign for $self { 186 | fn rem_assign(&mut self, rhs: Self) { 187 | *self = self.rem(rhs); 188 | } 189 | } 190 | 191 | impl_traits_for_native!($self, $native); 192 | }; 193 | } 194 | 195 | macro_rules! derive_int_signed { 196 | ($self:ty, $native:ty, $from_bytes:ident, $to_bytes:ident$(,)?) => { 197 | derive_int!($self, $native, $from_bytes, $to_bytes); 198 | 199 | impl Signed for $self { 200 | fn abs(&self) -> Self { 201 | Self::from_native(self.to_native().abs()) 202 | } 203 | fn abs_sub(&self, other: &Self) -> Self { 204 | Self::from_native(self.to_native().abs_sub(&other.to_native())) 205 | } 206 | fn signum(&self) -> Self { 207 | Self::from_native(self.to_native().signum()) 208 | } 209 | fn is_positive(&self) -> bool { 210 | self.to_native().is_positive() 211 | } 212 | fn is_negative(&self) -> bool { 213 | self.to_native().is_negative() 214 | } 215 | } 216 | 217 | impl Neg for $self { 218 | type Output = Self; 219 | fn neg(self) -> Self::Output { 220 | Self::from_native(-self.to_native()) 221 | } 222 | } 223 | }; 224 | } 225 | 226 | macro_rules! derive_int_unsigned { 227 | ($self:ty, $native:ty, $from_bytes:ident, $to_bytes:ident$(,)?) => { 228 | derive_int!($self, $native, $from_bytes, $to_bytes); 229 | 230 | impl Unsigned for $self {} 231 | }; 232 | } 233 | 234 | macro_rules! derive_le_int_signed { 235 | ($self:ty, $native:ty $(,)?) => { 236 | derive_int_signed!($self, $native, from_le_bytes, to_le_bytes); 237 | }; 238 | } 239 | 240 | macro_rules! derive_le_int_unsigned { 241 | ($self:ty, $native:ty $(,)?) => { 242 | derive_int_unsigned!($self, $native, from_le_bytes, to_le_bytes); 243 | }; 244 | } 245 | 246 | macro_rules! derive_be_int_signed { 247 | ($self:ty, $native:ty $(,)?) => { 248 | derive_int_signed!($self, $native, from_be_bytes, to_be_bytes); 249 | }; 250 | } 251 | 252 | macro_rules! derive_be_int_unsigned { 253 | ($self:ty, $native:ty $(,)?) => { 254 | derive_int_unsigned!($self, $native, from_be_bytes, to_be_bytes); 255 | }; 256 | } 257 | 258 | derive_le_int_unsigned!(Int, u16); 259 | derive_le_int_unsigned!(Int, u32); 260 | derive_le_int_unsigned!(Int, u64); 261 | derive_le_int_signed!(Int, i16); 262 | derive_le_int_signed!(Int, i32); 263 | derive_le_int_signed!(Int, i64); 264 | 265 | derive_be_int_unsigned!(Int, u16); 266 | derive_be_int_unsigned!(Int, u32); 267 | derive_be_int_unsigned!(Int, u64); 268 | derive_be_int_signed!(Int, i16); 269 | derive_be_int_signed!(Int, i32); 270 | derive_be_int_signed!(Int, i64); 271 | 272 | unsafe impl Portable for u8 {} 273 | unsafe impl Portable for i8 {} 274 | 275 | pub mod le { 276 | use super::Int; 277 | 278 | pub type U16 = Int; 279 | pub type U32 = Int; 280 | pub type U64 = Int; 281 | pub type I16 = Int; 282 | pub type I32 = Int; 283 | pub type I64 = Int; 284 | } 285 | 286 | pub mod be { 287 | use super::Int; 288 | 289 | pub type U16 = Int; 290 | pub type U32 = Int; 291 | pub type U64 = Int; 292 | pub type I16 = Int; 293 | pub type I32 = Int; 294 | pub type I64 = Int; 295 | } 296 | -------------------------------------------------------------------------------- /portable/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | mod bool_; 4 | mod float; 5 | mod impl_; 6 | mod int; 7 | #[cfg(test)] 8 | mod tests; 9 | 10 | use flatty_base::traits::Flat; 11 | 12 | /// Type that can be safely transfered between different machines. 13 | /// 14 | /// # Safety 15 | /// 16 | /// Implementing this trait must guarantee that `Self` has the same binary representation on any target platform this crate could be built for. 17 | pub unsafe trait Portable: Flat {} 18 | 19 | unsafe impl Portable for () {} 20 | 21 | pub use bool_::Bool; 22 | pub use float::Float; 23 | pub use int::Int; 24 | 25 | /// Little-endian types. 26 | pub mod le { 27 | use super::*; 28 | pub use float::le::*; 29 | pub use int::le::*; 30 | } 31 | 32 | /// Big-endian types. 33 | pub mod be { 34 | use super::*; 35 | pub use float::be::*; 36 | pub use int::be::*; 37 | } 38 | 39 | pub mod traits { 40 | pub use super::Portable; 41 | } 42 | 43 | macro_rules! impl_traits_for_native { 44 | ($self:ty, $native:ty) => { 45 | impl core::fmt::Debug for $self { 46 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 47 | <$native as core::fmt::Debug>::fmt(&self.to_native(), f) 48 | } 49 | } 50 | impl core::fmt::Display for $self { 51 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 52 | <$native as core::fmt::Display>::fmt(&self.to_native(), f) 53 | } 54 | } 55 | 56 | #[cfg(feature = "serde")] 57 | impl serde::Serialize for $self { 58 | fn serialize(&self, serializer: S) -> Result 59 | where 60 | S: serde::Serializer, 61 | { 62 | <$native as serde::Serialize>::serialize(&self.to_native(), serializer) 63 | } 64 | } 65 | #[cfg(feature = "serde")] 66 | impl<'de> serde::Deserialize<'de> for $self { 67 | fn deserialize(deserializer: D) -> Result 68 | where 69 | D: serde::Deserializer<'de>, 70 | { 71 | <$native as serde::Deserialize<'de>>::deserialize(deserializer).map(<$self>::from_native) 72 | } 73 | } 74 | }; 75 | } 76 | 77 | pub(crate) use impl_traits_for_native; 78 | -------------------------------------------------------------------------------- /portable/src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::le; 2 | use core::mem::align_of_val; 3 | use flatty_base::traits::*; 4 | use flatty_containers::vec::FlatVec; 5 | 6 | #[test] 7 | fn vec() { 8 | let mut bytes = [0u8; 2 + 3 * 4]; 9 | let flat_vec = FlatVec::::default_in_place(&mut bytes).unwrap(); 10 | 11 | flat_vec.push(le::I32::from(0)).unwrap(); 12 | flat_vec.push(le::I32::from(1)).unwrap(); 13 | flat_vec.push(le::I32::from(2)).unwrap(); 14 | assert!(flat_vec.push(le::I32::from(3)).is_err()); 15 | 16 | assert_eq!(FlatVec::::ALIGN, 1); 17 | assert_eq!(align_of_val(flat_vec), 1); 18 | } 19 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Flat message buffers with direct mapping to Rust types without packing/unpacking. 2 | //! 3 | //! # Main traits 4 | //! 5 | //! + [`Flat`] - type that occupies a single contiguous memory area. Guaranteed to always have the same binary representation on the same platform. 6 | //! + [`Portable`] - flat type that has stable platform-independent binary representation and therefore it can be safely transfered between different platforms (of even different endianness). 7 | //! 8 | //! # Basic types 9 | //! 10 | //! ## Sized 11 | //! 12 | //! + Unit type (`()`). 13 | //! + Signed and unsigned integers ([`u8`], [`i8`], [`u16`], [`i16`], [`u32`], [`i32`], [`u64`], [`i64`], [`u128`], [`i128`], [`usize`], [`isize`]). 14 | //! + Floating-point numbers ([`f32`], [`f64`]). 15 | //! + Array of some sized flat type ([`[T; N]`](`array`)` where T: `[`FlatSized`]). 16 | //! 17 | //! ## Unsized 18 | //! 19 | //! + Flat vector ([`FlatVec`](`FlatVec`)). 20 | //! 21 | //! # User-defined types 22 | //! 23 | //! User can create new composite types by using [`flat`] macro. 24 | //! 25 | //! ## Struct 26 | //! 27 | //! ```rust 28 | //! #[flatty::flat] 29 | //! struct SizedStruct { 30 | //! a: u8, 31 | //! b: u16, 32 | //! c: u32, 33 | //! d: [u64; 4], 34 | //! } 35 | //! ``` 36 | //! 37 | //! ## Enum 38 | //! 39 | //! For enum you may explicitly set the type of tag (default value is [`u8`]). 40 | //! 41 | //! ```rust 42 | //! #[flatty::flat(tag_type = "u32")] 43 | //! enum SizedEnum { 44 | //! A, 45 | //! B(u16, u8), 46 | //! C { a: u8, b: u16 }, 47 | //! D(u32), 48 | //! } 49 | //! ``` 50 | //! 51 | //! ## Unsized struct 52 | //! 53 | //! Unsized struct is [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html). The reference to that structure contains its size. 54 | //! 55 | //! ```rust 56 | //! #[flatty::flat(sized = false)] 57 | //! struct UnsizedStruct { 58 | //! a: u8, 59 | //! b: u16, 60 | //! c: flatty::FlatVec, 61 | //! } 62 | //! ``` 63 | //! 64 | //! ## Unsized enum 65 | //! 66 | //! Rust doesn't support [DST](https://doc.rust-lang.org/reference/dynamically-sized-types.html) enums yet so for now enum declaration is translated to unsized structure. 67 | //! 68 | //! But it has `as_ref`/`as_mut` methods that returns a native enum that contains references to original enum fields. 69 | //! 70 | //! ```rust 71 | //! #[flatty::flat(sized = false)] 72 | //! enum UnsizedEnum { 73 | //! A, 74 | //! B(u8, u16), 75 | //! C { a: u8, b: flatty::FlatVec }, 76 | //! } 77 | //! ``` 78 | //! 79 | #![no_std] 80 | 81 | pub use flatty_base::{ 82 | emplacer::{self, Emplacer}, 83 | error::{self, Error}, 84 | traits::{Flat, FlatDefault, FlatSized}, 85 | utils, 86 | }; 87 | #[cfg(feature = "alloc")] 88 | pub use flatty_containers::bytes::AlignedBytes; 89 | pub use flatty_containers::{ 90 | flex::{self, flex_vec, FlexVec}, 91 | string::{self, FlatString}, 92 | vec::{self, flat_vec, FlatVec}, 93 | wrap::{FlatWrap, TrustedRef}, 94 | }; 95 | pub use flatty_macros::flat; 96 | pub use flatty_portable as portable; 97 | 98 | pub use portable::Portable; 99 | 100 | /// All traits. 101 | pub mod traits { 102 | pub use flatty_base::traits::*; 103 | pub use flatty_portable::traits::*; 104 | } 105 | 106 | /// Now it's just an alias to [`traits`]. 107 | pub mod prelude { 108 | pub use super::traits::*; 109 | } 110 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "flatty-tests" 3 | version = "0.0.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | flatty = { path = ".." } 10 | -------------------------------------------------------------------------------- /tests/src/c_like_enum.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use flatty::flat; 4 | 5 | #[derive(Clone, Debug, Default)] 6 | #[flat] 7 | pub enum TagOnly { 8 | #[default] 9 | A, 10 | B, 11 | } 12 | -------------------------------------------------------------------------------- /tests/src/generics.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use flatty::{flat, traits::FlatValidate, Flat, FlatVec}; 4 | use std::marker::PhantomData; 5 | 6 | #[derive(Clone, Copy, Debug)] 7 | struct Unused(PhantomData); 8 | unsafe impl Send for Unused {} 9 | unsafe impl Sync for Unused {} 10 | unsafe impl Flat for Unused {} 11 | unsafe impl FlatValidate for Unused { 12 | unsafe fn validate_unchecked(_: &[u8]) -> Result<(), flatty::Error> { 13 | Ok(()) 14 | } 15 | } 16 | impl Default for Unused { 17 | fn default() -> Self { 18 | Unused(PhantomData) 19 | } 20 | } 21 | 22 | #[flat] 23 | #[derive(Default)] 24 | struct GenericSizedStruct<'a, S: Flat, T: Flat, U = (), const N: usize = 0> 25 | where 26 | U: 'a, 27 | [T; N]: Default, 28 | { 29 | a: S, 30 | b: [T; N], 31 | c: Unused<&'a U>, 32 | } 33 | 34 | #[flat] 35 | #[derive(Default)] 36 | enum GenericSizedEnum<'a, S: Flat, T: Flat, U = (), const N: usize = 0> 37 | where 38 | U: 'a, 39 | [T; N]: Default, 40 | S: Default, 41 | T: Default, 42 | { 43 | A(S, T), 44 | B([T; N]), 45 | C { 46 | x: T, 47 | _p: Unused<&'a U>, 48 | }, 49 | D(GenericSizedStruct<'a, S, T, U, N>), 50 | #[default] 51 | E, 52 | } 53 | 54 | #[flat(sized = false, default = true)] 55 | struct GenericUnsizedStruct<'a, T: Flat, U = (), const N: usize = 0> 56 | where 57 | U: 'a, 58 | [T; N]: Default, 59 | { 60 | a: [T; N], 61 | b: Unused<&'a U>, 62 | c: FlatVec, 63 | } 64 | 65 | #[flat(sized = false, default = true)] 66 | enum GenericUnsizedEnum<'a, S: Flat, T: Flat, U = (), const N: usize = 0> 67 | where 68 | U: 'a, 69 | [T; N]: Default, 70 | S: Default, 71 | T: Default, 72 | { 73 | A(S, T), 74 | B([T; N], FlatVec), 75 | C { 76 | x: T, 77 | _p: Unused<&'a U>, 78 | }, 79 | D(GenericUnsizedStruct<'a, T, U, N>), 80 | #[default] 81 | E, 82 | } 83 | -------------------------------------------------------------------------------- /tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | 3 | mod c_like_enum; 4 | mod generics; 5 | mod not_default; 6 | mod portable; 7 | mod sized_enum; 8 | mod sized_struct; 9 | mod unsized_enum; 10 | mod unsized_sized_enum; 11 | mod unsized_struct; 12 | -------------------------------------------------------------------------------- /tests/src/not_default.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use flatty::{flat, FlatVec}; 4 | 5 | #[flat(default = false)] 6 | #[derive(Clone, Debug, PartialEq, Eq)] 7 | struct SizedStruct { 8 | a: u8, 9 | b: u16, 10 | c: u32, 11 | d: [u64; 4], 12 | } 13 | 14 | #[flat(default = false)] 15 | #[derive(Clone, Debug, PartialEq, Eq)] 16 | enum SizedEnum { 17 | A, 18 | B(u16, u8), 19 | C { a: u8, b: u16 }, 20 | D(u32), 21 | } 22 | 23 | #[flat(sized = false, default = false)] 24 | #[derive(Debug, PartialEq, Eq)] 25 | struct UnsizedStruct { 26 | a: u8, 27 | b: u16, 28 | c: FlatVec, 29 | } 30 | 31 | #[flat(sized = false, default = false)] 32 | enum UnsizedEnum { 33 | A, 34 | B(u8, u16), 35 | C { a: u8, b: FlatVec }, 36 | } 37 | -------------------------------------------------------------------------------- /tests/src/portable.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use flatty::{flat, portable::le, FlatVec}; 4 | 5 | #[flat(portable = true, default = true)] 6 | struct PortableStruct { 7 | a: u8, 8 | b: le::U16, 9 | c: le::U32, 10 | d: [le::U64; 4], 11 | } 12 | 13 | #[flat(portable = true, default = true)] 14 | enum PortableEnum { 15 | #[default] 16 | A, 17 | B(le::F32, PortableStruct), 18 | C(PortableStruct), 19 | } 20 | 21 | #[flat(sized = false, portable = true, default = true)] 22 | struct PortableUnsizedStruct { 23 | a: le::U16, 24 | b: FlatVec, 25 | } 26 | 27 | #[flat(sized = false, portable = true, default = true)] 28 | enum PortableUnsizedEnum { 29 | #[default] 30 | A, 31 | B(le::F32, PortableStruct), 32 | C(PortableUnsizedStruct), 33 | } 34 | -------------------------------------------------------------------------------- /tests/src/sized_enum/auto.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::flat; 3 | 4 | #[derive(Default, Clone, Debug, PartialEq, Eq)] 5 | #[flat] 6 | enum SizedEnum { 7 | #[default] 8 | A, 9 | B(u16, u8), 10 | C { 11 | a: u8, 12 | b: u16, 13 | }, 14 | D(u32), 15 | } 16 | 17 | generate_tests!(); 18 | -------------------------------------------------------------------------------- /tests/src/sized_enum/manual.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::{ 3 | error::{Error, ErrorKind}, 4 | prelude::*, 5 | utils::{ 6 | ceil_mul, 7 | iter::{self, prelude::*}, 8 | }, 9 | }; 10 | 11 | #[derive(Clone, Default, Debug, PartialEq, Eq)] 12 | #[repr(C, u8)] 13 | enum SizedEnum { 14 | #[default] 15 | A, 16 | B(u16, u8), 17 | C { 18 | a: u8, 19 | b: u16, 20 | }, 21 | D(u32), 22 | } 23 | 24 | unsafe impl FlatValidate for SizedEnum { 25 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 26 | u8::validate_unchecked(bytes)?; 27 | let tag = u8::from_bytes_unchecked(bytes); 28 | 29 | let data_offset: usize = ceil_mul(u8::SIZE, Self::ALIGN); 30 | let bytes = unsafe { bytes.get_unchecked(data_offset..) }; 31 | match tag { 32 | 0 => Ok(()), 33 | 1 => unsafe { iter::BytesIter::new_unchecked(bytes, iter::type_list!(u16, u8)) } 34 | .validate_all() 35 | .map_err(|e| e.offset(data_offset)), 36 | 2 => unsafe { iter::BytesIter::new_unchecked(bytes, iter::type_list!(u8, u16)) } 37 | .validate_all() 38 | .map_err(|e| e.offset(data_offset)), 39 | 3 => unsafe { iter::BytesIter::new_unchecked(bytes, iter::type_list!(u32)) } 40 | .validate_all() 41 | .map_err(|e| e.offset(data_offset)), 42 | _ => Err(Error { 43 | kind: ErrorKind::InvalidEnumTag, 44 | pos: 0, 45 | }), 46 | } 47 | } 48 | } 49 | 50 | unsafe impl Flat for SizedEnum {} 51 | 52 | generate_tests!(); 53 | -------------------------------------------------------------------------------- /tests/src/sized_enum/mod.rs: -------------------------------------------------------------------------------- 1 | mod auto; 2 | mod manual; 3 | mod tests; 4 | -------------------------------------------------------------------------------- /tests/src/sized_enum/tests.rs: -------------------------------------------------------------------------------- 1 | macro_rules! generate_tests { 2 | () => { 3 | mod tests { 4 | use super::SizedEnum; 5 | use core::mem::{align_of, size_of}; 6 | use flatty::{prelude::*, AlignedBytes}; 7 | 8 | #[test] 9 | fn init_a() { 10 | let mut m = AlignedBytes::new(4 + 4, 4); 11 | SizedEnum::new_in_place(&mut m, SizedEnum::A).unwrap(); 12 | assert_eq!(m[0], 0); 13 | } 14 | 15 | #[test] 16 | fn init_b() { 17 | let mut m = AlignedBytes::new(4 + 4, 4); 18 | let se = SizedEnum::new_in_place(&mut m, SizedEnum::B(0x1234, 0x56)).unwrap(); 19 | 20 | if let SizedEnum::B(a, b) = se { 21 | assert_eq!(*a, 0x1234); 22 | assert_eq!(*b, 0x56); 23 | } else { 24 | panic!(); 25 | } 26 | 27 | assert_eq!(m[0], 1); 28 | assert_eq!(&m[4..7], [0x34, 0x12, 0x56]); 29 | } 30 | 31 | #[test] 32 | fn init_c() { 33 | let mut m = AlignedBytes::new(4 + 4, 4); 34 | let se = SizedEnum::new_in_place(&mut m, SizedEnum::C { a: 0xab, b: 0xcdef }).unwrap(); 35 | 36 | if let SizedEnum::C { a, b } = se { 37 | assert_eq!(*a, 0xab); 38 | assert_eq!(*b, 0xcdef); 39 | } else { 40 | panic!(); 41 | } 42 | 43 | assert_eq!(m[0], 2); 44 | assert_eq!(m[4], 0xab); 45 | assert_eq!(&m[6..], [0xef, 0xcd]); 46 | } 47 | 48 | #[test] 49 | fn init_d() { 50 | let mut m = AlignedBytes::new(4 + 4, 4); 51 | let se = SizedEnum::new_in_place(&mut m, SizedEnum::D(0x12345678)).unwrap(); 52 | 53 | if let SizedEnum::D(a) = se { 54 | assert_eq!(*a, 0x12345678); 55 | } else { 56 | panic!(); 57 | } 58 | 59 | assert_eq!(m[0], 3); 60 | assert_eq!(&m[4..], [0x78, 0x56, 0x34, 0x12]); 61 | } 62 | 63 | #[test] 64 | fn interpret_c() { 65 | let m = AlignedBytes::from_slice(&[2, 0, 0, 0, 0xab, 0, 0xef, 0xcd], 4); 66 | let s = SizedEnum::from_bytes(&m).unwrap(); 67 | 68 | if let SizedEnum::C { a, b } = s { 69 | assert_eq!(*a, 0xab); 70 | assert_eq!(*b, 0xcdef); 71 | } else { 72 | panic!(); 73 | } 74 | } 75 | 76 | #[test] 77 | fn layout() { 78 | let mut m = AlignedBytes::new(4 + 4, 4); 79 | let se = SizedEnum::default_in_place(&mut m).unwrap(); 80 | 81 | assert_eq!(align_of::(), ::ALIGN); 82 | assert_eq!(size_of::(), ::SIZE); 83 | assert_eq!(::SIZE, se.size()); 84 | } 85 | } 86 | }; 87 | } 88 | 89 | pub(crate) use generate_tests; 90 | -------------------------------------------------------------------------------- /tests/src/sized_struct/auto.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::flat; 3 | 4 | #[derive(Default, Clone, Debug, PartialEq, Eq)] 5 | #[flat] 6 | struct SizedStruct { 7 | a: u8, 8 | b: u16, 9 | c: u32, 10 | d: [u64; 4], 11 | } 12 | 13 | generate_tests!(); 14 | -------------------------------------------------------------------------------- /tests/src/sized_struct/manual.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::{ 3 | prelude::*, 4 | utils::iter::{self, prelude::*, type_list}, 5 | Error, 6 | }; 7 | 8 | #[derive(Clone, Default, Debug, PartialEq, Eq)] 9 | #[repr(C)] 10 | struct SizedStruct { 11 | a: u8, 12 | b: u16, 13 | c: u32, 14 | d: [u64; 4], 15 | } 16 | 17 | unsafe impl FlatValidate for SizedStruct { 18 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 19 | unsafe { iter::BytesIter::new_unchecked(bytes, type_list!(u8, u16, u32, [u64; 4])) }.validate_all()?; 20 | Ok(()) 21 | } 22 | } 23 | 24 | unsafe impl Flat for SizedStruct {} 25 | 26 | generate_tests!(); 27 | -------------------------------------------------------------------------------- /tests/src/sized_struct/mod.rs: -------------------------------------------------------------------------------- 1 | mod auto; 2 | mod manual; 3 | mod tests; 4 | -------------------------------------------------------------------------------- /tests/src/sized_struct/tests.rs: -------------------------------------------------------------------------------- 1 | macro_rules! generate_tests { 2 | () => { 3 | mod tests { 4 | use super::SizedStruct; 5 | use core::mem::{align_of, size_of}; 6 | use flatty::{prelude::*, AlignedBytes}; 7 | 8 | #[test] 9 | fn init() { 10 | let mut m = AlignedBytes::new(16 + 8 * 4, 8); 11 | let ss = SizedStruct::new_in_place( 12 | &mut m, 13 | SizedStruct { 14 | a: 200, 15 | b: 40000, 16 | c: 2000000000, 17 | d: [1, 2, 3, 4], 18 | }, 19 | ) 20 | .unwrap(); 21 | 22 | assert_eq!(ss.a, 200); 23 | assert_eq!(ss.b, 40000); 24 | assert_eq!(ss.c, 2000000000); 25 | assert_eq!(ss.d, [1, 2, 3, 4]); 26 | } 27 | 28 | #[test] 29 | fn default() { 30 | let mut m = AlignedBytes::new(16 + 8 * 4, 8); 31 | let ss = SizedStruct::default_in_place(&mut m).unwrap(); 32 | 33 | assert_eq!(ss.a, u8::default()); 34 | assert_eq!(ss.b, u16::default()); 35 | assert_eq!(ss.c, u32::default()); 36 | assert_eq!(ss.d, <[u64; 4]>::default()); 37 | } 38 | 39 | #[test] 40 | fn interpret() { 41 | let um = (0..4).fold(vec![0x12, 0xff, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12], |mut a, i| { 42 | a.extend_from_slice(&[i + 1, 0, 0, 0, 0, 0, 0, 0]); 43 | a 44 | }); 45 | let m = AlignedBytes::from_slice(&um, 8); 46 | let ss = SizedStruct::from_bytes(&m).unwrap(); 47 | 48 | assert_eq!(ss.a, 0x12); 49 | assert_eq!(ss.b, 0x1234); 50 | assert_eq!(ss.c, 0x12345678); 51 | assert_eq!(ss.d, [1, 2, 3, 4]); 52 | } 53 | 54 | #[test] 55 | fn layout() { 56 | let mut m = AlignedBytes::new(16 + 8 * 4, 8); 57 | let ss = SizedStruct::default_in_place(&mut m).unwrap(); 58 | 59 | assert_eq!(align_of::(), ::ALIGN); 60 | assert_eq!(size_of::(), ::SIZE); 61 | assert_eq!(::SIZE, ss.size()); 62 | } 63 | } 64 | }; 65 | } 66 | 67 | pub(crate) use generate_tests; 68 | -------------------------------------------------------------------------------- /tests/src/unsized_enum/auto.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::{flat, FlatVec}; 3 | 4 | #[flat(sized = false, default = true)] 5 | enum UnsizedEnum { 6 | #[default] 7 | A, 8 | B(u8, u16), 9 | C { 10 | offset: u32, 11 | bytes: FlatVec, 12 | }, 13 | } 14 | 15 | generate_tests!(); 16 | -------------------------------------------------------------------------------- /tests/src/unsized_enum/manual.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use core::mem::align_of; 3 | use flatty::{ 4 | emplacer::{Emplacer, NeverEmplacer}, 5 | error::{Error, ErrorKind}, 6 | prelude::*, 7 | utils::{ 8 | ceil_mul, floor_mul, 9 | iter::{self, prelude::*}, 10 | mem::{set_slice_ptr_len, slice_ptr_len}, 11 | min, 12 | }, 13 | FlatVec, 14 | }; 15 | 16 | #[repr(C)] 17 | struct UnsizedEnum { 18 | tag: UnsizedEnumTag, 19 | _align: [UnsizedEnumAlignAs; 0], 20 | data: [u8], 21 | } 22 | 23 | #[repr(u8)] 24 | #[derive(Clone, Copy, Default, PartialEq, Eq, Debug)] 25 | enum UnsizedEnumTag { 26 | #[default] 27 | A = 0, 28 | B, 29 | C, 30 | } 31 | 32 | unsafe impl FlatValidate for UnsizedEnumTag { 33 | unsafe fn validate_unchecked(__bytes: &[u8]) -> Result<(), Error> { 34 | u8::validate_unchecked(__bytes)?; 35 | let tag = u8::from_bytes_unchecked(__bytes); 36 | if *tag < 3 { 37 | Ok(()) 38 | } else { 39 | Err(Error { 40 | kind: ErrorKind::InvalidEnumTag, 41 | pos: 0, 42 | }) 43 | } 44 | } 45 | } 46 | 47 | unsafe impl Flat for UnsizedEnumTag {} 48 | 49 | #[allow(dead_code)] 50 | enum UnsizedEnumRef<'a> { 51 | A, 52 | B(&'a u8, &'a u16), 53 | C { offset: &'a u32, bytes: &'a FlatVec }, 54 | } 55 | 56 | #[allow(dead_code)] 57 | enum UnsizedEnumMut<'a> { 58 | A, 59 | B(&'a mut u8, &'a mut u16), 60 | C { 61 | offset: &'a mut u32, 62 | bytes: &'a mut FlatVec, 63 | }, 64 | } 65 | 66 | #[repr(C)] 67 | struct UnsizedEnumAlignAs(u8, u8, u16, u32, as FlatUnsized>::AlignAs); 68 | 69 | #[allow(dead_code)] 70 | enum UnsizedEnumInit { 71 | A, 72 | B(B0, B1), 73 | C { offset: CA, bytes: CB }, 74 | } 75 | 76 | #[allow(dead_code)] 77 | struct UnsizedEnumInitA; 78 | 79 | #[allow(dead_code)] 80 | struct UnsizedEnumInitB(pub B0, pub B1); 81 | 82 | #[allow(dead_code)] 83 | struct UnsizedEnumInitC { 84 | pub offset: CA, 85 | pub bytes: CB, 86 | } 87 | 88 | impl From for UnsizedEnumInit { 89 | fn from(_: UnsizedEnumInitA) -> Self { 90 | Self::A 91 | } 92 | } 93 | 94 | impl From> for UnsizedEnumInit { 95 | fn from(this: UnsizedEnumInitB) -> Self { 96 | Self::B(this.0, this.1) 97 | } 98 | } 99 | 100 | impl From> for UnsizedEnumInit { 101 | fn from(this: UnsizedEnumInitC) -> Self { 102 | Self::C { 103 | offset: this.offset, 104 | bytes: this.bytes, 105 | } 106 | } 107 | } 108 | 109 | unsafe impl Emplacer for UnsizedEnumInitA { 110 | unsafe fn emplace_unchecked(self, __bytes: &mut [u8]) -> Result<&mut UnsizedEnum, Error> { 111 | UnsizedEnumInit::from(self).emplace_unchecked(__bytes) 112 | } 113 | } 114 | 115 | unsafe impl Emplacer for UnsizedEnumInitB 116 | where 117 | B0: Emplacer, 118 | B1: Emplacer, 119 | { 120 | unsafe fn emplace_unchecked(self, __bytes: &mut [u8]) -> Result<&mut UnsizedEnum, Error> { 121 | UnsizedEnumInit::from(self).emplace_unchecked(__bytes) 122 | } 123 | } 124 | 125 | unsafe impl Emplacer for UnsizedEnumInitC 126 | where 127 | CA: Emplacer, 128 | CB: Emplacer>, 129 | { 130 | unsafe fn emplace_unchecked(self, __bytes: &mut [u8]) -> Result<&mut UnsizedEnum, Error> { 131 | UnsizedEnumInit::from(self).emplace_unchecked(__bytes) 132 | } 133 | } 134 | 135 | impl UnsizedEnumInit { 136 | fn tag(&self) -> UnsizedEnumTag { 137 | match self { 138 | Self::A => UnsizedEnumTag::A, 139 | Self::B(..) => UnsizedEnumTag::B, 140 | Self::C { .. } => UnsizedEnumTag::C, 141 | } 142 | } 143 | } 144 | 145 | unsafe impl Emplacer for UnsizedEnumInit 146 | where 147 | B0: Emplacer, 148 | B1: Emplacer, 149 | CA: Emplacer, 150 | CB: Emplacer>, 151 | { 152 | unsafe fn emplace_unchecked(self, __bytes: &mut [u8]) -> Result<&mut UnsizedEnum, Error> { 153 | self.tag().emplace_unchecked(__bytes)?; 154 | match self { 155 | Self::A => (), 156 | Self::B(b0, b1) => { 157 | let iter = iter::BytesMutIter::new( 158 | unsafe { __bytes.get_unchecked_mut(UnsizedEnum::DATA_OFFSET..) }, 159 | iter::type_list!(u8, u16), 160 | ) 161 | .map_err(|e| e.offset(UnsizedEnum::DATA_OFFSET))?; 162 | let (iter, u0) = iter.next(); 163 | b0.emplace_unchecked(u0)?; 164 | let u1 = iter.finalize(); 165 | b1.emplace_unchecked(u1)?; 166 | } 167 | Self::C { offset, bytes } => { 168 | let iter = iter::BytesMutIter::new( 169 | unsafe { __bytes.get_unchecked_mut(UnsizedEnum::DATA_OFFSET..) }, 170 | iter::type_list!(u32, FlatVec), 171 | ) 172 | .map_err(|e| e.offset(UnsizedEnum::DATA_OFFSET))?; 173 | let (iter, __u_offset) = iter.next(); 174 | offset.emplace_unchecked(__u_offset)?; 175 | let __u_bytes = iter.finalize(); 176 | bytes.emplace_unchecked(__u_bytes)?; 177 | } 178 | } 179 | Ok(unsafe { UnsizedEnum::from_mut_bytes_unchecked(__bytes) }) 180 | } 181 | } 182 | 183 | impl UnsizedEnum { 184 | const DATA_OFFSET: usize = ceil_mul(u8::SIZE, Self::ALIGN); 185 | const LAST_FIELD_OFFSETS: [usize; 3] = [ 186 | 0, 187 | ceil_mul(iter::fold_size!(0; u8), u16::ALIGN), 188 | ceil_mul(iter::fold_size!(0; u32), FlatVec::::ALIGN), 189 | ]; 190 | const DATA_MIN_SIZES: [usize; 3] = [ 191 | 0, 192 | ceil_mul(Self::LAST_FIELD_OFFSETS[1] + u16::MIN_SIZE, Self::ALIGN), 193 | ceil_mul(Self::LAST_FIELD_OFFSETS[2] + FlatVec::::MIN_SIZE, Self::ALIGN), 194 | ]; 195 | 196 | pub fn tag(&self) -> UnsizedEnumTag { 197 | self.tag 198 | } 199 | 200 | pub fn as_ref(&self) -> UnsizedEnumRef<'_> { 201 | match self.tag { 202 | UnsizedEnumTag::A => UnsizedEnumRef::A, 203 | UnsizedEnumTag::B => { 204 | let iter = unsafe { 205 | iter::RefIter::new_unchecked(iter::UncheckedRefData::new(&self.data), iter::type_list!(u8, u16)) 206 | }; 207 | let (iter, b0) = iter.next(); 208 | let b1 = iter.finalize(); 209 | UnsizedEnumRef::B(b0, b1) 210 | } 211 | UnsizedEnumTag::C => { 212 | let iter = unsafe { 213 | iter::RefIter::new_unchecked( 214 | iter::UncheckedRefData::new(&self.data), 215 | iter::type_list!(u32, FlatVec), 216 | ) 217 | }; 218 | let (iter, offset) = iter.next(); 219 | let bytes = iter.finalize(); 220 | UnsizedEnumRef::C { offset, bytes } 221 | } 222 | } 223 | } 224 | pub fn as_mut(&mut self) -> UnsizedEnumMut<'_> { 225 | match self.tag { 226 | UnsizedEnumTag::A => UnsizedEnumMut::A, 227 | UnsizedEnumTag::B => { 228 | let iter = unsafe { 229 | iter::MutIter::new_unchecked(iter::UncheckedMutData::new(&mut self.data), iter::type_list!(u8, u16)) 230 | }; 231 | let (iter, b0) = iter.next(); 232 | let b1 = iter.finalize(); 233 | UnsizedEnumMut::B(b0, b1) 234 | } 235 | UnsizedEnumTag::C => { 236 | let iter = unsafe { 237 | iter::MutIter::new_unchecked( 238 | iter::UncheckedMutData::new(&mut self.data), 239 | iter::type_list!(u32, FlatVec), 240 | ) 241 | }; 242 | let (iter, offset) = iter.next(); 243 | let bytes = iter.finalize(); 244 | UnsizedEnumMut::C { offset, bytes } 245 | } 246 | } 247 | } 248 | } 249 | 250 | unsafe impl FlatBase for UnsizedEnum { 251 | const ALIGN: usize = align_of::(); 252 | const MIN_SIZE: usize = 253 | Self::DATA_OFFSET + min(Self::DATA_MIN_SIZES[0], min(Self::DATA_MIN_SIZES[1], Self::DATA_MIN_SIZES[2])); 254 | 255 | fn size(&self) -> usize { 256 | ceil_mul( 257 | Self::DATA_OFFSET 258 | + match self.tag { 259 | UnsizedEnumTag::A => 0, 260 | UnsizedEnumTag::B => unsafe { 261 | iter::BytesIter::new_unchecked(&self.data, iter::type_list!(u8, u16)).fold_size(0) 262 | }, 263 | UnsizedEnumTag::C => unsafe { 264 | iter::BytesIter::new_unchecked(&self.data, iter::type_list!(u32, FlatVec)).fold_size(0) 265 | }, 266 | }, 267 | Self::ALIGN, 268 | ) 269 | } 270 | } 271 | 272 | unsafe impl FlatUnsized for UnsizedEnum { 273 | type AlignAs = UnsizedEnumAlignAs; 274 | 275 | unsafe fn ptr_from_bytes(__bytes: *mut [u8]) -> *mut Self { 276 | set_slice_ptr_len(__bytes, floor_mul(slice_ptr_len(__bytes) - Self::DATA_OFFSET, Self::ALIGN)) as *mut Self 277 | } 278 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 279 | let __bytes = this as *mut [u8]; 280 | set_slice_ptr_len(__bytes, Self::DATA_OFFSET + slice_ptr_len(__bytes)) 281 | } 282 | } 283 | 284 | unsafe impl FlatValidate for UnsizedEnum { 285 | unsafe fn validate_unchecked(__bytes: &[u8]) -> Result<(), Error> { 286 | UnsizedEnumTag::validate_unchecked(__bytes)?; 287 | let tag = UnsizedEnumTag::from_bytes_unchecked(__bytes); 288 | let __bytes = unsafe { __bytes.get_unchecked(Self::DATA_OFFSET..) }; 289 | if __bytes.len() < Self::DATA_MIN_SIZES[*tag as usize] { 290 | return Err(Error { 291 | kind: ErrorKind::InsufficientSize, 292 | pos: Self::DATA_OFFSET, 293 | }); 294 | } 295 | match tag { 296 | UnsizedEnumTag::A => Ok(()), 297 | UnsizedEnumTag::B => unsafe { iter::BytesIter::new_unchecked(__bytes, iter::type_list!(u8, u16)).validate_all() }, 298 | UnsizedEnumTag::C => unsafe { 299 | iter::BytesIter::new_unchecked(__bytes, iter::type_list!(u32, FlatVec)).validate_all() 300 | }, 301 | } 302 | .map_err(|e| e.offset(Self::DATA_OFFSET)) 303 | } 304 | } 305 | 306 | impl FlatDefault for UnsizedEnum { 307 | type DefaultEmplacer = UnsizedEnumInitA; 308 | fn default_emplacer() -> Self::DefaultEmplacer { 309 | UnsizedEnumInitA 310 | } 311 | } 312 | 313 | unsafe impl Flat for UnsizedEnum {} 314 | 315 | generate_tests!(); 316 | -------------------------------------------------------------------------------- /tests/src/unsized_enum/mod.rs: -------------------------------------------------------------------------------- 1 | mod auto; 2 | mod manual; 3 | mod tests; 4 | -------------------------------------------------------------------------------- /tests/src/unsized_enum/tests.rs: -------------------------------------------------------------------------------- 1 | macro_rules! generate_tests { 2 | () => { 3 | mod tests { 4 | use super::{UnsizedEnum, UnsizedEnumInitB, UnsizedEnumInitC, UnsizedEnumMut, UnsizedEnumRef, UnsizedEnumTag}; 5 | use core::mem::{align_of_val, size_of_val}; 6 | use flatty::{ 7 | error::{Error, ErrorKind}, 8 | flat_vec, 9 | prelude::*, 10 | AlignedBytes, 11 | }; 12 | 13 | #[test] 14 | fn init_a() { 15 | let mut mem = AlignedBytes::new(8, 4); 16 | let ue = UnsizedEnum::default_in_place(&mut mem).unwrap(); 17 | assert_eq!(ue.size(), 4); 18 | 19 | match ue.as_ref() { 20 | UnsizedEnumRef::A => (), 21 | _ => panic!(), 22 | } 23 | 24 | assert_eq!(mem[0], 0); 25 | } 26 | 27 | #[test] 28 | fn init_b() { 29 | let mut mem = AlignedBytes::new(8, 4); 30 | let ue = UnsizedEnum::new_in_place(&mut mem, UnsizedEnumInitB(0xab, 0xcdef)).unwrap(); 31 | assert_eq!(ue.size(), 8); 32 | 33 | match ue.as_ref() { 34 | UnsizedEnumRef::B(x, y) => { 35 | assert_eq!(*x, 0xab); 36 | assert_eq!(*y, 0xcdef); 37 | } 38 | _ => panic!(), 39 | } 40 | 41 | assert_eq!(mem[0], 1); 42 | assert_eq!(mem[4], 0xab); 43 | assert_eq!(&mem[6..], [0xef, 0xcd]); 44 | } 45 | 46 | #[test] 47 | fn init_c() { 48 | let mut mem = AlignedBytes::new(16, 4); 49 | let ue = UnsizedEnum::new_in_place( 50 | &mut mem, 51 | UnsizedEnumInitC { 52 | offset: 0x12345678, 53 | bytes: flat_vec![0x12, 0x34, 0x56, 0x78], 54 | }, 55 | ) 56 | .unwrap(); 57 | assert_eq!(ue.size(), 16); 58 | 59 | match ue.as_mut() { 60 | UnsizedEnumMut::C { offset, bytes } => { 61 | assert_eq!(*offset, 0x12345678); 62 | assert_eq!(bytes.len(), 4); 63 | assert_eq!(bytes.capacity(), 6); 64 | bytes.push(0x9a).unwrap(); 65 | bytes.push(0xbc).unwrap(); 66 | assert!(bytes.push(0xde).is_err()); 67 | } 68 | _ => panic!(), 69 | } 70 | assert_eq!(ue.size(), 16); 71 | 72 | assert_eq!(mem[0], 2); 73 | assert_eq!(mem[4..8], [0x78, 0x56, 0x34, 0x12]); 74 | assert_eq!(&mem[8..10], [6, 0]); 75 | assert_eq!(&mem[10..], [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc]); 76 | } 77 | 78 | #[test] 79 | fn tag() { 80 | let mut mem = AlignedBytes::new(8, 4); 81 | let ue = UnsizedEnum::default_in_place(&mut mem).unwrap(); 82 | assert_eq!(ue.tag(), UnsizedEnumTag::A); 83 | ue.assign_in_place(UnsizedEnumInitB(0, 0)).unwrap(); 84 | assert_eq!(ue.tag(), UnsizedEnumTag::B); 85 | } 86 | 87 | #[test] 88 | fn from_bytes_err() { 89 | let mut mem = AlignedBytes::new(1, 2); 90 | let res = UnsizedEnum::from_mut_bytes(&mut mem); 91 | assert_eq!( 92 | res.err().unwrap(), 93 | Error { 94 | kind: ErrorKind::InsufficientSize, 95 | pos: 0 96 | } 97 | ); 98 | } 99 | 100 | #[test] 101 | fn validate_err() { 102 | let mut mem = AlignedBytes::from_slice(&[1, 0, 0, 0, 0, 0], 4); 103 | let res = UnsizedEnum::from_mut_bytes(&mut mem); 104 | assert_eq!( 105 | res.err().unwrap(), 106 | Error { 107 | kind: ErrorKind::InsufficientSize, 108 | pos: 4 109 | } 110 | ); 111 | } 112 | 113 | #[test] 114 | fn set_err() { 115 | let mut mem = AlignedBytes::new(4, 4); 116 | let ue = UnsizedEnum::default_in_place(&mut mem).unwrap(); 117 | let res = ue.assign_in_place(UnsizedEnumInitB(0, 0)); 118 | assert_eq!( 119 | res.err().unwrap(), 120 | Error { 121 | kind: ErrorKind::InsufficientSize, 122 | pos: 4 123 | } 124 | ); 125 | } 126 | 127 | #[test] 128 | fn bad_tag() { 129 | let mem = AlignedBytes::from_slice(&[4, 0, 0, 0], 4); 130 | let res = UnsizedEnum::from_bytes(&mem); 131 | assert_eq!( 132 | res.err().unwrap(), 133 | Error { 134 | kind: ErrorKind::InvalidEnumTag, 135 | pos: 0 136 | } 137 | ); 138 | } 139 | 140 | #[test] 141 | fn layout() { 142 | let mut mem = AlignedBytes::new(4 + 4 + 2 + 6 + 3, 2); 143 | let ue = UnsizedEnum::new_in_place( 144 | &mut mem, 145 | UnsizedEnumInitC { 146 | offset: 0x12345678, 147 | bytes: flat_vec![], 148 | }, 149 | ) 150 | .unwrap(); 151 | if let UnsizedEnumMut::C { offset: _, bytes } = ue.as_mut() { 152 | for i in 0.. { 153 | if bytes.push(i).is_err() { 154 | break; 155 | } 156 | } 157 | } else { 158 | unreachable!(); 159 | } 160 | 161 | assert_eq!(UnsizedEnum::DATA_OFFSET, 4); 162 | assert_eq!(align_of_val(ue), ::ALIGN); 163 | assert_eq!(size_of_val(ue), ue.size()); 164 | assert_eq!(ue.size(), mem.len() - 3); 165 | } 166 | } 167 | }; 168 | } 169 | 170 | pub(crate) use generate_tests; 171 | -------------------------------------------------------------------------------- /tests/src/unsized_sized_enum.rs: -------------------------------------------------------------------------------- 1 | use flatty::{error::ErrorKind, flat, prelude::*, AlignedBytes}; 2 | 3 | #[flat(sized = false, default = true)] 4 | #[derive(Default, Debug, PartialEq, Eq)] 5 | enum UnsizedSizedEnum { 6 | #[default] 7 | A, 8 | B(u8, u16), 9 | C { 10 | a: u8, 11 | b: u16, 12 | c: [u8; 4], 13 | }, 14 | } 15 | 16 | #[test] 17 | fn init_a() { 18 | let mut mem = AlignedBytes::new(2, 2); 19 | let use_ = UnsizedSizedEnum::default_in_place(&mut mem).unwrap(); 20 | assert_eq!(use_.size(), 2); 21 | 22 | match use_.as_ref() { 23 | UnsizedSizedEnumRef::A => (), 24 | _ => panic!(), 25 | } 26 | 27 | assert_eq!(mem[0], 0); 28 | } 29 | 30 | #[test] 31 | fn init_b() { 32 | let mut mem = AlignedBytes::new(6, 2); 33 | let use_ = UnsizedSizedEnum::new_in_place(&mut mem, UnsizedSizedEnumInitB(0xab, 0xcdef)).unwrap(); 34 | assert_eq!(use_.size(), 6); 35 | 36 | match use_.as_ref() { 37 | UnsizedSizedEnumRef::B(x, y) => { 38 | assert_eq!(*x, 0xab); 39 | assert_eq!(*y, 0xcdef); 40 | } 41 | _ => panic!(), 42 | } 43 | 44 | assert_eq!(mem[0], 1); 45 | assert_eq!(mem[2], 0xab); 46 | assert_eq!(&mem[4..], [0xef, 0xcd]); 47 | } 48 | 49 | #[test] 50 | fn init_c() { 51 | let mut mem = AlignedBytes::new(10, 2); 52 | let use_ = UnsizedSizedEnum::new_in_place( 53 | &mut mem, 54 | UnsizedSizedEnumInitC { 55 | a: 0xab, 56 | b: 0xcdef, 57 | c: [0x12, 0x34, 0x56, 0x78], 58 | }, 59 | ) 60 | .unwrap(); 61 | assert_eq!(use_.size(), 10); 62 | 63 | match use_.as_mut() { 64 | UnsizedSizedEnumMut::C { a, b, c } => { 65 | assert_eq!(*a, 0xab); 66 | assert_eq!(*b, 0xcdef); 67 | assert_eq!(*c, [0x12, 0x34, 0x56, 0x78]); 68 | } 69 | _ => panic!(), 70 | } 71 | 72 | assert_eq!(mem[0], 2); 73 | assert_eq!(mem[2], 0xab); 74 | assert_eq!(&mem[4..6], [0xef, 0xcd]); 75 | assert_eq!(&mem[6..], [0x12, 0x34, 0x56, 0x78]); 76 | } 77 | 78 | #[test] 79 | fn from_bytes_err() { 80 | let mut mem = AlignedBytes::new(1, 2); 81 | let res = UnsizedSizedEnum::from_mut_bytes(&mut mem); 82 | assert_eq!(res.err().unwrap().kind, ErrorKind::InsufficientSize); 83 | } 84 | 85 | #[test] 86 | fn init_err() { 87 | let mut mem = AlignedBytes::new(3, 2); 88 | let res = UnsizedSizedEnum::new_in_place(&mut mem, UnsizedSizedEnumInitB(0, 0)); 89 | assert_eq!(res.err().unwrap().kind, ErrorKind::InsufficientSize); 90 | } 91 | -------------------------------------------------------------------------------- /tests/src/unsized_struct/auto.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use flatty::{flat, FlatVec}; 3 | 4 | #[flat(sized = false, default = true)] 5 | #[derive(Debug, PartialEq, Eq)] 6 | pub struct UnsizedStruct { 7 | pub a: u8, 8 | b: u16, 9 | c: FlatVec, 10 | } 11 | 12 | generate_tests!(); 13 | -------------------------------------------------------------------------------- /tests/src/unsized_struct/manual.rs: -------------------------------------------------------------------------------- 1 | use super::tests::generate_tests; 2 | use core::mem::align_of; 3 | use flatty::{ 4 | prelude::*, 5 | utils::{ 6 | ceil_mul, 7 | iter::{self, prelude::*}, 8 | mem::{cast_wide_ptr_with_offset, offset_slice_ptr_start}, 9 | }, 10 | Emplacer, Error, FlatVec, 11 | }; 12 | 13 | #[derive(Debug, PartialEq, Eq)] 14 | #[repr(C)] 15 | struct UnsizedStruct { 16 | a: u8, 17 | b: u16, 18 | c: FlatVec, 19 | } 20 | 21 | #[repr(C)] 22 | struct AlignAs(u8, u16, as FlatUnsized>::AlignAs); 23 | 24 | struct UnsizedStructInit { 25 | a: A, 26 | b: B, 27 | c: C, 28 | } 29 | 30 | unsafe impl Emplacer for UnsizedStructInit 31 | where 32 | A: Emplacer, 33 | B: Emplacer, 34 | C: Emplacer>, 35 | { 36 | unsafe fn emplace_unchecked(self, bytes: &mut [u8]) -> Result<&mut UnsizedStruct, Error> { 37 | let iter = iter::BytesMutIter::new_unchecked(bytes, iter::type_list!(u8, u16, FlatVec)); 38 | let (iter, u_a) = iter.next(); 39 | self.a.emplace_unchecked(u_a)?; 40 | let (iter, u_b) = iter.next(); 41 | self.b.emplace_unchecked(u_b)?; 42 | let u_c = iter.finalize(); 43 | self.c.emplace_unchecked(u_c)?; 44 | Ok(unsafe { UnsizedStruct::from_mut_bytes_unchecked(bytes) }) 45 | } 46 | } 47 | 48 | impl UnsizedStruct { 49 | const LAST_FIELD_OFFSET: usize = ceil_mul(iter::fold_size!(0; u8, u16), FlatVec::::ALIGN); 50 | } 51 | 52 | unsafe impl FlatBase for UnsizedStruct { 53 | const ALIGN: usize = align_of::(); 54 | const MIN_SIZE: usize = ceil_mul(iter::fold_min_size!(0; u8, u16, FlatVec), Self::ALIGN); 55 | 56 | fn size(&self) -> usize { 57 | ceil_mul(Self::LAST_FIELD_OFFSET + self.c.size(), Self::ALIGN) 58 | } 59 | } 60 | 61 | unsafe impl FlatUnsized for UnsizedStruct { 62 | type AlignAs = AlignAs; 63 | 64 | unsafe fn ptr_from_bytes(bytes: *mut [u8]) -> *mut Self { 65 | cast_wide_ptr_with_offset!( 66 | Self, 67 | FlatVec::::ptr_from_bytes(offset_slice_ptr_start(bytes, Self::LAST_FIELD_OFFSET as isize)), 68 | -(Self::LAST_FIELD_OFFSET as isize), 69 | ) 70 | } 71 | unsafe fn ptr_to_bytes(this: *mut Self) -> *mut [u8] { 72 | offset_slice_ptr_start( 73 | FlatVec::::ptr_to_bytes(cast_wide_ptr_with_offset!( 74 | FlatVec::, 75 | this, 76 | Self::LAST_FIELD_OFFSET as isize 77 | )), 78 | -(Self::LAST_FIELD_OFFSET as isize), 79 | ) 80 | } 81 | } 82 | 83 | unsafe impl FlatValidate for UnsizedStruct { 84 | unsafe fn validate_unchecked(bytes: &[u8]) -> Result<(), Error> { 85 | unsafe { iter::BytesIter::new_unchecked(bytes, iter::type_list!(u8, u16, FlatVec)) }.validate_all() 86 | } 87 | } 88 | 89 | impl FlatDefault for UnsizedStruct { 90 | type DefaultEmplacer = UnsizedStructInit as FlatDefault>::DefaultEmplacer>; 91 | fn default_emplacer() -> Self::DefaultEmplacer { 92 | UnsizedStructInit { 93 | a: u8::default(), 94 | b: u16::default(), 95 | c: as FlatDefault>::default_emplacer(), 96 | } 97 | } 98 | } 99 | 100 | unsafe impl Flat for UnsizedStruct {} 101 | 102 | generate_tests!(); 103 | -------------------------------------------------------------------------------- /tests/src/unsized_struct/mod.rs: -------------------------------------------------------------------------------- 1 | mod auto; 2 | mod manual; 3 | mod tests; 4 | -------------------------------------------------------------------------------- /tests/src/unsized_struct/tests.rs: -------------------------------------------------------------------------------- 1 | macro_rules! generate_tests { 2 | () => { 3 | mod tests { 4 | use super::{UnsizedStruct, UnsizedStructInit}; 5 | use core::mem::{align_of_val, size_of_val}; 6 | use flatty::{flat_vec, prelude::*, AlignedBytes}; 7 | 8 | #[test] 9 | fn init() { 10 | let mut mem = AlignedBytes::new(16 + 4 * 8, 8); 11 | let us = UnsizedStruct::new_in_place( 12 | &mut mem, 13 | UnsizedStructInit { 14 | a: 200, 15 | b: 40000, 16 | c: flat_vec![0, 1], 17 | }, 18 | ) 19 | .unwrap(); 20 | 21 | assert_eq!(us.size(), 32); 22 | assert_eq!(us.a, 200); 23 | assert_eq!(us.b, 40000); 24 | assert_eq!(us.c.len(), 2); 25 | 26 | for i in 2.. { 27 | if us.c.push(i).is_err() { 28 | break; 29 | } 30 | } 31 | 32 | assert_eq!(us.size(), 48); 33 | assert_eq!(us.a, 200); 34 | assert_eq!(us.b, 40000); 35 | assert_eq!(us.c.len(), 4); 36 | assert_eq!(us.c.as_slice(), [0, 1, 2, 3]); 37 | } 38 | 39 | #[test] 40 | fn default() { 41 | let mut mem = AlignedBytes::new(16 + 4 * 8, 8); 42 | let us = UnsizedStruct::default_in_place(&mut mem).unwrap(); 43 | 44 | assert_eq!(us.size(), 16); 45 | assert_eq!(us.a, 0); 46 | assert_eq!(us.b, 0); 47 | assert_eq!(us.c.len(), 0); 48 | } 49 | 50 | #[test] 51 | fn layout() { 52 | let mut mem = AlignedBytes::new(16 + 4 * 8, 8); 53 | let us = UnsizedStruct::default_in_place(&mut mem).unwrap(); 54 | us.a = 0; 55 | us.b = 0; 56 | for i in 0.. { 57 | if us.c.push(i).is_err() { 58 | break; 59 | } 60 | } 61 | 62 | assert_eq!(align_of_val(us), ::ALIGN); 63 | assert_eq!(size_of_val(us), us.size()); 64 | assert_eq!(us.size(), mem.len()); 65 | } 66 | 67 | #[test] 68 | fn eq() { 69 | let mut mem_ab = AlignedBytes::new(16 + 4 * 8, 8); 70 | let mut mem_c = AlignedBytes::new(16 + 3 * 8, 8); 71 | { 72 | UnsizedStruct::new_in_place( 73 | &mut mem_ab, 74 | UnsizedStructInit { 75 | a: 1, 76 | b: 2, 77 | c: flat_vec![3, 4, 5, 6], 78 | }, 79 | ) 80 | .unwrap(); 81 | } 82 | let us_a = UnsizedStruct::from_bytes(&mem_ab).unwrap(); 83 | let us_b = UnsizedStruct::from_bytes(&mem_ab).unwrap(); 84 | let us_c = UnsizedStruct::new_in_place( 85 | &mut mem_c, 86 | UnsizedStructInit { 87 | a: 1, 88 | b: 2, 89 | c: flat_vec![3, 4, 5], 90 | }, 91 | ) 92 | .unwrap(); 93 | 94 | assert_eq!(us_a, us_b); 95 | assert_ne!(us_a, us_c); 96 | assert_ne!(us_b, us_c); 97 | } 98 | } 99 | }; 100 | } 101 | 102 | pub(crate) use generate_tests; 103 | --------------------------------------------------------------------------------