├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets └── logo-512.png ├── ci.sh ├── source ├── postcard-derive │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── max_size.rs │ │ └── schema.rs ├── postcard-dyn │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── de.rs │ │ ├── lib.rs │ │ └── ser.rs ├── postcard-schema │ ├── Cargo.toml │ ├── README.md │ ├── src │ │ ├── impls │ │ │ ├── builtins_alloc.rs │ │ │ ├── builtins_nostd.rs │ │ │ ├── builtins_std.rs │ │ │ ├── chrono_v0_4.rs │ │ │ ├── heapless_v0_7.rs │ │ │ ├── heapless_v0_8.rs │ │ │ ├── mod.rs │ │ │ ├── nalgebra_v0_33.rs │ │ │ └── uuid_v1_0.rs │ │ ├── key │ │ │ ├── hash.rs │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── schema │ │ │ ├── fmt.rs │ │ │ ├── mod.rs │ │ │ └── owned.rs │ └── tests │ │ └── schema.rs └── postcard │ ├── Cargo.toml │ ├── README.md │ ├── src │ ├── accumulator.rs │ ├── de │ │ ├── deserializer.rs │ │ ├── flavors.rs │ │ └── mod.rs │ ├── eio.rs │ ├── error.rs │ ├── fixint.rs │ ├── lib.rs │ ├── max_size.rs │ ├── ser │ │ ├── flavors.rs │ │ ├── mod.rs │ │ └── serializer.rs │ └── varint.rs │ └── tests │ ├── accumulator.rs │ ├── crc.rs │ ├── loopback.rs │ └── max_size.rs └── spec ├── .gitignore ├── LICENSE-CC-BY-SA ├── book.toml └── src ├── SUMMARY.md ├── glossary.md ├── intro.md ├── serde-data-model.md └── wire-format.md /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | 11 | # Cancel old workflows for PRs (only the most recent workflow can run). 12 | concurrency: 13 | group: ci-${{ github.ref }} 14 | cancel-in-progress: ${{ github.event_name == 'pull_request' }} 15 | 16 | # Avoid workflow-level permissions, instead use job-level permissions. 17 | permissions: {} 18 | 19 | jobs: 20 | ubuntu: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | - run: ./ci.sh 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .vscode -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Changes will be described here. 4 | 5 | ## 1.0.8 -> Unreleased 6 | 7 | * None yet! 8 | 9 | ## 1.0.7 -> 1.0.8 10 | 11 | * Add IO traits support by @xgroleau in https://github.com/jamesmunns/postcard/pull/91 12 | 13 | ## 1.0.6 -> 1.0.7 14 | 15 | * Fix off-by-one in varint size calculation by @Palladinium in https://github.com/jamesmunns/postcard/pull/111 16 | * Add specific error for Crc errors by @CBJamo in https://github.com/jamesmunns/postcard/pull/112 17 | 18 | ## 1.0.5 -> 1.0.6 19 | 20 | * Add cfg information to docs by @dtolnay in https://github.com/jamesmunns/postcard/pull/108 21 | 22 | ## 1.0.4 -> 1.0.5 23 | 24 | * improved documentation of the cobs decoding by @gutzchi in https://github.com/jamesmunns/postcard/pull/97 25 | * Fix strict provenance use of pointers by @jamesmunns in https://github.com/jamesmunns/postcard/pull/100 26 | * Introduces CRCs by @huntc in https://github.com/jamesmunns/postcard/pull/98 27 | * Format with rustfmt to eliminate trailing whitespaces by @dtolnay in https://github.com/jamesmunns/postcard/pull/104 28 | * Fix documentation of re-exports that have dedicated doc comment by @dtolnay in https://github.com/jamesmunns/postcard/pull/107 29 | 30 | ## New Contributors 31 | 32 | * @gutzchi made their first contribution in https://github.com/jamesmunns/postcard/pull/97 33 | 34 | **Full Changelog**: https://github.com/jamesmunns/postcard/compare/v1.0.4...v1.0.5 35 | 36 | ## 1.0.3 -> 1.0.4 37 | 38 | * fix cobs accumulator out-of-bounds index when data is 1 byte too long ([PR#90]) 39 | * Move cobs accumulator tests into a `cfg(test)` module 40 | 41 | [PR#90]: https://github.com/jamesmunns/postcard/pull/90 42 | 43 | ## 1.0.2 -> 1.0.3 44 | 45 | * PhantomData's T doesn't need MaxSize impl ([PR#87]) 46 | * Add function for computing the postcard serialized size of a value. ([PR#86]) 47 | * Fix typos & encoding example in wire doc ([PR#83]) 48 | * Optimize varint decode ([PR#62]) 49 | * Bump postcard-derive version ([PR#74]) 50 | * add std::string::String and alloc::string::String ([PR#76]) 51 | * Make fixints usable through serde field attributes instead of wrappers ([PR#69]) 52 | * Add support for 16-bit and 8-bit architectures ([PR#64]) 53 | * Add feed_ref to cobs_accumulator ([PR#70]) 54 | * Add a link to doc.rs documentation in README ([PR#72]) 55 | 56 | [PR#87]: https://github.com/jamesmunns/postcard/pull/87 57 | [PR#86]: https://github.com/jamesmunns/postcard/pull/86 58 | [PR#83]: https://github.com/jamesmunns/postcard/pull/83 59 | [PR#62]: https://github.com/jamesmunns/postcard/pull/62 60 | [PR#74]: https://github.com/jamesmunns/postcard/pull/74 61 | [PR#76]: https://github.com/jamesmunns/postcard/pull/76 62 | [PR#69]: https://github.com/jamesmunns/postcard/pull/69 63 | [PR#64]: https://github.com/jamesmunns/postcard/pull/64 64 | [PR#70]: https://github.com/jamesmunns/postcard/pull/70 65 | [PR#72]: https://github.com/jamesmunns/postcard/pull/72 66 | 67 | ## 1.0.1 -> 1.0.2 68 | 69 | * Correct exporting of experimental Schema proc macro ([PR#73]) 70 | 71 | [PR#73]: https://github.com/jamesmunns/postcard/pull/73 72 | 73 | ## 1.0.0 -> 1.0.1 74 | 75 | * [Fixed deserialization] of `i128`, which was using the "new style" varint serialization, but the incorrect, "old style" fixed deserialization. 76 | * This is considered a defect, and not a breaking change, as it brings the code back in line with the specification behavior. 77 | * Version 1.0.0 will be yanked due to this defect. 78 | 79 | [Fixed deserialization]: https://github.com/jamesmunns/postcard/commit/70ea33a1ac7f82632697f4578002267eaf9095f5 80 | 81 | ## 1.0.0-alpha.4 -> 1.0.0 82 | 83 | * Added experimental derive features 84 | * Made Flavor fields private again 85 | * Optimized varint encoding 86 | * Use crate `Result` for `Flavor`s 87 | 88 | ## 1.0.0-alpha.3 -> 1.0.0-alpha.4 89 | 90 | * Updated the signature of deserialization `Flavor` trait 91 | * Added documentation and tests 92 | * Removed the `Encoder` wrapper type to better match serialization and deserialization types 93 | * Renamed `ser_flavor::Flavor::release` to `finalize` for consistency 94 | * Re-organized some public items and modules 95 | * Made `Error` non-exhaustive 96 | * Added a `fixint` type to avoid varints 97 | 98 | ## 1.0.0-alpha.2 -> 1.0.0-alpha.3 99 | 100 | * Moved back to `cobs` from `postcard-cobs` 101 | * This fixed a number of upstream issues, including removal of panicking branches 102 | * Improved documentation and code examples 103 | * Corrected the behavior of `take_from_cobs` 104 | * Added support for serializing `Debug`/`Display` representation strings via serde's `collect_str` method (and removed the panic) 105 | 106 | ## 1.0.0-alpha.1 -> 1.0.0-alpha.2 107 | 108 | * Re-exposed fields of the Flavor constructors, made various flavors impl `Default` 109 | * No breaking changes vs `1.0.0-alpha.1`. 110 | 111 | ## 0.7.3 -> 1.0.0-alpha.1 112 | 113 | * WARNING: This includes a BREAKING wire change from postcard v0.x.y! Please ensure 114 | all devices using postcard are recompiled with the newest version! 115 | * added `#[inline]` to many functions, increasing performance 116 | * All unsigned integers u16-u128 are varint encoded 117 | * All signed integers i16-i128 are zigzag + varint encoded 118 | * Serialization flavors have been tweaked slightly, with the `Slice` flavor now faster 119 | * Introduction of Deserialization flavors 120 | * Please report any bugs upstream as we prepare for the v1.0.0 release! 121 | 122 | ## 0.7.2 -> 0.7.3 123 | 124 | * Added optional [`defmt`](https://crates.io/crates/defmt) support with the `use-defmt` feature. 125 | * Improved docs 126 | 127 | ## 0.7.1 -> 0.7.2 128 | 129 | * Changed the `CobsAccumulator::new()` into a const fn. 130 | 131 | ## 0.7.0 -> 0.7.1 132 | 133 | * Added the `CobsAccumulator` type for accumulating COBS encoded data for deserialization. 134 | 135 | ## 0.6.x -> 0.7.0 136 | 137 | * Updated `heapless` dependency to `v0.7.0`, which added support for const-generic sized buffers. 138 | 139 | ## Prior to 0.7.0 140 | 141 | * No changelog information added yet. 142 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = '2' 3 | members = [ 4 | "source/postcard", 5 | "source/postcard-derive", 6 | "source/postcard-dyn", 7 | "source/postcard-schema", 8 | ] 9 | -------------------------------------------------------------------------------- /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) 2019 Anthony James Munns 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 | # Postcard 2 | 3 | [![Documentation](https://docs.rs/postcard/badge.svg)](https://docs.rs/postcard) 4 | 5 | the postcard logo 6 | 7 | Postcard is a `#![no_std]` focused serializer and deserializer for Serde. 8 | 9 | Postcard aims to be convenient for developers in constrained environments, while 10 | allowing for flexibility to customize behavior as needed. 11 | 12 | ## Design Goals 13 | 14 | 1. Design primarily for `#![no_std]` usage, in embedded or other constrained contexts 15 | 2. Support a maximal set of `serde` features, so `postcard` can be used as a drop in replacement 16 | 3. Avoid special differences in code between communication code written for a microcontroller or a desktop/server PC 17 | 4. Be resource efficient - memory usage, code size, developer time, and CPU time; in that order 18 | 5. Allow library users to customize the serialization and deserialization behavior to fit their bespoke needs 19 | 20 | ## Format Stability 21 | 22 | As of v1.0.0, `postcard` has a documented and stable wire format. More information about this 23 | wire format can be found in the `spec/` folder of the Postcard repository, or viewed online 24 | at . 25 | 26 | Work towards the Postcard Specification and portions of the Postcard 1.0 Release 27 | were sponsored by Mozilla Corporation. 28 | 29 | ## Variable Length Data 30 | 31 | All signed and unsigned integers larger than eight bits are encoded using a [Varint]. 32 | This includes the length of array slices, as well as the discriminant of `enums`. 33 | 34 | For more information, see the [Varint] chapter of the wire specification. 35 | 36 | [Varint]: https://postcard.jamesmunns.com/wire-format.html#varint-encoded-integers 37 | 38 | ## Example - Serialization/Deserialization 39 | 40 | Postcard can serialize and deserialize messages similar to other `serde` formats. 41 | 42 | Using the default `heapless` feature to serialize to a `heapless::Vec`: 43 | 44 | ```rust 45 | use core::ops::Deref; 46 | use serde::{Serialize, Deserialize}; 47 | use postcard::{from_bytes, to_vec}; 48 | use heapless::Vec; 49 | 50 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 51 | struct RefStruct<'a> { 52 | bytes: &'a [u8], 53 | str_s: &'a str, 54 | } 55 | let message = "hElLo"; 56 | let bytes = [0x01, 0x10, 0x02, 0x20]; 57 | let output: Vec = to_vec(&RefStruct { 58 | bytes: &bytes, 59 | str_s: message, 60 | }).unwrap(); 61 | 62 | assert_eq!( 63 | &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], 64 | output.deref() 65 | ); 66 | 67 | let out: RefStruct = from_bytes(output.deref()).unwrap(); 68 | assert_eq!( 69 | out, 70 | RefStruct { 71 | bytes: &bytes, 72 | str_s: message, 73 | } 74 | ); 75 | ``` 76 | 77 | Or the optional `alloc` feature to serialize to an `alloc::vec::Vec`: 78 | 79 | ```rust 80 | use core::ops::Deref; 81 | use serde::{Serialize, Deserialize}; 82 | use postcard::{from_bytes, to_allocvec}; 83 | extern crate alloc; 84 | use alloc::vec::Vec; 85 | 86 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 87 | struct RefStruct<'a> { 88 | bytes: &'a [u8], 89 | str_s: &'a str, 90 | } 91 | let message = "hElLo"; 92 | let bytes = [0x01, 0x10, 0x02, 0x20]; 93 | let output: Vec = to_allocvec(&RefStruct { 94 | bytes: &bytes, 95 | str_s: message, 96 | }).unwrap(); 97 | 98 | assert_eq!( 99 | &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], 100 | output.deref() 101 | ); 102 | 103 | let out: RefStruct = from_bytes(output.deref()).unwrap(); 104 | assert_eq!( 105 | out, 106 | RefStruct { 107 | bytes: &bytes, 108 | str_s: message, 109 | } 110 | ); 111 | ``` 112 | 113 | ## Flavors 114 | 115 | `postcard` supports a system called `Flavors`, which are used to modify the way 116 | postcard serializes or processes serialized data. These flavors act as "plugins" or "middlewares" 117 | during the serialization or deserialization process, and can be combined to obtain complex protocol formats. 118 | 119 | See the documentation of the `ser_flavors` or `de_flavors` modules for more information on usage. 120 | 121 | ## Setup - `Cargo.toml` 122 | 123 | Don't forget to add [the `no-std` subset](https://serde.rs/no-std.html) of `serde` along with `postcard` to the `[dependencies]` section of your `Cargo.toml`! 124 | 125 | ```toml 126 | [dependencies] 127 | postcard = "1.0.0" 128 | 129 | # By default, `serde` has the `std` feature enabled, which makes it unsuitable for embedded targets 130 | # disabling default-features fixes this 131 | serde = { version = "1.0.*", default-features = false } 132 | ``` 133 | 134 | ## Unsupported serde attributes 135 | 136 | In the case `serde` attributes are used with `postcard`, the serialization and deserialization 137 | process should be tested extensively as multiple attributes can cause problems. 138 | 139 | > See the [tracking issue](https://github.com/jamesmunns/postcard/issues/125) 140 | 141 | Some serde attributes break serialization/deserialization like: 142 | 143 | - `serde(flatten)` 144 | - `serde(skip_serializing_if($COND))` 145 | 146 | Some attributes can cause silent issues: 147 | 148 | - `serde(skip)` Can break deserialization. If used for enum variants, make sure that 149 | the skipped fields are the last variants, otherwise `postcard` will attempt to deserialize 150 | based on an offsetted discriminant, causing failure, or silent mismatch on C-like enums. 151 | 152 | ## License 153 | 154 | Licensed under either of 155 | 156 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 157 | ) 158 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 159 | 160 | at your option. 161 | 162 | ### Contribution 163 | 164 | Unless you explicitly state otherwise, any contribution intentionally submitted 165 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 166 | dual licensed as above, without any additional terms or conditions. 167 | -------------------------------------------------------------------------------- /assets/logo-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamesmunns/postcard/c198b9733518a8d7f64fb142c47f98aa2d2194dc/assets/logo-512.png -------------------------------------------------------------------------------- /ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -ex 3 | 4 | has_target() { 5 | rustup target list --installed | grep -q "$1" 6 | } 7 | ensure_target() { 8 | has_target "$1" || rustup target add "$1" 9 | } 10 | 11 | ensure_target thumbv7em-none-eabi 12 | ensure_target riscv32i-unknown-none-elf 13 | 14 | has_toolchain() { 15 | rustup toolchain list | grep -q "$1" 16 | } 17 | ensure_toolchain() { 18 | has_toolchain "$1" || rustup toolchain install "$1" 19 | } 20 | 21 | ensure_toolchain nightly 22 | 23 | cargo_check() { 24 | cargo check --all "$@" 25 | cargo clippy --all "$@" -- --deny=warnings 26 | } 27 | cargo_test() { 28 | cargo_check --all-targets "$@" 29 | cargo test --all "$@" 30 | } 31 | 32 | cargo_test --features=alloc,experimental-derive,use-std,use-crc,derive,nalgebra-v0_33,heapless-v0_8 33 | 34 | # NOTE: we exclude postcard-dyn for these checks because it is std-only 35 | 36 | cargo_check --target=thumbv7em-none-eabi --no-default-features --exclude postcard-dyn 37 | cargo_check --target=thumbv7em-none-eabi --features=alloc,experimental-derive --exclude postcard-dyn 38 | 39 | # CC https://github.com/jamesmunns/postcard/issues/167 - don't accidentally use atomics 40 | # on non-atomic systems 41 | cargo_check --target=riscv32i-unknown-none-elf --features=alloc,experimental-derive --exclude postcard-dyn 42 | 43 | cargo fmt --all -- --check 44 | 45 | # Check docs.rs build 46 | # 47 | # TODO: We SHOULDN'T exclude postcard-dyn but it does weird things with feature unification and 48 | # makes the embedded-io stuff break 49 | env RUSTDOCFLAGS='--cfg=docsrs --deny=warnings' cargo +nightly doc --all --no-deps --all-features --exclude postcard-dyn 50 | -------------------------------------------------------------------------------- /source/postcard-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postcard-derive" 3 | version = "0.2.1" 4 | authors = [ 5 | "Lachlan Sneff ", 6 | "James Munns ", 7 | ] 8 | edition = "2021" 9 | readme = "../../README.md" 10 | repository = "https://github.com/jamesmunns/postcard" 11 | description = "A no_std + serde compatible message library for Rust - Derive Crate" 12 | license = "MIT OR Apache-2.0" 13 | categories = [ 14 | "embedded", 15 | "no-std", 16 | ] 17 | keywords = [ 18 | "serde", 19 | "cobs", 20 | "framing", 21 | ] 22 | documentation = "https://docs.rs/postcard-derive/" 23 | 24 | [lib] 25 | proc-macro = true 26 | 27 | [package.metadata.docs.rs] 28 | all-features = true 29 | 30 | [dependencies] 31 | syn = "2.0" 32 | quote = "1.0" 33 | proc-macro2 = "1.0" 34 | -------------------------------------------------------------------------------- /source/postcard-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use syn::{parse_macro_input, DeriveInput}; 2 | 3 | mod max_size; 4 | mod schema; 5 | 6 | /// Derive the `postcard::MaxSize` trait for a struct or enum. 7 | #[proc_macro_derive(MaxSize)] 8 | pub fn derive_max_size(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 9 | max_size::do_derive_max_size(item) 10 | } 11 | 12 | /// Derive the `postcard_schema::Schema` trait for a struct or enum. 13 | #[proc_macro_derive(Schema, attributes(postcard))] 14 | pub fn derive_schema(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 15 | let input = parse_macro_input!(item as DeriveInput); 16 | schema::do_derive_schema(input) 17 | .unwrap_or_else(syn::Error::into_compile_error) 18 | .into() 19 | } 20 | -------------------------------------------------------------------------------- /source/postcard-derive/src/max_size.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::{quote, quote_spanned}; 3 | use syn::{ 4 | parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Fields, GenericParam, 5 | Generics, 6 | }; 7 | 8 | pub fn do_derive_max_size(item: proc_macro::TokenStream) -> proc_macro::TokenStream { 9 | let input = parse_macro_input!(item as DeriveInput); 10 | 11 | let span = input.span(); 12 | let name = input.ident; 13 | 14 | // Add a bound `T: MaxSize` to every type parameter T. 15 | let generics = add_trait_bounds(input.generics); 16 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 17 | 18 | let sum = max_size_sum(&input.data, span).unwrap_or_else(syn::Error::into_compile_error); 19 | 20 | let expanded = quote! { 21 | impl #impl_generics ::postcard::experimental::max_size::MaxSize for #name #ty_generics #where_clause { 22 | const POSTCARD_MAX_SIZE: usize = #sum; 23 | } 24 | }; 25 | 26 | expanded.into() 27 | } 28 | 29 | /// Add a bound `T: MaxSize` to every type parameter T. 30 | fn add_trait_bounds(mut generics: Generics) -> Generics { 31 | for param in &mut generics.params { 32 | if let GenericParam::Type(ref mut type_param) = *param { 33 | type_param 34 | .bounds 35 | .push(parse_quote!(::postcard::experimental::max_size::MaxSize)); 36 | } 37 | } 38 | generics 39 | } 40 | 41 | /// Generate a constant expression that sums up the maximum size of the type. 42 | fn max_size_sum(data: &Data, span: Span) -> Result { 43 | match data { 44 | Data::Struct(data) => Ok(sum_fields(&data.fields)), 45 | Data::Enum(data) => { 46 | let variant_count = data.variants.len(); 47 | 48 | let recurse = data.variants.iter().map(|v| sum_fields(&v.fields)); 49 | 50 | let discriminant_size = varint_size_discriminant(variant_count as u32) as usize; 51 | 52 | // Generate a tree of max expressions. 53 | let max = recurse.fold(quote!(0), |acc, x| { 54 | quote! { 55 | { 56 | let lhs = #acc; 57 | let rhs = #x; 58 | if lhs > rhs { 59 | lhs 60 | } else { 61 | rhs 62 | } 63 | } 64 | } 65 | }); 66 | 67 | Ok(quote! { 68 | #discriminant_size + #max 69 | }) 70 | } 71 | Data::Union(_) => Err(syn::Error::new( 72 | span, 73 | "unions are not supported by `postcard::MaxSize`", 74 | )), 75 | } 76 | } 77 | 78 | fn sum_fields(fields: &Fields) -> TokenStream { 79 | match fields { 80 | syn::Fields::Named(fields) => { 81 | // Expands to an expression like 82 | // 83 | // 0 + ::POSTCARD_MAX_SIZE + ::POSTCARD_MAX_SIZE + ... 84 | // 85 | // but using fully qualified syntax. 86 | 87 | let recurse = fields.named.iter().map(|f| { 88 | let ty = &f.ty; 89 | quote_spanned! { f.span() => <#ty as ::postcard::experimental::max_size::MaxSize>::POSTCARD_MAX_SIZE } 90 | }); 91 | 92 | quote! { 93 | 0 #(+ #recurse)* 94 | } 95 | } 96 | syn::Fields::Unnamed(fields) => { 97 | let recurse = fields.unnamed.iter().map(|f| { 98 | let ty = &f.ty; 99 | quote_spanned! { f.span() => <#ty as ::postcard::experimental::max_size::MaxSize>::POSTCARD_MAX_SIZE } 100 | }); 101 | 102 | quote! { 103 | 0 #(+ #recurse)* 104 | } 105 | } 106 | syn::Fields::Unit => quote!(0), 107 | } 108 | } 109 | 110 | fn varint_size_discriminant(max_n: u32) -> u32 { 111 | const BITS_PER_BYTE: u32 = 8; 112 | const BITS_PER_VARINT_BYTE: u32 = 7; 113 | 114 | // How many data bits do we need for `max_n`. 115 | let bits = core::mem::size_of::() as u32 * BITS_PER_BYTE - max_n.leading_zeros(); 116 | 117 | let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1); 118 | 119 | // Apply division, using normal "round down" integer division 120 | roundup_bits / BITS_PER_VARINT_BYTE 121 | } 122 | -------------------------------------------------------------------------------- /source/postcard-derive/src/schema.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::{quote, quote_spanned}; 3 | use syn::{ 4 | parse_quote, punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, Fields, GenericParam, 5 | Generics, Path, Token, 6 | }; 7 | 8 | pub fn do_derive_schema(input: DeriveInput) -> syn::Result { 9 | let span = input.span(); 10 | let name = &input.ident; 11 | 12 | let mut generator = Generator::new(&input)?; 13 | 14 | // Add a bound `T: Schema` to every type parameter T unless overridden by `#[postcard(bound = "...")]` 15 | let generics = match generator.bound.take() { 16 | Some(bounds) => { 17 | let mut generics = input.generics; 18 | generics.make_where_clause().predicates.extend(bounds); 19 | generics 20 | } 21 | None => generator.add_trait_bounds(input.generics), 22 | }; 23 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 24 | 25 | let ty = generator.generate_type(&input.data, span, name.to_string())?; 26 | 27 | let postcard_schema = &generator.postcard_schema; 28 | let expanded = quote! { 29 | impl #impl_generics #postcard_schema::Schema for #name #ty_generics #where_clause { 30 | const SCHEMA: &'static #postcard_schema::schema::DataModelType = #ty; 31 | } 32 | }; 33 | 34 | Ok(expanded) 35 | } 36 | 37 | struct Generator { 38 | postcard_schema: Path, 39 | bound: Option>, 40 | } 41 | 42 | impl Generator { 43 | fn new(input: &DeriveInput) -> syn::Result { 44 | let mut generator = Self { 45 | postcard_schema: parse_quote!(::postcard_schema), 46 | bound: None, 47 | }; 48 | for attr in &input.attrs { 49 | if attr.path().is_ident("postcard") { 50 | attr.parse_nested_meta(|meta| { 51 | // #[postcard(crate = path::to::postcard)] 52 | if meta.path.is_ident("crate") { 53 | generator.postcard_schema = meta.value()?.parse()?; 54 | return Ok(()); 55 | } 56 | 57 | // #[postcard(bound = "T: Schema")] 58 | if meta.path.is_ident("bound") { 59 | let bound = meta.value()?.parse::()?; 60 | let bound = bound.parse_with( 61 | Punctuated::::parse_terminated, 62 | )?; 63 | if generator.bound.is_some() { 64 | return Err(meta.error("duplicate #[postcard(bound = \"...\")]")); 65 | } 66 | generator.bound = Some(bound); 67 | return Ok(()); 68 | } 69 | 70 | Err(meta.error("unsupported #[postcard] attribute")) 71 | })?; 72 | } 73 | } 74 | Ok(generator) 75 | } 76 | 77 | fn generate_type( 78 | &self, 79 | data: &Data, 80 | span: Span, 81 | name: String, 82 | ) -> Result { 83 | let postcard_schema = &self.postcard_schema; 84 | match data { 85 | Data::Struct(data) => { 86 | let data = self.generate_struct(&data.fields); 87 | Ok(quote! { 88 | &#postcard_schema::schema::DataModelType::Struct{ 89 | name: #name, 90 | data: #data, 91 | } 92 | }) 93 | } 94 | Data::Enum(data) => { 95 | let variants = data.variants.iter().map(|v| { 96 | let (name, data) = (v.ident.to_string(), self.generate_variants(&v.fields)); 97 | quote! { #postcard_schema::schema::Variant { name: #name, data: #data } } 98 | }); 99 | 100 | Ok(quote! { 101 | &#postcard_schema::schema::DataModelType::Enum { 102 | name: #name, 103 | variants: &[#(&#variants),*], 104 | } 105 | }) 106 | } 107 | Data::Union(_) => Err(syn::Error::new( 108 | span, 109 | "#[derive(Schema)] does not support unions", 110 | )), 111 | } 112 | } 113 | 114 | fn generate_struct(&self, fields: &Fields) -> TokenStream { 115 | let postcard_schema = &self.postcard_schema; 116 | match fields { 117 | syn::Fields::Named(fields) => { 118 | let fields = fields.named.iter().map(|f| { 119 | let ty = &f.ty; 120 | let name = f.ident.as_ref().unwrap().to_string(); 121 | quote_spanned!(f.span() => &#postcard_schema::schema::NamedField { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA }) 122 | }); 123 | quote! { #postcard_schema::schema::Data::Struct(&[ 124 | #( #fields ),* 125 | ]) } 126 | } 127 | syn::Fields::Unnamed(fields) => { 128 | if fields.unnamed.len() == 1 { 129 | let f = fields.unnamed[0].clone(); 130 | let ty = &f.ty; 131 | let qs = quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA); 132 | 133 | quote! { #postcard_schema::schema::Data::Newtype(#qs) } 134 | } else { 135 | let fields = fields.unnamed.iter().map(|f| { 136 | let ty = &f.ty; 137 | quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA) 138 | }); 139 | quote! { #postcard_schema::schema::Data::Tuple(&[ 140 | #( #fields ),* 141 | ]) } 142 | } 143 | } 144 | syn::Fields::Unit => { 145 | quote! { #postcard_schema::schema::Data::Unit } 146 | } 147 | } 148 | } 149 | 150 | fn generate_variants(&self, fields: &Fields) -> TokenStream { 151 | let postcard_schema = &self.postcard_schema; 152 | match fields { 153 | syn::Fields::Named(fields) => { 154 | let fields = fields.named.iter().map(|f| { 155 | let ty = &f.ty; 156 | let name = f.ident.as_ref().unwrap().to_string(); 157 | quote_spanned!(f.span() => &#postcard_schema::schema::NamedField { name: #name, ty: <#ty as #postcard_schema::Schema>::SCHEMA }) 158 | }); 159 | quote! { #postcard_schema::schema::Data::Struct(&[ 160 | #( #fields ),* 161 | ]) } 162 | } 163 | syn::Fields::Unnamed(fields) => { 164 | if fields.unnamed.len() == 1 { 165 | let f = fields.unnamed[0].clone(); 166 | let ty = &f.ty; 167 | let qs = quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA); 168 | 169 | quote! { #postcard_schema::schema::Data::Newtype(#qs) } 170 | } else { 171 | let fields = fields.unnamed.iter().map(|f| { 172 | let ty = &f.ty; 173 | quote_spanned!(f.span() => <#ty as #postcard_schema::Schema>::SCHEMA) 174 | }); 175 | quote! { #postcard_schema::schema::Data::Tuple(&[ 176 | #( #fields ),* 177 | ]) } 178 | } 179 | } 180 | syn::Fields::Unit => { 181 | quote! { #postcard_schema::schema::Data::Unit } 182 | } 183 | } 184 | } 185 | 186 | /// Add a bound `T: Schema` to every type parameter T. 187 | fn add_trait_bounds(&self, mut generics: Generics) -> Generics { 188 | let postcard_schema = &self.postcard_schema; 189 | for param in &mut generics.params { 190 | if let GenericParam::Type(ref mut type_param) = *param { 191 | type_param 192 | .bounds 193 | .push(parse_quote!(#postcard_schema::Schema)); 194 | } 195 | } 196 | generics 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /source/postcard-dyn/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /source/postcard-dyn/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postcard-dyn" 3 | version = "0.2.0" 4 | authors = ["James Munns "] 5 | edition = "2021" 6 | repository = "https://github.com/jamesmunns/postcard" 7 | description = "Dynamic ser/de for postcard" 8 | license = "MIT OR Apache-2.0" 9 | categories = [ 10 | "embedded", 11 | "no-std", 12 | ] 13 | keywords = [ 14 | "serde", 15 | "cobs", 16 | "framing", 17 | ] 18 | documentation = "https://docs.rs/postcard-dyn/" 19 | 20 | 21 | [dependencies] 22 | serde = { version = "1.0.202", features = ["derive"] } 23 | serde_json = "1.0.117" 24 | 25 | [dependencies.postcard] 26 | version = "1.0.10" 27 | features = ["use-std"] 28 | path = "../postcard" 29 | 30 | [dependencies.postcard-schema] 31 | version = "0.2" 32 | features = ["use-std", "derive"] 33 | path = "../postcard-schema" 34 | -------------------------------------------------------------------------------- /source/postcard-dyn/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod de; 2 | mod ser; 3 | 4 | pub use de::from_slice_dyn; 5 | pub use ser::to_stdvec_dyn; 6 | pub use serde_json::Value; 7 | -------------------------------------------------------------------------------- /source/postcard-schema/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postcard-schema" 3 | version = "0.2.1" 4 | authors = ["James Munns "] 5 | edition = "2021" 6 | readme = "README.md" 7 | repository = "https://github.com/jamesmunns/postcard" 8 | description = "Reflection-like schemas for postcard types" 9 | license = "MIT OR Apache-2.0" 10 | categories = [ 11 | "embedded", 12 | "no-std", 13 | ] 14 | keywords = [ 15 | "serde", 16 | ] 17 | documentation = "https://docs.rs/postcard-schema/" 18 | 19 | [package.metadata.docs.rs] 20 | all-features = true 21 | rustdoc-args = ["--cfg", "docsrs"] 22 | 23 | [dependencies] 24 | 25 | [dependencies.serde] 26 | version = "1.0" 27 | default-features = false 28 | # todo: remove this? 29 | features = ["derive"] 30 | 31 | [dependencies.uuid_v1_0] 32 | package = "uuid" 33 | version = "1.0" 34 | default-features = false 35 | optional = true 36 | 37 | [dependencies.chrono_v0_4] 38 | package = "chrono" 39 | version = "0.4" 40 | default-features = false 41 | optional = true 42 | 43 | [dependencies.heapless_v0_7] 44 | package = "heapless" 45 | version = "0.7" 46 | default-features = false 47 | optional = true 48 | 49 | [dependencies.heapless_v0_8] 50 | package = "heapless" 51 | version = "0.8" 52 | default-features = false 53 | optional = true 54 | 55 | [dependencies.nalgebra_v0_33] 56 | package = "nalgebra" 57 | version = "0.33.0" 58 | optional = true 59 | default-features = false 60 | 61 | [dependencies.postcard-derive] 62 | path = "../postcard-derive" 63 | version = "0.2.0" 64 | optional = true 65 | 66 | [dependencies.defmt_v0_3] 67 | package = "defmt" 68 | version = "0.3.5" 69 | optional = true 70 | 71 | [dev-dependencies.postcard] 72 | path = "../postcard" 73 | version = "1.0" 74 | features = ["use-std"] 75 | 76 | [dev-dependencies.nalgebra_v0_33] 77 | package = "nalgebra" 78 | version = "0.33.0" 79 | default-features = false 80 | features = ["serde-serialize-no-std"] 81 | 82 | [features] 83 | default = [] 84 | use-std = ["serde/std"] 85 | alloc = ["serde/alloc"] 86 | derive = ["postcard-derive"] 87 | 88 | defmt-v0_3 = ["defmt_v0_3"] 89 | uuid-v1_0 = ["uuid_v1_0"] 90 | chrono-v0_4 = ["chrono_v0_4"] 91 | heapless-v0_7 = ["heapless_v0_7"] 92 | heapless-v0_8 = ["heapless_v0_8"] 93 | nalgebra-v0_33 = ["nalgebra_v0_33"] 94 | -------------------------------------------------------------------------------- /source/postcard-schema/README.md: -------------------------------------------------------------------------------- 1 | # Postcard Schema 2 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/builtins_alloc.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for `alloc` types 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | extern crate alloc; 6 | 7 | impl Schema for alloc::vec::Vec { 8 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA); 9 | } 10 | 11 | impl Schema for alloc::string::String { 12 | const SCHEMA: &'static DataModelType = &DataModelType::String; 13 | } 14 | 15 | impl Schema for alloc::collections::BTreeMap { 16 | const SCHEMA: &'static DataModelType = &DataModelType::Map { 17 | key: K::SCHEMA, 18 | val: V::SCHEMA, 19 | }; 20 | } 21 | 22 | impl Schema for alloc::collections::BTreeSet { 23 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA); 24 | } 25 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/builtins_nostd.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for `core` types 2 | //! 3 | //! These implementations are always available 4 | 5 | use crate::{ 6 | schema::{Data, DataModelType, NamedField, Variant}, 7 | Schema, 8 | }; 9 | use core::{ 10 | num::{ 11 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, 12 | NonZeroU32, NonZeroU64, NonZeroU8, 13 | }, 14 | ops::{Range, RangeFrom, RangeInclusive, RangeTo}, 15 | }; 16 | 17 | macro_rules! impl_schema { 18 | ($($t:ty: $sdm:expr),*) => { 19 | $( 20 | impl Schema for $t { 21 | const SCHEMA: &'static DataModelType = &$sdm; 22 | } 23 | )* 24 | }; 25 | (tuple => [$(($($generic:ident),*)),*]) => { 26 | $( 27 | impl<$($generic: Schema),*> Schema for ($($generic,)*) { 28 | const SCHEMA: &'static DataModelType = &DataModelType::Tuple(&[$($generic::SCHEMA),*]); 29 | } 30 | )* 31 | }; 32 | } 33 | 34 | impl_schema![ 35 | u8: DataModelType::U8, 36 | NonZeroU8: DataModelType::U8, 37 | i8: DataModelType::I8, 38 | NonZeroI8: DataModelType::I8, 39 | bool: DataModelType::Bool, 40 | f32: DataModelType::F32, 41 | f64: DataModelType::F64, 42 | char: DataModelType::Char, 43 | str: DataModelType::String, 44 | (): DataModelType::Unit, 45 | i16: DataModelType::I16, i32: DataModelType::I32, i64: DataModelType::I64, i128: DataModelType::I128, 46 | u16: DataModelType::U16, u32: DataModelType::U32, u64: DataModelType::U64, u128: DataModelType::U128, 47 | NonZeroI16: DataModelType::I16, NonZeroI32: DataModelType::I32, 48 | NonZeroI64: DataModelType::I64, NonZeroI128: DataModelType::I128, 49 | NonZeroU16: DataModelType::U16, NonZeroU32: DataModelType::U32, 50 | NonZeroU64: DataModelType::U64, NonZeroU128: DataModelType::U128 51 | ]; 52 | 53 | impl_schema!(tuple => [ 54 | (A), 55 | (A, B), 56 | (A, B, C), 57 | (A, B, C, D), 58 | (A, B, C, D, E), 59 | (A, B, C, D, E, F) 60 | ]); 61 | 62 | impl Schema for Option { 63 | const SCHEMA: &'static DataModelType = &DataModelType::Option(T::SCHEMA); 64 | } 65 | 66 | impl Schema for Result { 67 | const SCHEMA: &'static DataModelType = &DataModelType::Enum { 68 | name: "Result", 69 | variants: &[ 70 | &Variant { 71 | name: "Ok", 72 | data: Data::Newtype(T::SCHEMA), 73 | }, 74 | &Variant { 75 | name: "Err", 76 | data: Data::Newtype(E::SCHEMA), 77 | }, 78 | ], 79 | }; 80 | } 81 | 82 | impl Schema for &'_ T { 83 | const SCHEMA: &'static DataModelType = T::SCHEMA; 84 | } 85 | 86 | impl Schema for [T] { 87 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA); 88 | } 89 | 90 | impl Schema for [T; N] { 91 | const SCHEMA: &'static DataModelType = &DataModelType::Tuple(&[T::SCHEMA; N]); 92 | } 93 | 94 | impl Schema for Range { 95 | const SCHEMA: &'static DataModelType = &DataModelType::Struct { 96 | name: "Range", 97 | data: Data::Struct(&[ 98 | &NamedField { 99 | name: "start", 100 | ty: T::SCHEMA, 101 | }, 102 | &NamedField { 103 | name: "end", 104 | ty: T::SCHEMA, 105 | }, 106 | ]), 107 | }; 108 | } 109 | 110 | impl Schema for RangeInclusive { 111 | const SCHEMA: &'static DataModelType = &DataModelType::Struct { 112 | name: "RangeInclusive", 113 | data: Data::Struct(&[ 114 | &NamedField { 115 | name: "start", 116 | ty: T::SCHEMA, 117 | }, 118 | &NamedField { 119 | name: "end", 120 | ty: T::SCHEMA, 121 | }, 122 | ]), 123 | }; 124 | } 125 | 126 | impl Schema for RangeFrom { 127 | const SCHEMA: &'static DataModelType = &DataModelType::Struct { 128 | name: "RangeFrom", 129 | data: Data::Struct(&[&NamedField { 130 | name: "start", 131 | ty: T::SCHEMA, 132 | }]), 133 | }; 134 | } 135 | 136 | impl Schema for RangeTo { 137 | const SCHEMA: &'static DataModelType = &DataModelType::Struct { 138 | name: "RangeTo", 139 | data: Data::Struct(&[&NamedField { 140 | name: "end", 141 | ty: T::SCHEMA, 142 | }]), 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/builtins_std.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for `std` types 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 6 | impl Schema for std::vec::Vec { 7 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA); 8 | } 9 | 10 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 11 | impl Schema for std::string::String { 12 | const SCHEMA: &'static DataModelType = &DataModelType::String; 13 | } 14 | 15 | #[cfg_attr(docsrs, doc(cfg(feature = "use-std")))] 16 | impl Schema for std::path::PathBuf { 17 | const SCHEMA: &'static DataModelType = &DataModelType::String; 18 | } 19 | 20 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 21 | impl Schema for std::collections::HashMap { 22 | const SCHEMA: &'static DataModelType = &DataModelType::Map { 23 | key: K::SCHEMA, 24 | val: V::SCHEMA, 25 | }; 26 | } 27 | 28 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 29 | impl Schema for std::collections::BTreeMap { 30 | const SCHEMA: &'static DataModelType = &DataModelType::Map { 31 | key: K::SCHEMA, 32 | val: V::SCHEMA, 33 | }; 34 | } 35 | 36 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 37 | impl Schema for std::collections::HashSet { 38 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA); 39 | } 40 | 41 | #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "use-std"))))] 42 | impl Schema for std::collections::BTreeSet { 43 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(K::SCHEMA); 44 | } 45 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/chrono_v0_4.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for the `chrono` crate v0.4 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(feature = "chrono-v0_4")))] 6 | impl Schema for chrono_v0_4::DateTime { 7 | const SCHEMA: &'static DataModelType = &DataModelType::String; 8 | } 9 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/heapless_v0_7.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for the `heapless` crate v0.7 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_7")))] 6 | impl Schema for heapless_v0_7::Vec { 7 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA); 8 | } 9 | 10 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_7")))] 11 | impl Schema for heapless_v0_7::String { 12 | const SCHEMA: &'static DataModelType = &DataModelType::String; 13 | } 14 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/heapless_v0_8.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for the `heapless` crate v0.8 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_8")))] 6 | impl Schema for heapless_v0_8::Vec { 7 | const SCHEMA: &'static DataModelType = &DataModelType::Seq(T::SCHEMA); 8 | } 9 | 10 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_8")))] 11 | impl Schema for heapless_v0_8::String { 12 | const SCHEMA: &'static DataModelType = &DataModelType::String; 13 | } 14 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/mod.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for foreign crates 2 | //! 3 | //! Each module requires the matching feature flag to be enabled. 4 | 5 | use crate::{schema::DataModelType, Schema}; 6 | 7 | pub mod builtins_nostd; 8 | 9 | #[cfg(all(not(feature = "use-std"), feature = "alloc"))] 10 | #[cfg_attr(docsrs, doc(cfg(all(not(feature = "use-std"), feature = "alloc"))))] 11 | pub mod builtins_alloc; 12 | 13 | #[cfg(feature = "use-std")] 14 | #[cfg_attr(docsrs, doc(cfg(feature = "use-std")))] 15 | pub mod builtins_std; 16 | 17 | #[cfg(feature = "chrono-v0_4")] 18 | #[cfg_attr(docsrs, doc(cfg(feature = "chrono-v0_4")))] 19 | pub mod chrono_v0_4; 20 | 21 | #[cfg(feature = "uuid-v1_0")] 22 | #[cfg_attr(docsrs, doc(cfg(feature = "uuid-v1_0")))] 23 | pub mod uuid_v1_0; 24 | 25 | #[cfg(feature = "heapless-v0_7")] 26 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_7")))] 27 | pub mod heapless_v0_7; 28 | 29 | #[cfg(feature = "heapless-v0_8")] 30 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless-v0_8")))] 31 | pub mod heapless_v0_8; 32 | 33 | #[cfg(feature = "nalgebra-v0_33")] 34 | #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))] 35 | pub mod nalgebra_v0_33; 36 | 37 | impl Schema for DataModelType { 38 | const SCHEMA: &'static DataModelType = &DataModelType::Schema; 39 | } 40 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/nalgebra_v0_33.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for the `nalgebra` crate v0.33 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))] 6 | impl Schema 7 | for nalgebra_v0_33::Matrix< 8 | T, 9 | nalgebra_v0_33::Const, 10 | nalgebra_v0_33::Const, 11 | nalgebra_v0_33::ArrayStorage, 12 | > 13 | where 14 | T: Schema + nalgebra_v0_33::Scalar, 15 | { 16 | const SCHEMA: &'static DataModelType = &DataModelType::Tuple(flatten(&[[T::SCHEMA; R]; C])); 17 | } 18 | 19 | /// Const version of the const-unstable [`<[[T; N]]>::as_flattened()`] 20 | const fn flatten(slice: &[[T; N]]) -> &[T] { 21 | const { 22 | assert!(size_of::() != 0); 23 | } 24 | // SAFETY: `self.len() * N` cannot overflow because `self` is 25 | // already in the address space. 26 | let len = unsafe { slice.len().unchecked_mul(N) }; 27 | // SAFETY: `[T]` is layout-identical to `[T; N]` 28 | unsafe { core::slice::from_raw_parts(slice.as_ptr().cast(), len) } 29 | } 30 | 31 | #[test] 32 | fn flattened() { 33 | type T = nalgebra_v0_33::SMatrix; 34 | assert_eq!(T::SCHEMA, <[u8; 9]>::SCHEMA); 35 | } 36 | 37 | #[test] 38 | fn smoke() { 39 | let x = nalgebra_v0_33::SMatrix::::new(1, 2, 3, 4, 5, 6, 7, 8, 9); 40 | let y = postcard::to_stdvec(&x).unwrap(); 41 | assert_eq!(&[1, 4, 7, 2, 5, 8, 3, 6, 9], y.as_slice()); 42 | } 43 | -------------------------------------------------------------------------------- /source/postcard-schema/src/impls/uuid_v1_0.rs: -------------------------------------------------------------------------------- 1 | //! Implementations of the [`Schema`] trait for the `uuid` crate v1.0 2 | 3 | use crate::{schema::DataModelType, Schema}; 4 | 5 | impl Schema for uuid_v1_0::Uuid { 6 | const SCHEMA: &'static DataModelType = &DataModelType::ByteArray; 7 | } 8 | -------------------------------------------------------------------------------- /source/postcard-schema/src/key/mod.rs: -------------------------------------------------------------------------------- 1 | //! A hash-based "Key" tag for postcard-schema compatible types 2 | //! 3 | //! This originally was developed for use in postcard-rpc, but has more 4 | //! general use as a general purpose type + usage tag. 5 | //! 6 | //! This key should NOT be relied on for memory safety purposes, validation 7 | //! of the data should still be performed, like that done by serde. It should 8 | //! be treated as misuse **resistant**, not misuse **proof**. It is possible 9 | //! for there to be hash collisions, and as a non-cryptograpic hash, it is 10 | //! likely trivial to intentionally cause a collision. 11 | 12 | use serde::{Deserialize, Serialize}; 13 | 14 | use crate::{schema::DataModelType, Schema}; 15 | 16 | pub mod hash; 17 | 18 | // TODO: https://github.com/knurling-rs/defmt/issues/928 19 | #[cfg(feature = "defmt-v0_3")] 20 | use defmt_v0_3 as defmt; 21 | 22 | /// The `Key` uniquely identifies what "kind" of message this is. 23 | /// 24 | /// In order to generate it, `postcard-schema` takes two pieces of data: 25 | /// 26 | /// * a `&str` "path" URI, similar to how you would use URIs as part of an HTTP path 27 | /// * The schema of the message type itself, using the [`Schema`] trait 28 | /// 29 | /// [`Schema`]: crate::Schema 30 | /// 31 | /// Specifically, we use [`Fnv1a`](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function), 32 | /// and produce a 64-bit digest, by first hashing the path, then hashing the 33 | /// schema. Fnv1a is a non-cryptographic hash function, designed to be reasonably 34 | /// efficient to compute even on small platforms like microcontrollers. 35 | /// 36 | /// Changing **anything** about *either* of the path or the schema will produce 37 | /// a drastically different `Key` value. 38 | #[cfg_attr(feature = "defmt-v0_3", derive(defmt_v0_3::Format))] 39 | #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Serialize, Deserialize, Hash)] 40 | pub struct Key([u8; 8]); 41 | 42 | impl Schema for Key { 43 | const SCHEMA: &'static crate::schema::DataModelType = &DataModelType::Struct { 44 | name: "Key", 45 | data: crate::schema::Data::Newtype(<[u8; 8] as Schema>::SCHEMA), 46 | }; 47 | } 48 | 49 | impl core::fmt::Debug for Key { 50 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 51 | f.write_str("Key(")?; 52 | for b in self.0.iter() { 53 | f.write_fmt(format_args!("{} ", b))?; 54 | } 55 | f.write_str(")") 56 | } 57 | } 58 | 59 | impl Key { 60 | /// Create a Key for the given type and path 61 | pub const fn for_path(path: &str) -> Self 62 | where 63 | T: Schema + ?Sized, 64 | { 65 | Key(hash::fnv1a64::hash_ty_path::(path)) 66 | } 67 | 68 | /// Unsafely create a key from a given 8-byte value 69 | /// 70 | /// ## Safety 71 | /// 72 | /// This MUST only be used with pre-calculated values. Incorrectly 73 | /// created keys could lead to the improper deserialization of 74 | /// messages. 75 | pub const unsafe fn from_bytes(bytes: [u8; 8]) -> Self { 76 | Self(bytes) 77 | } 78 | 79 | /// Extract the bytes making up this key 80 | pub const fn to_bytes(&self) -> [u8; 8] { 81 | self.0 82 | } 83 | 84 | /// Compare 2 keys in const context. 85 | pub const fn const_cmp(&self, other: &Self) -> bool { 86 | let mut i = 0; 87 | while i < self.0.len() { 88 | if self.0[i] != other.0[i] { 89 | return false; 90 | } 91 | 92 | i += 1; 93 | } 94 | 95 | true 96 | } 97 | } 98 | 99 | #[cfg(feature = "use-std")] 100 | mod key_owned { 101 | use super::*; 102 | use crate::schema::owned::OwnedDataModelType; 103 | impl Key { 104 | /// Calculate the Key for the given path and [`OwnedDataModelType`] 105 | pub fn for_owned_schema_path(path: &str, nt: &OwnedDataModelType) -> Key { 106 | Key(hash::fnv1a64_owned::hash_ty_path_owned(path, nt)) 107 | } 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod test { 113 | use crate::{key::Key, schema::DataModelType, Schema}; 114 | 115 | #[test] 116 | fn matches_old_postcard_rpc_defn() { 117 | let old = &DataModelType::Struct { 118 | name: "Key", 119 | data: crate::schema::Data::Newtype(&DataModelType::Tuple(&[ 120 | &DataModelType::U8, 121 | &DataModelType::U8, 122 | &DataModelType::U8, 123 | &DataModelType::U8, 124 | &DataModelType::U8, 125 | &DataModelType::U8, 126 | &DataModelType::U8, 127 | &DataModelType::U8, 128 | ])), 129 | }; 130 | 131 | let new = ::SCHEMA; 132 | 133 | assert_eq!(old, new); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /source/postcard-schema/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(any(test, feature = "use-std")), no_std)] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(docsrs, feature(doc_cfg))] 4 | //! # Postcard Schema 5 | 6 | pub mod impls; 7 | pub mod key; 8 | pub mod schema; 9 | 10 | /// Derive [`Schema`] for a struct or enum 11 | /// 12 | /// # Examples 13 | /// 14 | /// ``` 15 | /// use postcard_schema::Schema; 16 | /// 17 | /// #[derive(Schema)] 18 | /// struct Point { 19 | /// x: i32, 20 | /// y: i32, 21 | /// } 22 | /// ``` 23 | /// 24 | /// # Attributes 25 | /// 26 | /// ## `#[postcard(crate = ...)]` 27 | /// 28 | /// The `#[postcard(crate = ...)]` attribute can be used to specify a path to the `postcard_schema` 29 | /// crate instance to use when referring to [`Schema`] and [schema types](schema) from generated 30 | /// code. This is normally only applicable when invoking re-exported derives from a different crate. 31 | /// 32 | /// ``` 33 | /// # use postcard_schema::Schema; 34 | /// use postcard_schema as reexported_postcard_schema; 35 | /// 36 | /// #[derive(Schema)] 37 | /// #[postcard(crate = reexported_postcard_schema)] 38 | /// struct Point { 39 | /// x: i32, 40 | /// y: i32, 41 | /// } 42 | /// ``` 43 | /// 44 | /// ## `#[postcard(bound = ...)]` 45 | /// 46 | /// The `#[postcard(bound = ...)]` attribute can be used to overwrite the default bounds when 47 | /// deriving [`Schema`]. The default bounds are `T: Schema` for each type parameter `T`. 48 | /// 49 | /// ``` 50 | /// # use postcard_schema::Schema; 51 | /// #[derive(Schema)] 52 | /// #[postcard(bound = "")] 53 | /// struct Foo(F::Wrap); 54 | /// 55 | /// trait Bar { 56 | /// type Wrap: Schema; 57 | /// } 58 | /// 59 | /// struct NoSchema; 60 | /// impl Bar for NoSchema { 61 | /// type Wrap = Option; 62 | /// } 63 | /// 64 | /// Foo::::SCHEMA; 65 | /// ``` 66 | #[cfg(feature = "derive")] 67 | pub use postcard_derive::Schema; 68 | 69 | /// A trait that represents a compile time calculated schema 70 | pub trait Schema { 71 | /// A recursive data structure that describes the schema of the given 72 | /// type. 73 | const SCHEMA: &'static schema::DataModelType; 74 | } 75 | 76 | #[cfg(test)] 77 | mod tests { 78 | use super::*; 79 | 80 | #[test] 81 | fn crate_path() { 82 | #[allow(unused)] 83 | #[derive(Schema)] 84 | #[postcard(crate = crate)] 85 | struct Point { 86 | x: i32, 87 | y: i32, 88 | } 89 | 90 | assert_eq!( 91 | Point::SCHEMA, 92 | &schema::DataModelType::Struct { 93 | name: "Point", 94 | data: schema::Data::Struct(&[ 95 | &schema::NamedField { 96 | name: "x", 97 | ty: i32::SCHEMA 98 | }, 99 | &schema::NamedField { 100 | name: "y", 101 | ty: i32::SCHEMA 102 | }, 103 | ]) 104 | } 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /source/postcard-schema/src/schema/fmt.rs: -------------------------------------------------------------------------------- 1 | //! Formatting helper functionality 2 | //! 3 | //! This module provides ways of turning Data Model information into a human 4 | //! readable output 5 | 6 | #[cfg(all(not(feature = "use-std"), feature = "alloc"))] 7 | extern crate alloc; 8 | 9 | use super::owned::{OwnedData, OwnedDataModelType}; 10 | 11 | #[cfg(feature = "use-std")] 12 | use std::{string::String, vec::Vec}; 13 | 14 | #[cfg(all(not(feature = "use-std"), feature = "alloc"))] 15 | use alloc::{format, string::String, vec::Vec}; 16 | 17 | /// Is this [`OwnedDataModelType`] a primitive? 18 | pub fn is_prim(osdmty: &OwnedDataModelType) -> bool { 19 | match osdmty { 20 | OwnedDataModelType::Bool => true, 21 | OwnedDataModelType::I8 => true, 22 | OwnedDataModelType::U8 => true, 23 | OwnedDataModelType::I16 => true, 24 | OwnedDataModelType::I32 => true, 25 | OwnedDataModelType::I64 => true, 26 | OwnedDataModelType::I128 => true, 27 | OwnedDataModelType::U16 => true, 28 | OwnedDataModelType::U32 => true, 29 | OwnedDataModelType::U64 => true, 30 | OwnedDataModelType::U128 => true, 31 | OwnedDataModelType::Usize => true, 32 | OwnedDataModelType::Isize => true, 33 | OwnedDataModelType::F32 => true, 34 | OwnedDataModelType::F64 => true, 35 | OwnedDataModelType::Char => true, 36 | OwnedDataModelType::String => true, 37 | OwnedDataModelType::ByteArray => true, 38 | OwnedDataModelType::Option(ty) => is_prim(ty), 39 | OwnedDataModelType::Unit => true, 40 | OwnedDataModelType::Seq(_) => false, 41 | OwnedDataModelType::Tuple(_) => false, 42 | OwnedDataModelType::Map { key, val } => is_prim(key) && is_prim(val), 43 | OwnedDataModelType::Struct { .. } => false, 44 | OwnedDataModelType::Enum { .. } => false, 45 | OwnedDataModelType::Schema => true, 46 | } 47 | } 48 | 49 | /// Format an [`OwnedDataModelType`] to the given string. 50 | /// 51 | /// Use `top_level = true` when this is a standalone type, and `top_level = false` 52 | /// when this type is contained within another type 53 | pub fn fmt_owned_dmt_to_buf(dmt: &OwnedDataModelType, buf: &mut String, top_level: bool) { 54 | let fmt_data = |data: &OwnedData, buf: &mut String| match data { 55 | OwnedData::Unit => {} 56 | OwnedData::Newtype(inner) => { 57 | *buf += "("; 58 | fmt_owned_dmt_to_buf(inner, buf, false); 59 | *buf += ")"; 60 | } 61 | OwnedData::Tuple(fields) => { 62 | *buf += "("; 63 | let mut fields = fields.iter(); 64 | if let Some(first) = fields.next() { 65 | fmt_owned_dmt_to_buf(first, buf, false); 66 | } 67 | for field in fields { 68 | *buf += ", "; 69 | fmt_owned_dmt_to_buf(field, buf, false); 70 | } 71 | *buf += ")"; 72 | } 73 | OwnedData::Struct(fields) => { 74 | *buf += " { "; 75 | let mut fields = fields.iter(); 76 | if let Some(first) = fields.next() { 77 | *buf += &first.name; 78 | *buf += ": "; 79 | fmt_owned_dmt_to_buf(&first.ty, buf, false); 80 | } 81 | for field in fields { 82 | *buf += ", "; 83 | *buf += &field.name; 84 | *buf += ": "; 85 | fmt_owned_dmt_to_buf(&field.ty, buf, false); 86 | } 87 | *buf += " }"; 88 | } 89 | }; 90 | 91 | match dmt { 92 | OwnedDataModelType::Bool => *buf += "bool", 93 | OwnedDataModelType::I8 => *buf += "i8", 94 | OwnedDataModelType::U8 => *buf += "u8", 95 | OwnedDataModelType::I16 => *buf += "i16", 96 | OwnedDataModelType::I32 => *buf += "i32", 97 | OwnedDataModelType::I64 => *buf += "i64", 98 | OwnedDataModelType::I128 => *buf += "i128", 99 | OwnedDataModelType::U16 => *buf += "u16", 100 | OwnedDataModelType::U32 => *buf += "u32", 101 | OwnedDataModelType::U64 => *buf += "u64", 102 | OwnedDataModelType::U128 => *buf += "u128", 103 | OwnedDataModelType::Usize => *buf += "usize", 104 | OwnedDataModelType::Isize => *buf += "isize", 105 | OwnedDataModelType::F32 => *buf += "f32", 106 | OwnedDataModelType::F64 => *buf += "f64", 107 | OwnedDataModelType::Char => *buf += "char", 108 | OwnedDataModelType::String => *buf += "String", 109 | OwnedDataModelType::ByteArray => *buf += "[u8]", 110 | OwnedDataModelType::Option(ty) => { 111 | *buf += "Option<"; 112 | fmt_owned_dmt_to_buf(ty, buf, false); 113 | *buf += ">"; 114 | } 115 | OwnedDataModelType::Unit => *buf += "()", 116 | OwnedDataModelType::Seq(ty) => { 117 | *buf += "["; 118 | fmt_owned_dmt_to_buf(ty, buf, false); 119 | *buf += "]"; 120 | } 121 | OwnedDataModelType::Tuple(vec) => { 122 | if !vec.is_empty() { 123 | let first = &vec[0]; 124 | if vec.iter().all(|v| first == v) { 125 | // This is a fixed size array 126 | *buf += "["; 127 | fmt_owned_dmt_to_buf(first, buf, false); 128 | *buf += "; "; 129 | *buf += &format!("{}", vec.len()); 130 | *buf += "]"; 131 | } else { 132 | *buf += "("; 133 | let fields = vec 134 | .iter() 135 | .map(|v| { 136 | let mut buf = String::new(); 137 | fmt_owned_dmt_to_buf(v, &mut buf, false); 138 | buf 139 | }) 140 | .collect::>() 141 | .join(", "); 142 | *buf += &fields; 143 | *buf += ")"; 144 | } 145 | } else { 146 | *buf += "()"; 147 | } 148 | } 149 | OwnedDataModelType::Map { key, val } => { 150 | *buf += "Map<"; 151 | fmt_owned_dmt_to_buf(key, buf, false); 152 | *buf += ", "; 153 | fmt_owned_dmt_to_buf(val, buf, false); 154 | *buf += ">"; 155 | } 156 | OwnedDataModelType::Struct { name, data } => { 157 | if top_level { 158 | *buf += "struct "; 159 | *buf += name; 160 | fmt_data(data, buf); 161 | } else { 162 | *buf += name; 163 | } 164 | } 165 | OwnedDataModelType::Enum { name, variants } => { 166 | if top_level { 167 | *buf += "enum "; 168 | *buf += name; 169 | *buf += " { "; 170 | 171 | let fields = variants 172 | .iter() 173 | .map(|v| { 174 | let mut buf = String::new(); 175 | buf += &v.name; 176 | fmt_data(&v.data, &mut buf); 177 | buf 178 | }) 179 | .collect::>() 180 | .join(", "); 181 | *buf += &fields; 182 | *buf += " }"; 183 | } else { 184 | *buf += name; 185 | } 186 | } 187 | OwnedDataModelType::Schema => *buf += "Schema", 188 | } 189 | } 190 | 191 | /// Collect unique types mentioned by this [`OwnedDataModelType`] 192 | #[cfg(feature = "use-std")] 193 | pub fn discover_tys( 194 | ty: &OwnedDataModelType, 195 | set: &mut std::collections::HashSet, 196 | ) { 197 | let discover_tys_data = |data: &OwnedData, set: &mut _| match data { 198 | OwnedData::Unit => {} 199 | OwnedData::Newtype(inner) => discover_tys(inner, set), 200 | OwnedData::Tuple(elements) => { 201 | for element in elements { 202 | discover_tys(element, set) 203 | } 204 | } 205 | OwnedData::Struct(fields) => { 206 | for field in fields { 207 | discover_tys(&field.ty, set) 208 | } 209 | } 210 | }; 211 | 212 | set.insert(ty.clone()); 213 | match ty { 214 | OwnedDataModelType::Bool => {} 215 | OwnedDataModelType::I8 => {} 216 | OwnedDataModelType::U8 => {} 217 | OwnedDataModelType::I16 => {} 218 | OwnedDataModelType::I32 => {} 219 | OwnedDataModelType::I64 => {} 220 | OwnedDataModelType::I128 => {} 221 | OwnedDataModelType::U16 => {} 222 | OwnedDataModelType::U32 => {} 223 | OwnedDataModelType::U64 => {} 224 | OwnedDataModelType::U128 => {} 225 | 226 | // TODO: usize and isize don't impl Schema, which, fair. 227 | OwnedDataModelType::Usize => unreachable!(), 228 | OwnedDataModelType::Isize => unreachable!(), 229 | // 230 | OwnedDataModelType::F32 => {} 231 | OwnedDataModelType::F64 => {} 232 | OwnedDataModelType::Char => {} 233 | OwnedDataModelType::String => {} 234 | OwnedDataModelType::ByteArray => {} 235 | OwnedDataModelType::Option(inner) => { 236 | discover_tys(inner, set); 237 | } 238 | OwnedDataModelType::Unit => {} 239 | OwnedDataModelType::Seq(elements) => { 240 | discover_tys(elements, set); 241 | } 242 | OwnedDataModelType::Tuple(vec) => { 243 | for v in vec.iter() { 244 | discover_tys(v, set); 245 | } 246 | } 247 | OwnedDataModelType::Map { key, val } => { 248 | discover_tys(key, set); 249 | discover_tys(val, set); 250 | } 251 | OwnedDataModelType::Struct { name: _, data } => { 252 | discover_tys_data(data, set); 253 | } 254 | OwnedDataModelType::Enum { name: _, variants } => { 255 | for variant in variants { 256 | discover_tys_data(&variant.data, set); 257 | } 258 | } 259 | OwnedDataModelType::Schema => todo!(), 260 | }; 261 | } 262 | -------------------------------------------------------------------------------- /source/postcard-schema/src/schema/mod.rs: -------------------------------------------------------------------------------- 1 | //! ## Schema types 2 | //! 3 | //! The types in this module are used to define the schema of a given data type. 4 | //! 5 | //! The **Postcard Data Model** is nearly identical to the **Serde Data Model**, however Postcard also 6 | //! allows for one additional type, `Schema`, which maps to the [`DataModelType`] type, allowing 7 | //! the schema of types to also be sent over the wire and implement the `Schema` trait. 8 | //! 9 | //! ## Borrowed vs Owned 10 | //! 11 | //! For reasons that have to do with allowing for arbitrarily sized and nested schemas that 12 | //! can be created at compile/const time, as well as being usable in `no-std` contexts, the 13 | //! schema types in this module are implemented using a LOT of `&'static` references. 14 | //! 15 | //! This is useful in those limited contexts, however it makes it difficult to do things 16 | //! like deserialize them, as you can't generally get static references at runtime without 17 | //! a lot of leaking. 18 | //! 19 | //! For cases like this, the [`owned`] module exists, which has copies of all of the "borrowed" 20 | //! versions of the Data Model types. These owned types implement `From` for their borrowed 21 | //! counterpoint, so if you need to deserialize something, you probably want the Owned variant! 22 | 23 | #[cfg(any(feature = "use-std", feature = "alloc"))] 24 | pub mod owned; 25 | 26 | #[cfg(any(feature = "use-std", feature = "alloc"))] 27 | pub mod fmt; 28 | 29 | use serde::Serialize; 30 | 31 | /// This enum lists which of the Data Model Types apply to a given type. This describes how the 32 | /// type is encoded on the wire. 33 | /// 34 | /// This enum contains all Serde Data Model types as well as a "Schema" Type, 35 | /// which corresponds to [`DataModelType`] itself. 36 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] 37 | pub enum DataModelType { 38 | /// The `bool` Serde Data Model Type 39 | Bool, 40 | 41 | /// The `i8` Serde Data Model Type 42 | I8, 43 | 44 | /// The `u8` Serde Data Model Type 45 | U8, 46 | 47 | /// A variably encoded i16 48 | I16, 49 | 50 | /// A variably encoded i32 51 | I32, 52 | 53 | /// A variably encoded i64 54 | I64, 55 | 56 | /// A variably encoded i128 57 | I128, 58 | 59 | /// A variably encoded u16 60 | U16, 61 | 62 | /// A variably encoded u32 63 | U32, 64 | 65 | /// A variably encoded u64 66 | U64, 67 | 68 | /// A variably encoded u128 69 | U128, 70 | 71 | /// A variably encoded usize 72 | Usize, 73 | 74 | /// A variably encoded isize 75 | Isize, 76 | 77 | /// The `f32` Serde Data Model Type 78 | F32, 79 | 80 | /// The `f64` Serde Data Model Type 81 | F64, 82 | 83 | /// The `char` Serde Data Model Type 84 | Char, 85 | 86 | /// The `String` Serde Data Model Type 87 | String, 88 | 89 | /// The `&[u8]` Serde Data Model Type 90 | ByteArray, 91 | 92 | /// The `Option` Serde Data Model Type 93 | Option(&'static Self), 94 | 95 | /// The `()` Serde Data Model Type 96 | Unit, 97 | 98 | /// The "Sequence" Serde Data Model Type 99 | Seq(&'static Self), 100 | 101 | /// The "Tuple" Serde Data Model Type 102 | Tuple(&'static [&'static Self]), 103 | 104 | /// The "Map" Serde Data Model Type 105 | Map { 106 | /// The map "Key" type 107 | key: &'static Self, 108 | /// The map "Value" type 109 | val: &'static Self, 110 | }, 111 | 112 | /// One of the struct Serde Data Model types 113 | Struct { 114 | /// The name of this struct 115 | name: &'static str, 116 | /// The data contained in this struct 117 | data: Data, 118 | }, 119 | 120 | /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types) 121 | Enum { 122 | /// The name of this struct 123 | name: &'static str, 124 | /// The variants contained in this enum 125 | variants: &'static [&'static Variant], 126 | }, 127 | 128 | /// A [`DataModelType`]/[`OwnedDataModelType`](owned::OwnedDataModelType) 129 | Schema, 130 | } 131 | 132 | /// The contents of a struct or enum variant. 133 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] 134 | pub enum Data { 135 | /// The "Unit Struct" or "Unit Variant" Serde Data Model Type 136 | Unit, 137 | 138 | /// The "Newtype Struct" or "Newtype Variant" Serde Data Model Type 139 | Newtype(&'static DataModelType), 140 | 141 | /// The "Tuple Struct" or "Tuple Variant" Serde Data Model Type 142 | Tuple(&'static [&'static DataModelType]), 143 | 144 | /// The "Struct" or "Struct Variant" Serde Data Model Type 145 | Struct(&'static [&'static NamedField]), 146 | } 147 | 148 | /// This represents a named struct field. 149 | /// 150 | /// For example, in `struct Ex { a: u32 }` the field `a` would be reflected as 151 | /// `NamedField { name: "a", ty: DataModelType::U32 }`. 152 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] 153 | pub struct NamedField { 154 | /// The name of this field 155 | pub name: &'static str, 156 | /// The type of this field 157 | pub ty: &'static DataModelType, 158 | } 159 | 160 | /// An enum variant e.g. `T::Bar(...)` 161 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)] 162 | pub struct Variant { 163 | /// The name of this variant 164 | pub name: &'static str, 165 | /// The data contained in this variant 166 | pub data: Data, 167 | } 168 | -------------------------------------------------------------------------------- /source/postcard-schema/src/schema/owned.rs: -------------------------------------------------------------------------------- 1 | //! Owned Schema version 2 | 3 | use super::{Data, DataModelType, NamedField, Variant}; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[cfg(all(not(feature = "use-std"), feature = "alloc"))] 7 | extern crate alloc; 8 | 9 | #[cfg(feature = "use-std")] 10 | use std::{boxed::Box, collections::HashSet, string::String}; 11 | 12 | #[cfg(all(not(feature = "use-std"), feature = "alloc"))] 13 | use alloc::{boxed::Box, string::String}; 14 | 15 | // --- 16 | 17 | impl OwnedDataModelType { 18 | /// Convert an `[OwnedDataModelType]` to a pseudo-Rust type format 19 | pub fn to_pseudocode(&self) -> String { 20 | let mut buf = String::new(); 21 | super::fmt::fmt_owned_dmt_to_buf(self, &mut buf, true); 22 | buf 23 | } 24 | 25 | /// Collect all types used recursively by this type 26 | #[cfg(feature = "use-std")] 27 | pub fn all_used_types(&self) -> HashSet { 28 | let mut buf = HashSet::new(); 29 | super::fmt::discover_tys(self, &mut buf); 30 | buf 31 | } 32 | } 33 | 34 | impl core::fmt::Display for OwnedDataModelType { 35 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 36 | let pc = self.to_pseudocode(); 37 | f.write_str(&pc) 38 | } 39 | } 40 | 41 | impl crate::Schema for OwnedDataModelType { 42 | const SCHEMA: &'static DataModelType = &DataModelType::Schema; 43 | } 44 | 45 | // --- 46 | 47 | /// The owned version of [`DataModelType`] 48 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 49 | pub enum OwnedDataModelType { 50 | /// The `bool` Serde Data Model Type 51 | Bool, 52 | 53 | /// The `i8` Serde Data Model Type 54 | I8, 55 | 56 | /// The `u8` Serde Data Model Type 57 | U8, 58 | 59 | /// A variably encoded i16 60 | I16, 61 | 62 | /// A variably encoded i32 63 | I32, 64 | 65 | /// A variably encoded i64 66 | I64, 67 | 68 | /// A variably encoded i128 69 | I128, 70 | 71 | /// A variably encoded u16 72 | U16, 73 | 74 | /// A variably encoded u32 75 | U32, 76 | 77 | /// A variably encoded u64 78 | U64, 79 | 80 | /// A variably encoded u128 81 | U128, 82 | 83 | /// A variably encoded usize 84 | Usize, 85 | 86 | /// A variably encoded isize 87 | Isize, 88 | 89 | /// The `f32` Serde Data Model Type 90 | F32, 91 | 92 | /// The `f64 Serde Data Model Type 93 | F64, 94 | 95 | /// The `char` Serde Data Model Type 96 | Char, 97 | 98 | /// The `String` Serde Data Model Type 99 | String, 100 | 101 | /// The `&[u8]` Serde Data Model Type 102 | ByteArray, 103 | 104 | /// The `Option` Serde Data Model Type 105 | Option(Box), 106 | 107 | /// The `()` Serde Data Model Type 108 | Unit, 109 | 110 | /// The "Sequence" Serde Data Model Type 111 | Seq(Box), 112 | 113 | /// The "Tuple" Serde Data Model Type 114 | Tuple(Box<[Self]>), 115 | 116 | /// The "Map" Serde Data Model Type 117 | Map { 118 | /// The map "Key" type 119 | key: Box, 120 | /// The map "Value" type 121 | val: Box, 122 | }, 123 | 124 | /// One of the struct Serde Data Model types 125 | Struct { 126 | /// The name of this struct 127 | name: Box, 128 | /// The data contained in this struct 129 | data: OwnedData, 130 | }, 131 | 132 | /// The "Enum" Serde Data Model Type (which contains any of the "Variant" types) 133 | Enum { 134 | /// The name of this struct 135 | name: Box, 136 | /// The variants contained in this enum 137 | variants: Box<[OwnedVariant]>, 138 | }, 139 | 140 | /// A [`DataModelType`]/[`OwnedDataModelType`] 141 | Schema, 142 | } 143 | 144 | impl From<&DataModelType> for OwnedDataModelType { 145 | fn from(other: &DataModelType) -> Self { 146 | match other { 147 | DataModelType::Bool => Self::Bool, 148 | DataModelType::I8 => Self::I8, 149 | DataModelType::U8 => Self::U8, 150 | DataModelType::I16 => Self::I16, 151 | DataModelType::I32 => Self::I32, 152 | DataModelType::I64 => Self::I64, 153 | DataModelType::I128 => Self::I128, 154 | DataModelType::U16 => Self::U16, 155 | DataModelType::U32 => Self::U32, 156 | DataModelType::U64 => Self::U64, 157 | DataModelType::U128 => Self::U128, 158 | DataModelType::Usize => Self::Usize, 159 | DataModelType::Isize => Self::Isize, 160 | DataModelType::F32 => Self::F32, 161 | DataModelType::F64 => Self::F64, 162 | DataModelType::Char => Self::Char, 163 | DataModelType::String => Self::String, 164 | DataModelType::ByteArray => Self::ByteArray, 165 | DataModelType::Option(o) => Self::Option(Box::new((*o).into())), 166 | DataModelType::Unit => Self::Unit, 167 | DataModelType::Seq(s) => Self::Seq(Box::new((*s).into())), 168 | DataModelType::Tuple(t) => Self::Tuple(t.iter().map(|i| (*i).into()).collect()), 169 | DataModelType::Map { key, val } => Self::Map { 170 | key: Box::new((*key).into()), 171 | val: Box::new((*val).into()), 172 | }, 173 | DataModelType::Struct { name, data } => Self::Struct { 174 | name: (*name).into(), 175 | data: data.into(), 176 | }, 177 | DataModelType::Enum { name, variants } => Self::Enum { 178 | name: (*name).into(), 179 | variants: variants.iter().map(|i| (*i).into()).collect(), 180 | }, 181 | DataModelType::Schema => Self::Schema, 182 | } 183 | } 184 | } 185 | 186 | // --- 187 | 188 | /// The owned version of [`Data`]. 189 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 190 | pub enum OwnedData { 191 | /// The "Unit Struct" or "Unit Variant" Serde Data Model Type 192 | Unit, 193 | 194 | /// The "Newtype Struct" or "Newtype Variant" Serde Data Model Type 195 | Newtype(Box), 196 | 197 | /// The "Tuple Struct" or "Tuple Variant" Serde Data Model Type 198 | Tuple(Box<[OwnedDataModelType]>), 199 | 200 | /// The "Struct" or "Struct Variant" Serde Data Model Type 201 | Struct(Box<[OwnedNamedField]>), 202 | } 203 | 204 | impl From<&Data> for OwnedData { 205 | fn from(data: &Data) -> Self { 206 | match data { 207 | Data::Unit => Self::Unit, 208 | Data::Newtype(d) => Self::Newtype(Box::new((*d).into())), 209 | Data::Tuple(d) => Self::Tuple(d.iter().map(|i| (*i).into()).collect()), 210 | Data::Struct(d) => Self::Struct(d.iter().map(|i| (*i).into()).collect()), 211 | } 212 | } 213 | } 214 | 215 | // --- 216 | 217 | /// The owned version of [`NamedField`] 218 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 219 | pub struct OwnedNamedField { 220 | /// The name of this value 221 | pub name: Box, 222 | /// The type of this value 223 | pub ty: OwnedDataModelType, 224 | } 225 | 226 | impl From<&NamedField> for OwnedNamedField { 227 | fn from(value: &NamedField) -> Self { 228 | Self { 229 | name: value.name.into(), 230 | ty: value.ty.into(), 231 | } 232 | } 233 | } 234 | 235 | // --- 236 | 237 | /// The owned version of [`Variant`] 238 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] 239 | pub struct OwnedVariant { 240 | /// The name of this variant 241 | pub name: Box, 242 | /// The data contained in this variant 243 | pub data: OwnedData, 244 | } 245 | 246 | impl From<&Variant> for OwnedVariant { 247 | fn from(value: &Variant) -> Self { 248 | Self { 249 | name: value.name.into(), 250 | data: (&value.data).into(), 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /source/postcard-schema/tests/schema.rs: -------------------------------------------------------------------------------- 1 | use postcard_schema::{ 2 | schema::{owned::OwnedDataModelType, Data, DataModelType, NamedField, Variant}, 3 | Schema, 4 | }; 5 | use std::path::PathBuf; 6 | 7 | #[allow(unused)] 8 | #[derive(Schema)] 9 | enum Inner { 10 | Alpha, 11 | Beta, 12 | Gamma, 13 | Delta(i32, i16), 14 | Epsilon { zeta: f32, eta: bool }, 15 | } 16 | 17 | #[allow(unused)] 18 | #[derive(Schema)] 19 | struct Outer<'a> { 20 | a: u32, 21 | b: u64, 22 | c: u8, 23 | d: Inner, 24 | e: [u8; 10], 25 | f: &'a [u8], 26 | } 27 | 28 | #[allow(unused)] 29 | #[derive(Schema)] 30 | struct Slice<'a> { 31 | x: &'a [u8], 32 | } 33 | 34 | #[allow(unused)] 35 | #[derive(Schema)] 36 | #[postcard(bound = "")] // doesn't compile without this 37 | struct Bound { 38 | x: F::Out, 39 | } 40 | 41 | mod bound { 42 | use super::*; 43 | 44 | pub trait Fun { 45 | type Out: Schema; 46 | } 47 | 48 | pub enum Id {} 49 | impl Fun for Id { 50 | type Out = In; 51 | } 52 | } 53 | 54 | #[test] 55 | fn test_enum_serialize() { 56 | assert_eq!( 57 | &DataModelType::Enum { 58 | name: "Inner", 59 | variants: &[ 60 | &Variant { 61 | name: "Alpha", 62 | data: Data::Unit 63 | }, 64 | &Variant { 65 | name: "Beta", 66 | data: Data::Unit 67 | }, 68 | &Variant { 69 | name: "Gamma", 70 | data: Data::Unit 71 | }, 72 | &Variant { 73 | name: "Delta", 74 | data: Data::Tuple(&[i32::SCHEMA, i16::SCHEMA,]) 75 | }, 76 | &Variant { 77 | name: "Epsilon", 78 | data: Data::Struct(&[ 79 | &NamedField { 80 | name: "zeta", 81 | ty: f32::SCHEMA, 82 | }, 83 | &NamedField { 84 | name: "eta", 85 | ty: bool::SCHEMA, 86 | } 87 | ]), 88 | } 89 | ], 90 | }, 91 | Inner::SCHEMA 92 | ); 93 | } 94 | 95 | #[test] 96 | fn test_struct_serialize() { 97 | assert_eq!( 98 | Outer::SCHEMA, 99 | &DataModelType::Struct { 100 | name: "Outer", 101 | data: Data::Struct(&[ 102 | &NamedField { 103 | name: "a", 104 | ty: u32::SCHEMA 105 | }, 106 | &NamedField { 107 | name: "b", 108 | ty: u64::SCHEMA 109 | }, 110 | &NamedField { 111 | name: "c", 112 | ty: u8::SCHEMA 113 | }, 114 | &NamedField { 115 | name: "d", 116 | ty: Inner::SCHEMA 117 | }, 118 | &NamedField { 119 | name: "e", 120 | ty: &DataModelType::Tuple(&[u8::SCHEMA; 10]), 121 | }, 122 | &NamedField { 123 | name: "f", 124 | ty: &DataModelType::Seq(u8::SCHEMA), 125 | }, 126 | ]), 127 | } 128 | ); 129 | } 130 | 131 | #[test] 132 | fn test_slice_serialize() { 133 | assert_eq!( 134 | &DataModelType::Struct { 135 | name: "Slice", 136 | data: Data::Struct(&[&NamedField { 137 | name: "x", 138 | ty: &DataModelType::Seq(u8::SCHEMA), 139 | }]), 140 | }, 141 | Slice::SCHEMA 142 | ); 143 | } 144 | 145 | #[test] 146 | fn test_bound_serialize() { 147 | assert_eq!( 148 | &DataModelType::Struct { 149 | name: "Bound", 150 | data: Data::Struct(&[&NamedField { 151 | name: "x", 152 | ty: u8::SCHEMA 153 | }]), 154 | }, 155 | Bound::::SCHEMA, 156 | ); 157 | } 158 | 159 | #[allow(unused)] 160 | #[derive(Debug, Schema)] 161 | enum TestEnum<'a> { 162 | Alpha, 163 | Beta(u32), 164 | Gamma { a: bool, b: &'a [u8] }, 165 | Delta(f32, Option<&'a str>), 166 | Epsilon(TestStruct1), 167 | } 168 | 169 | #[allow(unused)] 170 | #[derive(Debug, Schema)] 171 | struct TestStruct1 { 172 | a: i8, 173 | b: i16, 174 | c: i32, 175 | d: i64, 176 | } 177 | 178 | #[allow(unused)] 179 | #[derive(Debug, Schema)] 180 | struct TestStruct2<'a> { 181 | x: TestEnum<'a>, 182 | y: TestStruct1, 183 | z: Result, 184 | } 185 | 186 | #[test] 187 | fn owned_punning() { 188 | let borrowed_schema = TestStruct2::SCHEMA; 189 | let owned_schema: OwnedDataModelType = borrowed_schema.into(); 190 | 191 | // Check that they are the same on the wire when serialized 192 | let ser_borrowed_schema = postcard::to_stdvec(borrowed_schema).unwrap(); 193 | let ser_owned_schema = postcard::to_stdvec(&owned_schema).unwrap(); 194 | assert_eq!(ser_borrowed_schema, ser_owned_schema); 195 | 196 | // TODO: This is wildly repetitive, and likely could benefit from interning of 197 | // repeated types, strings, etc. 198 | assert_eq!(ser_borrowed_schema.len(), 187); 199 | 200 | // Check that we round-trip correctly 201 | let deser_borrowed_schema = 202 | postcard::from_bytes::(&ser_borrowed_schema).unwrap(); 203 | let deser_owned_schema = postcard::from_bytes::(&ser_owned_schema).unwrap(); 204 | assert_eq!(deser_borrowed_schema, deser_owned_schema); 205 | assert_eq!(deser_borrowed_schema, owned_schema); 206 | assert_eq!(deser_owned_schema, owned_schema); 207 | } 208 | 209 | #[allow(unused)] 210 | #[derive(Debug, Schema)] 211 | struct TestStruct3(u64); 212 | 213 | #[allow(unused)] 214 | #[derive(Debug, Schema)] 215 | struct TestStruct4(u64, bool); 216 | 217 | #[allow(unused)] 218 | #[derive(Debug, Schema)] 219 | enum TestEnum2 { 220 | Nt(u64), 221 | Tup(u64, bool), 222 | } 223 | 224 | #[test] 225 | fn newtype_vs_tuple() { 226 | assert_eq!( 227 | TestStruct3::SCHEMA, 228 | &DataModelType::Struct { 229 | name: "TestStruct3", 230 | data: Data::Newtype(u64::SCHEMA) 231 | } 232 | ); 233 | 234 | assert_eq!( 235 | TestStruct4::SCHEMA, 236 | &DataModelType::Struct { 237 | name: "TestStruct4", 238 | data: Data::Tuple(&[u64::SCHEMA, bool::SCHEMA]), 239 | } 240 | ); 241 | 242 | assert_eq!( 243 | TestEnum2::SCHEMA, 244 | &DataModelType::Enum { 245 | name: "TestEnum2", 246 | variants: &[ 247 | &Variant { 248 | name: "Nt", 249 | data: Data::Newtype(u64::SCHEMA) 250 | }, 251 | &Variant { 252 | name: "Tup", 253 | data: Data::Tuple(&[u64::SCHEMA, bool::SCHEMA]) 254 | }, 255 | ], 256 | } 257 | ); 258 | } 259 | 260 | // Formatting 261 | 262 | fn dewit() -> String { 263 | let schema: OwnedDataModelType = T::SCHEMA.into(); 264 | schema.to_pseudocode() 265 | } 266 | 267 | #[allow(unused)] 268 | #[derive(Schema)] 269 | struct UnitStruct; 270 | 271 | #[allow(unused)] 272 | #[derive(Schema)] 273 | struct NewTypeStruct(String); 274 | 275 | #[allow(unused)] 276 | #[derive(Schema)] 277 | struct TupStruct(u64, String); 278 | 279 | #[allow(unused)] 280 | #[derive(Schema)] 281 | enum Enums { 282 | Unit, 283 | Nt(u64), 284 | Tup(u32, bool), 285 | } 286 | 287 | #[allow(unused)] 288 | #[derive(Schema)] 289 | struct Classic { 290 | a: u32, 291 | b: u16, 292 | c: bool, 293 | } 294 | 295 | #[allow(unused)] 296 | #[derive(Schema)] 297 | struct ClassicGen { 298 | a: u32, 299 | b: T, 300 | } 301 | 302 | #[test] 303 | fn smoke() { 304 | #[allow(clippy::type_complexity)] 305 | let tests: &[(fn() -> String, &str)] = &[ 306 | (dewit::, "u8"), 307 | (dewit::, "u16"), 308 | (dewit::, "u32"), 309 | (dewit::, "u64"), 310 | (dewit::, "u128"), 311 | (dewit::, "i8"), 312 | (dewit::, "i16"), 313 | (dewit::, "i32"), 314 | (dewit::, "i64"), 315 | (dewit::, "i128"), 316 | (dewit::<()>, "()"), 317 | (dewit::, "char"), 318 | (dewit::, "bool"), 319 | (dewit::, "String"), 320 | (dewit::>, "Option"), 321 | (dewit::, "struct UnitStruct"), 322 | (dewit::>, "Option"), 323 | (dewit::, "struct NewTypeStruct(String)"), 324 | (dewit::>, "Option"), 325 | ( 326 | dewit::, 327 | "enum Enums { Unit, Nt(u64), Tup(u32, bool) }", 328 | ), 329 | (dewit::>, "Option"), 330 | (dewit::<&[u8]>, "[u8]"), 331 | (dewit::>, "[u16]"), 332 | (dewit::<[u8; 16]>, "[u8; 16]"), 333 | (dewit::<(u8, u16, u32)>, "(u8, u16, u32)"), 334 | (dewit::, "struct TupStruct(u64, String)"), 335 | (dewit::>, "Option"), 336 | ( 337 | dewit::>, 338 | "Map", 339 | ), 340 | (dewit::>, "[u32]"), 341 | ( 342 | dewit::, 343 | "struct Classic { a: u32, b: u16, c: bool }", 344 | ), 345 | ( 346 | dewit::>, 347 | "struct ClassicGen { a: u32, b: i32 }", 348 | ), 349 | (dewit::>, "Option"), 350 | (dewit::>>, "Option"), 351 | (dewit::, "String"), 352 | ]; 353 | for (f, s) in tests { 354 | assert_eq!(f().as_str(), *s); 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /source/postcard/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "postcard" 3 | version = "1.1.1" 4 | authors = ["James Munns "] 5 | edition = "2021" 6 | readme = "README.md" 7 | repository = "https://github.com/jamesmunns/postcard" 8 | description = "A no_std + serde compatible message library for Rust" 9 | license = "MIT OR Apache-2.0" 10 | categories = [ 11 | "embedded", 12 | "no-std", 13 | ] 14 | keywords = [ 15 | "serde", 16 | "cobs", 17 | "framing", 18 | ] 19 | documentation = "https://docs.rs/postcard/" 20 | 21 | [package.metadata.docs.rs] 22 | all-features = true 23 | rustdoc-args = ["--cfg", "docsrs"] 24 | 25 | [dependencies] 26 | 27 | [dependencies.heapless] 28 | version = "0.7.0" 29 | default-features = false 30 | features = ["serde"] 31 | optional = true 32 | 33 | [dependencies.serde] 34 | version = "1.0.100" 35 | default-features = false 36 | features = ["derive"] 37 | 38 | [dependencies.cobs] 39 | version = "0.2.3" 40 | default-features = false 41 | 42 | [dependencies.defmt] 43 | version = "0.3" 44 | optional = true 45 | 46 | [dependencies.embedded-io-04] 47 | package = "embedded-io" 48 | version = "0.4" 49 | optional = true 50 | 51 | [dependencies.embedded-io-06] 52 | package = "embedded-io" 53 | version = "0.6" 54 | optional = true 55 | 56 | [dependencies.postcard-derive] 57 | version = "0.1.2" 58 | optional = true 59 | 60 | [dependencies.crc] 61 | version = "3.0.1" 62 | optional = true 63 | 64 | [features] 65 | default = ["heapless-cas"] 66 | 67 | # Enables support for `embedded-io` traits 68 | # This feature will track the latest `embedded-io` when major releases are made 69 | embedded-io = ["dep:embedded-io-04"] 70 | 71 | # Specific versions of `embedded-io` can be selected through the features below 72 | embedded-io-04 = ["dep:embedded-io-04"] 73 | embedded-io-06 = ["dep:embedded-io-06"] 74 | 75 | use-std = ["serde/std", "alloc"] 76 | heapless-cas = ["heapless", "dep:heapless", "heapless/cas"] 77 | alloc = ["serde/alloc", "embedded-io-04?/alloc", "embedded-io-06?/alloc"] 78 | use-defmt = ["defmt"] 79 | use-crc = ["crc"] 80 | 81 | # Experimental features! 82 | # 83 | # NOT subject to SemVer guarantees! 84 | experimental-derive = ["postcard-derive"] 85 | crc = ["dep:crc"] 86 | defmt = ["dep:defmt"] 87 | heapless = ["dep:heapless"] 88 | postcard-derive = ["dep:postcard-derive"] 89 | -------------------------------------------------------------------------------- /source/postcard/README.md: -------------------------------------------------------------------------------- 1 | # Postcard 2 | 3 | [![Documentation](https://docs.rs/postcard/badge.svg)](https://docs.rs/postcard) 4 | 5 | Postcard is a `#![no_std]` focused serializer and deserializer for Serde. 6 | 7 | Postcard aims to be convenient for developers in constrained environments, while 8 | allowing for flexibility to customize behavior as needed. 9 | 10 | ## Design Goals 11 | 12 | 1. Design primarily for `#![no_std]` usage, in embedded or other constrained contexts 13 | 2. Support a maximal set of `serde` features, so `postcard` can be used as a drop in replacement 14 | 3. Avoid special differences in code between communication code written for a microcontroller or a desktop/server PC 15 | 4. Be resource efficient - memory usage, code size, developer time, and CPU time; in that order 16 | 5. Allow library users to customize the serialization and deserialization behavior to fit their bespoke needs 17 | 18 | ## Format Stability 19 | 20 | As of v1.0.0, `postcard` has a documented and stable wire format. More information about this 21 | wire format can be found in the `spec/` folder of the Postcard repository, or viewed online 22 | at . 23 | 24 | Work towards the Postcard Specification and portions of the Postcard 1.0 Release 25 | were sponsored by Mozilla Corporation. 26 | 27 | ## Variable Length Data 28 | 29 | All signed and unsigned integers larger than eight bits are encoded using a [Varint]. 30 | This includes the length of array slices, as well as the discriminant of `enums`. 31 | 32 | For more information, see the [Varint] chapter of the wire specification. 33 | 34 | [Varint]: https://postcard.jamesmunns.com/wire-format.html#varint-encoded-integers 35 | 36 | ## Example - Serialization/Deserialization 37 | 38 | Postcard can serialize and deserialize messages similar to other `serde` formats. 39 | 40 | Using the default `heapless` feature to serialize to a `heapless::Vec`: 41 | 42 | ```rust 43 | use core::ops::Deref; 44 | use serde::{Serialize, Deserialize}; 45 | use postcard::{from_bytes, to_vec}; 46 | use heapless::Vec; 47 | 48 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 49 | struct RefStruct<'a> { 50 | bytes: &'a [u8], 51 | str_s: &'a str, 52 | } 53 | let message = "hElLo"; 54 | let bytes = [0x01, 0x10, 0x02, 0x20]; 55 | let output: Vec = to_vec(&RefStruct { 56 | bytes: &bytes, 57 | str_s: message, 58 | }).unwrap(); 59 | 60 | assert_eq!( 61 | &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], 62 | output.deref() 63 | ); 64 | 65 | let out: RefStruct = from_bytes(output.deref()).unwrap(); 66 | assert_eq!( 67 | out, 68 | RefStruct { 69 | bytes: &bytes, 70 | str_s: message, 71 | } 72 | ); 73 | ``` 74 | 75 | Or the optional `alloc` feature to serialize to an `alloc::vec::Vec`: 76 | 77 | ```rust 78 | use core::ops::Deref; 79 | use serde::{Serialize, Deserialize}; 80 | use postcard::{from_bytes, to_allocvec}; 81 | extern crate alloc; 82 | use alloc::vec::Vec; 83 | 84 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 85 | struct RefStruct<'a> { 86 | bytes: &'a [u8], 87 | str_s: &'a str, 88 | } 89 | let message = "hElLo"; 90 | let bytes = [0x01, 0x10, 0x02, 0x20]; 91 | let output: Vec = to_allocvec(&RefStruct { 92 | bytes: &bytes, 93 | str_s: message, 94 | }).unwrap(); 95 | 96 | assert_eq!( 97 | &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], 98 | output.deref() 99 | ); 100 | 101 | let out: RefStruct = from_bytes(output.deref()).unwrap(); 102 | assert_eq!( 103 | out, 104 | RefStruct { 105 | bytes: &bytes, 106 | str_s: message, 107 | } 108 | ); 109 | ``` 110 | 111 | ## Flavors 112 | 113 | `postcard` supports a system called `Flavors`, which are used to modify the way 114 | postcard serializes or processes serialized data. These flavors act as "plugins" or "middlewares" 115 | during the serialization or deserialization process, and can be combined to obtain complex protocol formats. 116 | 117 | See the documentation of the `ser_flavors` or `de_flavors` modules for more information on usage. 118 | 119 | ## Setup - `Cargo.toml` 120 | 121 | Don't forget to add [the `no-std` subset](https://serde.rs/no-std.html) of `serde` along with `postcard` to the `[dependencies]` section of your `Cargo.toml`! 122 | 123 | ```toml 124 | [dependencies] 125 | postcard = "1.0.0" 126 | 127 | # By default, `serde` has the `std` feature enabled, which makes it unsuitable for embedded targets 128 | # disabling default-features fixes this 129 | serde = { version = "1.0.*", default-features = false } 130 | ``` 131 | 132 | ## License 133 | 134 | Licensed under either of 135 | 136 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 137 | ) 138 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) 139 | 140 | at your option. 141 | 142 | ### Contribution 143 | 144 | Unless you explicitly state otherwise, any contribution intentionally submitted 145 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 146 | dual licensed as above, without any additional terms or conditions. 147 | -------------------------------------------------------------------------------- /source/postcard/src/accumulator.rs: -------------------------------------------------------------------------------- 1 | //! An accumulator used to collect chunked COBS data and deserialize it. 2 | 3 | use serde::Deserialize; 4 | 5 | /// An accumulator used to collect chunked COBS data and deserialize it. 6 | /// 7 | /// This is often useful when you receive "parts" of the message at a time, for example when draining 8 | /// a serial port buffer that may not contain an entire uninterrupted message. 9 | /// 10 | /// # Examples 11 | /// 12 | /// Deserialize a struct by reading chunks from a [`Read`]er. 13 | /// 14 | /// ```rust 15 | /// use postcard::accumulator::{CobsAccumulator, FeedResult}; 16 | /// use serde::Deserialize; 17 | /// use std::io::Read; 18 | /// 19 | /// # let mut input_buf = [0u8; 256]; 20 | /// # #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 21 | /// # struct MyData { 22 | /// # a: u32, 23 | /// # b: bool, 24 | /// # c: [u8; 16], 25 | /// # } 26 | /// let input = /* Anything that implements the `Read` trait */ 27 | /// # postcard::to_slice_cobs(&MyData { 28 | /// # a: 0xabcdef00, 29 | /// # b: true, 30 | /// # c: [0xab; 16], 31 | /// # }, &mut input_buf).unwrap(); 32 | /// # let mut input = &input[..]; 33 | /// 34 | /// let mut raw_buf = [0u8; 32]; 35 | /// let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new(); 36 | /// 37 | /// while let Ok(ct) = input.read(&mut raw_buf) { 38 | /// // Finished reading input 39 | /// if ct == 0 { 40 | /// break; 41 | /// } 42 | /// 43 | /// let buf = &raw_buf[..ct]; 44 | /// let mut window = &buf[..]; 45 | /// 46 | /// 'cobs: while !window.is_empty() { 47 | /// window = match cobs_buf.feed::(&window) { 48 | /// FeedResult::Consumed => break 'cobs, 49 | /// FeedResult::OverFull(new_wind) => new_wind, 50 | /// FeedResult::DeserError(new_wind) => new_wind, 51 | /// FeedResult::Success { data, remaining } => { 52 | /// // Do something with `data: MyData` here. 53 | /// 54 | /// dbg!(data); 55 | /// 56 | /// remaining 57 | /// } 58 | /// }; 59 | /// } 60 | /// } 61 | /// ``` 62 | /// 63 | /// [`Read`]: std::io::Read 64 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 65 | pub struct CobsAccumulator { 66 | buf: [u8; N], 67 | idx: usize, 68 | } 69 | 70 | /// The result of feeding the accumulator. 71 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 72 | pub enum FeedResult<'a, T> { 73 | /// Consumed all data, still pending. 74 | Consumed, 75 | 76 | /// Buffer was filled. Contains remaining section of input, if any. 77 | OverFull(&'a [u8]), 78 | 79 | /// Reached end of chunk, but deserialization failed. Contains remaining section of input, if. 80 | /// any 81 | DeserError(&'a [u8]), 82 | 83 | /// Deserialization complete. Contains deserialized data and remaining section of input, if any. 84 | Success { 85 | /// Deserialize data. 86 | data: T, 87 | 88 | /// Remaining data left in the buffer after deserializing. 89 | remaining: &'a [u8], 90 | }, 91 | } 92 | 93 | impl Default for CobsAccumulator { 94 | fn default() -> Self { 95 | Self::new() 96 | } 97 | } 98 | 99 | impl CobsAccumulator { 100 | /// Create a new accumulator. 101 | pub const fn new() -> Self { 102 | CobsAccumulator { 103 | buf: [0; N], 104 | idx: 0, 105 | } 106 | } 107 | 108 | /// Appends data to the internal buffer and attempts to deserialize the accumulated data into 109 | /// `T`. 110 | #[inline] 111 | pub fn feed<'a, T>(&mut self, input: &'a [u8]) -> FeedResult<'a, T> 112 | where 113 | T: for<'de> Deserialize<'de>, 114 | { 115 | self.feed_ref(input) 116 | } 117 | 118 | /// Appends data to the internal buffer and attempts to deserialize the accumulated data into 119 | /// `T`. 120 | /// 121 | /// This differs from feed, as it allows the `T` to reference data within the internal buffer, but 122 | /// mutably borrows the accumulator for the lifetime of the deserialization. 123 | /// If `T` does not require the reference, the borrow of `self` ends at the end of the function. 124 | pub fn feed_ref<'de, 'a, T>(&'de mut self, input: &'a [u8]) -> FeedResult<'a, T> 125 | where 126 | T: Deserialize<'de>, 127 | { 128 | if input.is_empty() { 129 | return FeedResult::Consumed; 130 | } 131 | 132 | let zero_pos = input.iter().position(|&i| i == 0); 133 | 134 | if let Some(n) = zero_pos { 135 | // Yes! We have an end of message here. 136 | // Add one to include the zero in the "take" portion 137 | // of the buffer, rather than in "release". 138 | let (take, release) = input.split_at(n + 1); 139 | 140 | // Does it fit? 141 | if (self.idx + take.len()) <= N { 142 | // Aw yiss - add to array 143 | self.extend_unchecked(take); 144 | 145 | let retval = match crate::from_bytes_cobs::(&mut self.buf[..self.idx]) { 146 | Ok(t) => FeedResult::Success { 147 | data: t, 148 | remaining: release, 149 | }, 150 | Err(_) => FeedResult::DeserError(release), 151 | }; 152 | self.idx = 0; 153 | retval 154 | } else { 155 | self.idx = 0; 156 | FeedResult::OverFull(release) 157 | } 158 | } else { 159 | // Does it fit? 160 | if (self.idx + input.len()) > N { 161 | // nope 162 | let new_start = N - self.idx; 163 | self.idx = 0; 164 | FeedResult::OverFull(&input[new_start..]) 165 | } else { 166 | // yup! 167 | self.extend_unchecked(input); 168 | FeedResult::Consumed 169 | } 170 | } 171 | } 172 | 173 | /// Extend the internal buffer with the given input. 174 | /// 175 | /// # Panics 176 | /// 177 | /// Will panic if the input does not fit in the internal buffer. 178 | fn extend_unchecked(&mut self, input: &[u8]) { 179 | let new_end = self.idx + input.len(); 180 | self.buf[self.idx..new_end].copy_from_slice(input); 181 | self.idx = new_end; 182 | } 183 | } 184 | 185 | #[cfg(test)] 186 | mod test { 187 | use super::*; 188 | 189 | #[test] 190 | fn loop_test() { 191 | #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 192 | struct Demo { 193 | a: u32, 194 | b: u8, 195 | } 196 | 197 | let mut raw_buf = [0u8; 64]; 198 | let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); 199 | 200 | let ser = crate::to_slice_cobs(&Demo { a: 10, b: 20 }, &mut raw_buf).unwrap(); 201 | 202 | if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) { 203 | assert_eq!(Demo { a: 10, b: 20 }, data); 204 | assert_eq!(remaining.len(), 0); 205 | } else { 206 | panic!() 207 | } 208 | } 209 | 210 | #[test] 211 | #[cfg(feature = "heapless")] 212 | fn double_loop_test() { 213 | #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 214 | struct Demo { 215 | a: u32, 216 | b: u8, 217 | } 218 | 219 | let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); 220 | 221 | let mut ser = crate::to_vec_cobs::<_, 128>(&Demo { a: 10, b: 20 }).unwrap(); 222 | let ser2 = crate::to_vec_cobs::<_, 128>(&Demo { 223 | a: 256854231, 224 | b: 115, 225 | }) 226 | .unwrap(); 227 | ser.extend(ser2); 228 | 229 | let (demo1, ser) = if let FeedResult::Success { data, remaining } = cobs_buf.feed(&ser[..]) 230 | { 231 | (data, remaining) 232 | } else { 233 | panic!() 234 | }; 235 | 236 | assert_eq!(Demo { a: 10, b: 20 }, demo1); 237 | 238 | let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) { 239 | assert_eq!(remaining.len(), 0); 240 | data 241 | } else { 242 | panic!() 243 | }; 244 | 245 | assert_eq!(Demo { a: 10, b: 20 }, demo1); 246 | assert_eq!( 247 | Demo { 248 | a: 256854231, 249 | b: 115 250 | }, 251 | demo2 252 | ); 253 | } 254 | 255 | #[test] 256 | #[cfg(feature = "heapless")] 257 | fn loop_test_ref() { 258 | #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 259 | struct Demo<'a> { 260 | a: u32, 261 | b: u8, 262 | c: &'a str, 263 | } 264 | 265 | let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); 266 | 267 | let ser = crate::to_vec_cobs::<_, 128>(&Demo { 268 | a: 10, 269 | b: 20, 270 | c: "test", 271 | }) 272 | .unwrap(); 273 | 274 | if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) { 275 | assert_eq!( 276 | Demo { 277 | a: 10, 278 | b: 20, 279 | c: "test" 280 | }, 281 | data 282 | ); 283 | assert_eq!(remaining.len(), 0); 284 | } else { 285 | panic!() 286 | } 287 | } 288 | 289 | #[test] 290 | #[cfg(feature = "heapless")] 291 | fn double_loop_test_ref() { 292 | #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 293 | struct Demo<'a> { 294 | a: u32, 295 | b: u8, 296 | c: &'a str, 297 | } 298 | 299 | let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); 300 | 301 | let mut ser = crate::to_vec_cobs::<_, 128>(&Demo { 302 | a: 10, 303 | b: 20, 304 | c: "test", 305 | }) 306 | .unwrap(); 307 | let ser2 = crate::to_vec_cobs::<_, 128>(&Demo { 308 | a: 256854231, 309 | b: 115, 310 | c: "different test", 311 | }) 312 | .unwrap(); 313 | ser.extend(ser2); 314 | 315 | let (data, ser) = 316 | if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) { 317 | (data, remaining) 318 | } else { 319 | panic!() 320 | }; 321 | 322 | assert!( 323 | Demo { 324 | a: 10, 325 | b: 20, 326 | c: "test" 327 | } == data 328 | ); 329 | 330 | let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(ser) { 331 | assert!(remaining.is_empty()); 332 | data 333 | } else { 334 | panic!() 335 | }; 336 | 337 | // Uncommenting the below line causes the test to no-longer compile, as cobs_buf would then be mutably borrowed twice 338 | //assert!(Demo { a: 10, b: 20, c : "test" } == data); 339 | 340 | assert!( 341 | Demo { 342 | a: 256854231, 343 | b: 115, 344 | c: "different test" 345 | } == demo2 346 | ); 347 | } 348 | 349 | #[test] 350 | #[cfg(feature = "heapless")] 351 | fn extend_unchecked_in_bounds_test() { 352 | // Test bug present in revision abcb407: 353 | // extend_unchecked may be passed slice with size 1 greater than accumulator buffer causing panic 354 | 355 | #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] 356 | struct Demo { 357 | data: [u8; 10], 358 | } 359 | 360 | let data = crate::to_vec_cobs::<_, 128>(&Demo { data: [0xcc; 10] }).unwrap(); 361 | assert_eq!(data.len(), 12); // 1 byte for offset + 1 sentinel byte appended 362 | 363 | // Accumulator has 1 byte less space than encoded message 364 | let mut acc: CobsAccumulator<11> = CobsAccumulator::new(); 365 | assert!(matches!( 366 | acc.feed::(&data[..]), 367 | FeedResult::OverFull(_) 368 | )); 369 | 370 | // Accumulator is juuuuust right 371 | let mut acc: CobsAccumulator<12> = CobsAccumulator::new(); 372 | assert!(matches!( 373 | acc.feed::(&data[..]), 374 | FeedResult::Success { .. } 375 | )); 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /source/postcard/src/de/deserializer.rs: -------------------------------------------------------------------------------- 1 | use serde::de::{self, DeserializeSeed, IntoDeserializer, Visitor}; 2 | 3 | use crate::de::flavors::{Flavor, Slice}; 4 | use crate::error::{Error, Result}; 5 | use crate::varint::{max_of_last_byte, varint_max}; 6 | use core::marker::PhantomData; 7 | 8 | /// A `serde` compatible deserializer, generic over “Flavors” of deserializing plugins. 9 | /// 10 | /// Please note that postcard messages are not self-describing and therefore incompatible with 11 | /// [internally tagged enums](https://serde.rs/enum-representations.html#internally-tagged). 12 | pub struct Deserializer<'de, F: Flavor<'de>> { 13 | flavor: F, 14 | _plt: PhantomData<&'de ()>, 15 | } 16 | 17 | impl<'de, F> Deserializer<'de, F> 18 | where 19 | F: Flavor<'de> + 'de, 20 | { 21 | /// Obtain a Deserializer from a slice of bytes 22 | pub fn from_flavor(flavor: F) -> Self { 23 | Deserializer { 24 | flavor, 25 | _plt: PhantomData, 26 | } 27 | } 28 | 29 | /// Return the remaining (unused) bytes in the Deserializer along with any 30 | /// additional data provided by the [`Flavor`] 31 | pub fn finalize(self) -> Result { 32 | self.flavor.finalize() 33 | } 34 | } 35 | 36 | impl<'de> Deserializer<'de, Slice<'de>> { 37 | /// Obtain a Deserializer from a slice of bytes 38 | pub fn from_bytes(input: &'de [u8]) -> Self { 39 | Deserializer { 40 | flavor: Slice::new(input), 41 | _plt: PhantomData, 42 | } 43 | } 44 | } 45 | 46 | impl<'de, F: Flavor<'de>> Deserializer<'de, F> { 47 | #[cfg(target_pointer_width = "16")] 48 | #[inline(always)] 49 | fn try_take_varint_usize(&mut self) -> Result { 50 | self.try_take_varint_u16().map(|u| u as usize) 51 | } 52 | 53 | #[cfg(target_pointer_width = "32")] 54 | #[inline(always)] 55 | fn try_take_varint_usize(&mut self) -> Result { 56 | self.try_take_varint_u32().map(|u| u as usize) 57 | } 58 | 59 | #[cfg(target_pointer_width = "64")] 60 | #[inline(always)] 61 | fn try_take_varint_usize(&mut self) -> Result { 62 | self.try_take_varint_u64().map(|u| u as usize) 63 | } 64 | 65 | #[inline] 66 | fn try_take_varint_u16(&mut self) -> Result { 67 | let mut out = 0; 68 | for i in 0..varint_max::() { 69 | let val = self.flavor.pop()?; 70 | let carry = (val & 0x7F) as u16; 71 | out |= carry << (7 * i); 72 | 73 | if (val & 0x80) == 0 { 74 | if i == varint_max::() - 1 && val > max_of_last_byte::() { 75 | return Err(Error::DeserializeBadVarint); 76 | } else { 77 | return Ok(out); 78 | } 79 | } 80 | } 81 | Err(Error::DeserializeBadVarint) 82 | } 83 | 84 | #[inline] 85 | fn try_take_varint_u32(&mut self) -> Result { 86 | let mut out = 0; 87 | for i in 0..varint_max::() { 88 | let val = self.flavor.pop()?; 89 | let carry = (val & 0x7F) as u32; 90 | out |= carry << (7 * i); 91 | 92 | if (val & 0x80) == 0 { 93 | if i == varint_max::() - 1 && val > max_of_last_byte::() { 94 | return Err(Error::DeserializeBadVarint); 95 | } else { 96 | return Ok(out); 97 | } 98 | } 99 | } 100 | Err(Error::DeserializeBadVarint) 101 | } 102 | 103 | #[inline] 104 | fn try_take_varint_u64(&mut self) -> Result { 105 | let mut out = 0; 106 | for i in 0..varint_max::() { 107 | let val = self.flavor.pop()?; 108 | let carry = (val & 0x7F) as u64; 109 | out |= carry << (7 * i); 110 | 111 | if (val & 0x80) == 0 { 112 | if i == varint_max::() - 1 && val > max_of_last_byte::() { 113 | return Err(Error::DeserializeBadVarint); 114 | } else { 115 | return Ok(out); 116 | } 117 | } 118 | } 119 | Err(Error::DeserializeBadVarint) 120 | } 121 | 122 | #[inline] 123 | fn try_take_varint_u128(&mut self) -> Result { 124 | let mut out = 0; 125 | for i in 0..varint_max::() { 126 | let val = self.flavor.pop()?; 127 | let carry = (val & 0x7F) as u128; 128 | out |= carry << (7 * i); 129 | 130 | if (val & 0x80) == 0 { 131 | if i == varint_max::() - 1 && val > max_of_last_byte::() { 132 | return Err(Error::DeserializeBadVarint); 133 | } else { 134 | return Ok(out); 135 | } 136 | } 137 | } 138 | Err(Error::DeserializeBadVarint) 139 | } 140 | } 141 | 142 | struct SeqAccess<'a, 'b, F: Flavor<'b>> { 143 | deserializer: &'a mut Deserializer<'b, F>, 144 | len: usize, 145 | } 146 | 147 | impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::SeqAccess<'b> for SeqAccess<'a, 'b, F> { 148 | type Error = Error; 149 | 150 | #[inline] 151 | fn next_element_seed>(&mut self, seed: V) -> Result> { 152 | if self.len > 0 { 153 | self.len -= 1; 154 | Ok(Some(DeserializeSeed::deserialize( 155 | seed, 156 | &mut *self.deserializer, 157 | )?)) 158 | } else { 159 | Ok(None) 160 | } 161 | } 162 | 163 | #[inline] 164 | fn size_hint(&self) -> Option { 165 | match self.deserializer.flavor.size_hint() { 166 | Some(size) if size < self.len => None, 167 | _ => Some(self.len), 168 | } 169 | } 170 | } 171 | 172 | struct MapAccess<'a, 'b, F: Flavor<'b>> { 173 | deserializer: &'a mut Deserializer<'b, F>, 174 | len: usize, 175 | } 176 | 177 | impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::MapAccess<'b> for MapAccess<'a, 'b, F> { 178 | type Error = Error; 179 | 180 | #[inline] 181 | fn next_key_seed>(&mut self, seed: K) -> Result> { 182 | if self.len > 0 { 183 | self.len -= 1; 184 | Ok(Some(DeserializeSeed::deserialize( 185 | seed, 186 | &mut *self.deserializer, 187 | )?)) 188 | } else { 189 | Ok(None) 190 | } 191 | } 192 | 193 | #[inline] 194 | fn next_value_seed>(&mut self, seed: V) -> Result { 195 | DeserializeSeed::deserialize(seed, &mut *self.deserializer) 196 | } 197 | 198 | #[inline] 199 | fn size_hint(&self) -> Option { 200 | Some(self.len) 201 | } 202 | } 203 | 204 | impl<'de, F: Flavor<'de>> de::Deserializer<'de> for &mut Deserializer<'de, F> { 205 | type Error = Error; 206 | 207 | #[inline] 208 | fn is_human_readable(&self) -> bool { 209 | false 210 | } 211 | 212 | // Postcard does not support structures not known at compile time 213 | #[inline] 214 | fn deserialize_any(self, _visitor: V) -> Result 215 | where 216 | V: Visitor<'de>, 217 | { 218 | // We wont ever support this. 219 | Err(Error::WontImplement) 220 | } 221 | 222 | // Take a boolean encoded as a u8 223 | #[inline] 224 | fn deserialize_bool(self, visitor: V) -> Result 225 | where 226 | V: Visitor<'de>, 227 | { 228 | let val = match self.flavor.pop()? { 229 | 0 => false, 230 | 1 => true, 231 | _ => return Err(Error::DeserializeBadBool), 232 | }; 233 | visitor.visit_bool(val) 234 | } 235 | 236 | #[inline] 237 | fn deserialize_i8(self, visitor: V) -> Result 238 | where 239 | V: Visitor<'de>, 240 | { 241 | visitor.visit_i8(self.flavor.pop()? as i8) 242 | } 243 | 244 | #[inline] 245 | fn deserialize_i16(self, visitor: V) -> Result 246 | where 247 | V: Visitor<'de>, 248 | { 249 | let v = self.try_take_varint_u16()?; 250 | visitor.visit_i16(de_zig_zag_i16(v)) 251 | } 252 | 253 | #[inline] 254 | fn deserialize_i32(self, visitor: V) -> Result 255 | where 256 | V: Visitor<'de>, 257 | { 258 | let v = self.try_take_varint_u32()?; 259 | visitor.visit_i32(de_zig_zag_i32(v)) 260 | } 261 | 262 | #[inline] 263 | fn deserialize_i64(self, visitor: V) -> Result 264 | where 265 | V: Visitor<'de>, 266 | { 267 | let v = self.try_take_varint_u64()?; 268 | visitor.visit_i64(de_zig_zag_i64(v)) 269 | } 270 | 271 | #[inline] 272 | fn deserialize_i128(self, visitor: V) -> Result 273 | where 274 | V: Visitor<'de>, 275 | { 276 | let v = self.try_take_varint_u128()?; 277 | visitor.visit_i128(de_zig_zag_i128(v)) 278 | } 279 | 280 | #[inline] 281 | fn deserialize_u8(self, visitor: V) -> Result 282 | where 283 | V: Visitor<'de>, 284 | { 285 | visitor.visit_u8(self.flavor.pop()?) 286 | } 287 | 288 | #[inline] 289 | fn deserialize_u16(self, visitor: V) -> Result 290 | where 291 | V: Visitor<'de>, 292 | { 293 | let v = self.try_take_varint_u16()?; 294 | visitor.visit_u16(v) 295 | } 296 | 297 | #[inline] 298 | fn deserialize_u32(self, visitor: V) -> Result 299 | where 300 | V: Visitor<'de>, 301 | { 302 | let v = self.try_take_varint_u32()?; 303 | visitor.visit_u32(v) 304 | } 305 | 306 | #[inline] 307 | fn deserialize_u64(self, visitor: V) -> Result 308 | where 309 | V: Visitor<'de>, 310 | { 311 | let v = self.try_take_varint_u64()?; 312 | visitor.visit_u64(v) 313 | } 314 | 315 | #[inline] 316 | fn deserialize_u128(self, visitor: V) -> Result 317 | where 318 | V: Visitor<'de>, 319 | { 320 | let v = self.try_take_varint_u128()?; 321 | visitor.visit_u128(v) 322 | } 323 | 324 | #[inline] 325 | fn deserialize_f32(self, visitor: V) -> Result 326 | where 327 | V: Visitor<'de>, 328 | { 329 | let bytes = self.flavor.try_take_n(4)?; 330 | let mut buf = [0u8; 4]; 331 | buf.copy_from_slice(bytes); 332 | visitor.visit_f32(f32::from_bits(u32::from_le_bytes(buf))) 333 | } 334 | 335 | #[inline] 336 | fn deserialize_f64(self, visitor: V) -> Result 337 | where 338 | V: Visitor<'de>, 339 | { 340 | let bytes = self.flavor.try_take_n(8)?; 341 | let mut buf = [0u8; 8]; 342 | buf.copy_from_slice(bytes); 343 | visitor.visit_f64(f64::from_bits(u64::from_le_bytes(buf))) 344 | } 345 | 346 | #[inline] 347 | fn deserialize_char(self, visitor: V) -> Result 348 | where 349 | V: Visitor<'de>, 350 | { 351 | let sz = self.try_take_varint_usize()?; 352 | if sz > 4 { 353 | return Err(Error::DeserializeBadChar); 354 | } 355 | let bytes: &'de [u8] = self.flavor.try_take_n(sz)?; 356 | // we pass the character through string conversion because 357 | // this handles transforming the array of code units to a 358 | // codepoint. we can't use char::from_u32() because it expects 359 | // an already-processed codepoint. 360 | let character = core::str::from_utf8(bytes) 361 | .map_err(|_| Error::DeserializeBadChar)? 362 | .chars() 363 | .next() 364 | .ok_or(Error::DeserializeBadChar)?; 365 | visitor.visit_char(character) 366 | } 367 | 368 | #[inline] 369 | fn deserialize_str(self, visitor: V) -> Result 370 | where 371 | V: Visitor<'de>, 372 | { 373 | let sz = self.try_take_varint_usize()?; 374 | let bytes: &'de [u8] = self.flavor.try_take_n(sz)?; 375 | let str_sl = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?; 376 | 377 | visitor.visit_borrowed_str(str_sl) 378 | } 379 | 380 | #[inline] 381 | fn deserialize_string(self, visitor: V) -> Result 382 | where 383 | V: Visitor<'de>, 384 | { 385 | self.deserialize_str(visitor) 386 | } 387 | 388 | #[inline] 389 | fn deserialize_bytes(self, visitor: V) -> Result 390 | where 391 | V: Visitor<'de>, 392 | { 393 | let sz = self.try_take_varint_usize()?; 394 | let bytes: &'de [u8] = self.flavor.try_take_n(sz)?; 395 | visitor.visit_borrowed_bytes(bytes) 396 | } 397 | 398 | #[inline] 399 | fn deserialize_byte_buf(self, visitor: V) -> Result 400 | where 401 | V: Visitor<'de>, 402 | { 403 | self.deserialize_bytes(visitor) 404 | } 405 | 406 | #[inline] 407 | fn deserialize_option(self, visitor: V) -> Result 408 | where 409 | V: Visitor<'de>, 410 | { 411 | match self.flavor.pop()? { 412 | 0 => visitor.visit_none(), 413 | 1 => visitor.visit_some(self), 414 | _ => Err(Error::DeserializeBadOption), 415 | } 416 | } 417 | 418 | // In Serde, unit means an anonymous value containing no data. 419 | // Unit is not actually encoded in Postcard. 420 | #[inline] 421 | fn deserialize_unit(self, visitor: V) -> Result 422 | where 423 | V: Visitor<'de>, 424 | { 425 | visitor.visit_unit() 426 | } 427 | 428 | // Unit struct means a named value containing no data. 429 | // Unit structs are not actually encoded in Postcard. 430 | #[inline] 431 | fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result 432 | where 433 | V: Visitor<'de>, 434 | { 435 | self.deserialize_unit(visitor) 436 | } 437 | 438 | #[inline] 439 | fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result 440 | where 441 | V: Visitor<'de>, 442 | { 443 | visitor.visit_newtype_struct(self) 444 | } 445 | 446 | #[inline] 447 | fn deserialize_seq(self, visitor: V) -> Result 448 | where 449 | V: Visitor<'de>, 450 | { 451 | let len = self.try_take_varint_usize()?; 452 | 453 | visitor.visit_seq(SeqAccess { 454 | deserializer: self, 455 | len, 456 | }) 457 | } 458 | 459 | #[inline] 460 | fn deserialize_tuple(self, len: usize, visitor: V) -> Result 461 | where 462 | V: Visitor<'de>, 463 | { 464 | visitor.visit_seq(SeqAccess { 465 | deserializer: self, 466 | len, 467 | }) 468 | } 469 | 470 | #[inline] 471 | fn deserialize_tuple_struct( 472 | self, 473 | _name: &'static str, 474 | len: usize, 475 | visitor: V, 476 | ) -> Result 477 | where 478 | V: Visitor<'de>, 479 | { 480 | self.deserialize_tuple(len, visitor) 481 | } 482 | 483 | #[inline] 484 | fn deserialize_map(self, visitor: V) -> Result 485 | where 486 | V: Visitor<'de>, 487 | { 488 | let len = self.try_take_varint_usize()?; 489 | 490 | visitor.visit_map(MapAccess { 491 | deserializer: self, 492 | len, 493 | }) 494 | } 495 | 496 | #[inline] 497 | fn deserialize_struct( 498 | self, 499 | _name: &'static str, 500 | fields: &'static [&'static str], 501 | visitor: V, 502 | ) -> Result 503 | where 504 | V: Visitor<'de>, 505 | { 506 | self.deserialize_tuple(fields.len(), visitor) 507 | } 508 | 509 | #[inline] 510 | fn deserialize_enum( 511 | self, 512 | _name: &'static str, 513 | _variants: &'static [&'static str], 514 | visitor: V, 515 | ) -> Result 516 | where 517 | V: Visitor<'de>, 518 | { 519 | visitor.visit_enum(self) 520 | } 521 | 522 | // As a binary format, Postcard does not encode identifiers 523 | #[inline] 524 | fn deserialize_identifier(self, _visitor: V) -> Result 525 | where 526 | V: Visitor<'de>, 527 | { 528 | // Will not support 529 | Err(Error::WontImplement) 530 | } 531 | 532 | #[inline] 533 | fn deserialize_ignored_any(self, _visitor: V) -> Result 534 | where 535 | V: Visitor<'de>, 536 | { 537 | // Will not support 538 | Err(Error::WontImplement) 539 | } 540 | } 541 | 542 | impl<'de, F: Flavor<'de>> serde::de::VariantAccess<'de> for &mut Deserializer<'de, F> { 543 | type Error = Error; 544 | 545 | #[inline] 546 | fn unit_variant(self) -> Result<()> { 547 | Ok(()) 548 | } 549 | 550 | #[inline] 551 | fn newtype_variant_seed>(self, seed: V) -> Result { 552 | DeserializeSeed::deserialize(seed, self) 553 | } 554 | 555 | #[inline] 556 | fn tuple_variant>(self, len: usize, visitor: V) -> Result { 557 | serde::de::Deserializer::deserialize_tuple(self, len, visitor) 558 | } 559 | 560 | #[inline] 561 | fn struct_variant>( 562 | self, 563 | fields: &'static [&'static str], 564 | visitor: V, 565 | ) -> Result { 566 | serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor) 567 | } 568 | } 569 | 570 | impl<'de, F: Flavor<'de>> serde::de::EnumAccess<'de> for &mut Deserializer<'de, F> { 571 | type Error = Error; 572 | type Variant = Self; 573 | 574 | #[inline] 575 | fn variant_seed>(self, seed: V) -> Result<(V::Value, Self)> { 576 | let varint = self.try_take_varint_u32()?; 577 | let v = DeserializeSeed::deserialize(seed, varint.into_deserializer())?; 578 | Ok((v, self)) 579 | } 580 | } 581 | 582 | fn de_zig_zag_i16(n: u16) -> i16 { 583 | ((n >> 1) as i16) ^ (-((n & 0b1) as i16)) 584 | } 585 | 586 | fn de_zig_zag_i32(n: u32) -> i32 { 587 | ((n >> 1) as i32) ^ (-((n & 0b1) as i32)) 588 | } 589 | 590 | fn de_zig_zag_i64(n: u64) -> i64 { 591 | ((n >> 1) as i64) ^ (-((n & 0b1) as i64)) 592 | } 593 | 594 | fn de_zig_zag_i128(n: u128) -> i128 { 595 | ((n >> 1) as i128) ^ (-((n & 0b1) as i128)) 596 | } 597 | -------------------------------------------------------------------------------- /source/postcard/src/eio.rs: -------------------------------------------------------------------------------- 1 | // We disable all embedded-io versions but the most recent in docs.rs, because we use 2 | // --all-features which doesn't work with non-additive features. 3 | #[cfg(all(feature = "embedded-io-04", feature = "embedded-io-06", not(docsrs)))] 4 | compile_error!("Only one version of `embedded-io` must be enabled through features"); 5 | 6 | #[cfg(all(feature = "embedded-io-04", not(docsrs)))] 7 | mod version_impl { 8 | pub use embedded_io_04 as embedded_io; 9 | pub use embedded_io_04::blocking::{Read, Write}; 10 | } 11 | 12 | #[cfg(feature = "embedded-io-06")] 13 | mod version_impl { 14 | pub use embedded_io_06 as embedded_io; 15 | pub use embedded_io_06::{Read, Write}; 16 | } 17 | 18 | // All versions should export the appropriate items 19 | #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] 20 | pub use version_impl::{embedded_io, Read, Write}; 21 | -------------------------------------------------------------------------------- /source/postcard/src/error.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::{Display, Formatter}; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// This is the error type used by Postcard 5 | #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] 6 | #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] 7 | #[non_exhaustive] 8 | pub enum Error { 9 | /// This is a feature that postcard will never implement 10 | WontImplement, 11 | /// This is a feature that postcard intends to support, but does not yet 12 | NotYetImplemented, 13 | /// The serialize buffer is full 14 | SerializeBufferFull, 15 | /// The length of a sequence must be known 16 | SerializeSeqLengthUnknown, 17 | /// Hit the end of buffer, expected more data 18 | DeserializeUnexpectedEnd, 19 | /// Found a varint that didn't terminate. Is the usize too big for this platform? 20 | DeserializeBadVarint, 21 | /// Found a bool that wasn't 0 or 1 22 | DeserializeBadBool, 23 | /// Found an invalid unicode char 24 | DeserializeBadChar, 25 | /// Tried to parse invalid utf-8 26 | DeserializeBadUtf8, 27 | /// Found an Option discriminant that wasn't 0 or 1 28 | DeserializeBadOption, 29 | /// Found an enum discriminant that was > `u32::MAX` 30 | DeserializeBadEnum, 31 | /// The original data was not well encoded 32 | DeserializeBadEncoding, 33 | /// Bad CRC while deserializing 34 | DeserializeBadCrc, 35 | /// Serde Serialization Error 36 | SerdeSerCustom, 37 | /// Serde Deserialization Error 38 | SerdeDeCustom, 39 | /// Error while processing `collect_str` during serialization 40 | CollectStrError, 41 | } 42 | 43 | impl Display for Error { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 45 | use Error::*; 46 | write!( 47 | f, 48 | "{}", 49 | match self { 50 | WontImplement => "This is a feature that PostCard will never implement", 51 | NotYetImplemented => { 52 | "This is a feature that Postcard intends to support, but does not yet" 53 | } 54 | SerializeBufferFull => "The serialize buffer is full", 55 | SerializeSeqLengthUnknown => "The length of a sequence must be known", 56 | DeserializeUnexpectedEnd => "Hit the end of buffer, expected more data", 57 | DeserializeBadVarint => { 58 | "Found a varint that didn't terminate. Is the usize too big for this platform?" 59 | } 60 | DeserializeBadBool => "Found a bool that wasn't 0 or 1", 61 | DeserializeBadChar => "Found an invalid unicode char", 62 | DeserializeBadUtf8 => "Tried to parse invalid utf-8", 63 | DeserializeBadOption => "Found an Option discriminant that wasn't 0 or 1", 64 | DeserializeBadEnum => "Found an enum discriminant that was > u32::max_value()", 65 | DeserializeBadEncoding => "The original data was not well encoded", 66 | DeserializeBadCrc => "Bad CRC while deserializing", 67 | SerdeSerCustom => "Serde Serialization Error", 68 | SerdeDeCustom => "Serde Deserialization Error", 69 | CollectStrError => "Error while processing `collect_str` during serialization", 70 | } 71 | ) 72 | } 73 | } 74 | 75 | /// This is the Result type used by Postcard. 76 | pub type Result = ::core::result::Result; 77 | 78 | impl serde::ser::Error for Error { 79 | fn custom(_msg: T) -> Self 80 | where 81 | T: Display, 82 | { 83 | Error::SerdeSerCustom 84 | } 85 | } 86 | 87 | impl serde::de::Error for Error { 88 | fn custom(_msg: T) -> Self 89 | where 90 | T: Display, 91 | { 92 | Error::SerdeDeCustom 93 | } 94 | } 95 | 96 | impl serde::ser::StdError for Error {} 97 | -------------------------------------------------------------------------------- /source/postcard/src/fixint.rs: -------------------------------------------------------------------------------- 1 | //! # Fixed Size Integers 2 | //! 3 | //! In some cases, the use of variably length encoded data may not be 4 | //! preferrable. These modules, for use with `#[serde(with = ...)]` 5 | //! "opt out" of variable length encoding. 6 | //! 7 | //! Support explicitly not provided for `usize` or `isize`, as 8 | //! these types would not be portable between systems of different 9 | //! pointer widths. 10 | //! 11 | //! Although all data in Postcard is typically encoded in little-endian 12 | //! order, these modules provide a choice to the user to encode the data 13 | //! in either little or big endian form, which may be useful for zero-copy 14 | //! applications. 15 | 16 | use serde::{Deserialize, Serialize, Serializer}; 17 | 18 | /// Use with the `#[serde(with = "postcard::fixint::le")]` field attribute. 19 | /// 20 | /// Disables varint serialization/deserialization for the specified integer 21 | /// field. The integer will always be serialized in the same way as a fixed 22 | /// size array, in **Little Endian** order on the wire. 23 | /// 24 | /// ```rust 25 | /// # use serde::Serialize; 26 | /// #[derive(Serialize)] 27 | /// pub struct DefinitelyLittleEndian { 28 | /// #[serde(with = "postcard::fixint::le")] 29 | /// x: u16, 30 | /// } 31 | /// ``` 32 | pub mod le { 33 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 34 | 35 | use super::LE; 36 | 37 | /// Serialize the integer value as a little-endian fixed-size array. 38 | pub fn serialize(val: &T, serializer: S) -> Result 39 | where 40 | S: Serializer, 41 | T: Copy, 42 | LE: Serialize, 43 | { 44 | LE(*val).serialize(serializer) 45 | } 46 | 47 | /// Deserialize the integer value from a little-endian fixed-size array. 48 | pub fn deserialize<'de, D, T>(deserializer: D) -> Result 49 | where 50 | D: Deserializer<'de>, 51 | LE: Deserialize<'de>, 52 | { 53 | LE::::deserialize(deserializer).map(|x| x.0) 54 | } 55 | } 56 | 57 | /// Disables varint serialization/deserialization for the specified integer field. 58 | /// 59 | /// Use with the `#[serde(with = "postcard::fixint::be")]` field attribute. 60 | /// The integer will always be serialized in the same way as a fixed 61 | /// size array, in **Big Endian** order on the wire. 62 | /// 63 | /// ```rust 64 | /// # use serde::Serialize; 65 | /// #[derive(Serialize)] 66 | /// pub struct DefinitelyBigEndian { 67 | /// #[serde(with = "postcard::fixint::be")] 68 | /// x: u16, 69 | /// } 70 | /// ``` 71 | pub mod be { 72 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 73 | 74 | use super::BE; 75 | 76 | /// Serialize the integer value as a big-endian fixed-size array. 77 | pub fn serialize(val: &T, serializer: S) -> Result 78 | where 79 | S: Serializer, 80 | T: Copy, 81 | BE: Serialize, 82 | { 83 | BE(*val).serialize(serializer) 84 | } 85 | 86 | /// Deserialize the integer value from a big-endian fixed-size array. 87 | pub fn deserialize<'de, D, T>(deserializer: D) -> Result 88 | where 89 | D: Deserializer<'de>, 90 | BE: Deserialize<'de>, 91 | { 92 | BE::::deserialize(deserializer).map(|x| x.0) 93 | } 94 | } 95 | 96 | #[doc(hidden)] 97 | pub struct LE(T); 98 | 99 | #[doc(hidden)] 100 | pub struct BE(T); 101 | 102 | macro_rules! impl_fixint { 103 | ($( $int:ty ),*) => { 104 | $( 105 | impl Serialize for LE<$int> { 106 | #[inline] 107 | fn serialize(&self, serializer: S) -> Result 108 | where 109 | S: Serializer, 110 | { 111 | self.0.to_le_bytes().serialize(serializer) 112 | } 113 | } 114 | 115 | impl<'de> Deserialize<'de> for LE<$int> { 116 | #[inline] 117 | fn deserialize(deserializer: D) -> Result 118 | where 119 | D: serde::Deserializer<'de>, 120 | { 121 | <_ as Deserialize>::deserialize(deserializer) 122 | .map(<$int>::from_le_bytes) 123 | .map(Self) 124 | } 125 | } 126 | 127 | impl Serialize for BE<$int> { 128 | #[inline] 129 | fn serialize(&self, serializer: S) -> Result 130 | where 131 | S: Serializer, 132 | { 133 | self.0.to_be_bytes().serialize(serializer) 134 | } 135 | } 136 | 137 | impl<'de> Deserialize<'de> for BE<$int> { 138 | #[inline] 139 | fn deserialize(deserializer: D) -> Result 140 | where 141 | D: serde::Deserializer<'de>, 142 | { 143 | <_ as Deserialize>::deserialize(deserializer) 144 | .map(<$int>::from_be_bytes) 145 | .map(Self) 146 | } 147 | } 148 | )* 149 | }; 150 | } 151 | 152 | impl_fixint![i16, i32, i64, i128, u16, u32, u64, u128]; 153 | 154 | #[cfg(test)] 155 | mod tests { 156 | use serde::{Deserialize, Serialize}; 157 | 158 | #[test] 159 | fn test_little_endian() { 160 | #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] 161 | pub struct DefinitelyLE { 162 | #[serde(with = "crate::fixint::le")] 163 | x: u16, 164 | } 165 | 166 | let input = DefinitelyLE { x: 0xABCD }; 167 | let mut buf = [0; 32]; 168 | let serialized = crate::to_slice(&input, &mut buf).unwrap(); 169 | assert_eq!(serialized, &[0xCD, 0xAB]); 170 | 171 | let deserialized: DefinitelyLE = crate::from_bytes(serialized).unwrap(); 172 | assert_eq!(deserialized, input); 173 | } 174 | 175 | #[test] 176 | fn test_big_endian() { 177 | #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] 178 | pub struct DefinitelyBE { 179 | #[serde(with = "crate::fixint::be")] 180 | x: u16, 181 | } 182 | 183 | let input = DefinitelyBE { x: 0xABCD }; 184 | let mut buf = [0; 32]; 185 | let serialized = crate::to_slice(&input, &mut buf).unwrap(); 186 | assert_eq!(serialized, &[0xAB, 0xCD]); 187 | 188 | let deserialized: DefinitelyBE = crate::from_bytes(serialized).unwrap(); 189 | assert_eq!(deserialized, input); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /source/postcard/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(not(any(test, feature = "use-std")), no_std)] 2 | #![warn(missing_docs)] 3 | #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] 4 | #![cfg_attr(docsrs, feature(doc_cfg))] 5 | 6 | pub mod accumulator; 7 | mod de; 8 | 9 | mod eio; 10 | 11 | mod error; 12 | pub mod fixint; 13 | mod ser; 14 | mod varint; 15 | 16 | // Still experimental! Don't make pub pub. 17 | pub(crate) mod max_size; 18 | 19 | /// # Experimental Postcard Features 20 | /// 21 | /// Items inside this module require various feature flags, and are not 22 | /// subject to SemVer stability. Items may be removed or deprecated at 23 | /// any point. 24 | /// 25 | /// ## Derive 26 | /// 27 | /// The `experimental-derive` feature enables one experimental feature: 28 | /// 29 | /// * Max size calculation 30 | /// 31 | /// ### Max Size Calculation 32 | /// 33 | /// This features enables calculation of the Max serialized size of a message as 34 | /// an associated `usize` constant called `POSTCARD_MAX_SIZE`. It also provides a 35 | /// `#[derive(MaxSize)]` macro that can be used for calculating user types. 36 | /// 37 | /// This is useful for determining the maximum buffer size needed when recieving 38 | /// or sending a message that has been serialized. 39 | /// 40 | /// NOTE: This only covers the size of "plain" flavored messages, e.g. not with COBS 41 | /// or any other Flavors applied. The overhead for these flavors must be calculated 42 | /// separately. 43 | /// 44 | /// Please report any missing types, or any incorrectly calculated values. 45 | /// 46 | /// ### Message Schema Generation 47 | /// 48 | /// This now lives in the `postcard-schema` crate. 49 | pub mod experimental { 50 | /// Compile time max-serialization size calculation 51 | #[cfg(feature = "experimental-derive")] 52 | #[cfg_attr(docsrs, doc(cfg(feature = "experimental-derive")))] 53 | pub mod max_size { 54 | // NOTE: This is the trait... 55 | pub use crate::max_size::MaxSize; 56 | // NOTE: ...and this is the derive macro 57 | pub use postcard_derive::MaxSize; 58 | } 59 | 60 | pub use crate::ser::serialized_size; 61 | } 62 | 63 | pub use de::deserializer::Deserializer; 64 | pub use de::flavors as de_flavors; 65 | pub use de::{from_bytes, from_bytes_cobs, take_from_bytes, take_from_bytes_cobs}; 66 | pub use error::{Error, Result}; 67 | pub use ser::flavors as ser_flavors; 68 | pub use ser::{serialize_with_flavor, serializer::Serializer, to_extend, to_slice, to_slice_cobs}; 69 | 70 | #[cfg(feature = "heapless")] 71 | pub use ser::{to_vec, to_vec_cobs}; 72 | 73 | #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] 74 | pub use ser::to_eio; 75 | 76 | #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] 77 | pub use de::from_eio; 78 | 79 | #[cfg(feature = "use-std")] 80 | pub use ser::{to_io, to_stdvec, to_stdvec_cobs}; 81 | 82 | #[cfg(feature = "use-std")] 83 | pub use de::from_io; 84 | 85 | #[cfg(feature = "alloc")] 86 | pub use ser::{to_allocvec, to_allocvec_cobs}; 87 | 88 | #[cfg(feature = "use-crc")] 89 | pub use { 90 | de::{from_bytes_crc32, take_from_bytes_crc32}, 91 | ser::to_slice_crc32, 92 | }; 93 | 94 | #[cfg(all(feature = "use-crc", feature = "heapless"))] 95 | pub use ser::to_vec_crc32; 96 | 97 | #[cfg(all(feature = "use-crc", feature = "use-std"))] 98 | pub use ser::to_stdvec_crc32; 99 | 100 | #[cfg(all(feature = "use-crc", feature = "alloc"))] 101 | pub use ser::to_allocvec_crc32; 102 | 103 | #[cfg(test)] 104 | mod test { 105 | #[test] 106 | fn varint_boundary_canon() { 107 | let x = u32::MAX; 108 | let mut buf = [0u8; 5]; 109 | let used = crate::to_slice(&x, &mut buf).unwrap(); 110 | let deser: u32 = crate::from_bytes(used).unwrap(); 111 | assert_eq!(deser, u32::MAX); 112 | assert_eq!(used, &mut [0xFF, 0xFF, 0xFF, 0xFF, 0x0F]); 113 | let deser: Result = crate::from_bytes(&[0xFF, 0xFF, 0xFF, 0xFF, 0x1F]); 114 | assert_eq!(deser, Err(crate::Error::DeserializeBadVarint)); 115 | } 116 | 117 | #[test] 118 | fn signed_int128() { 119 | let x = -19490127978232325886905073712831_i128; 120 | let mut buf = [0u8; 32]; 121 | let used = crate::to_slice(&x, &mut buf).unwrap(); 122 | let deser: i128 = crate::from_bytes(used).unwrap(); 123 | assert_eq!(deser, x); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /source/postcard/src/max_size.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "alloc")] 2 | extern crate alloc; 3 | 4 | #[cfg(feature = "alloc")] 5 | use alloc::{boxed::Box, rc::Rc}; 6 | 7 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 8 | use alloc::sync::Arc; 9 | 10 | use crate::varint::varint_max; 11 | use core::{ 12 | marker::PhantomData, 13 | num::{ 14 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 15 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, 16 | }, 17 | ops::{Range, RangeFrom, RangeInclusive, RangeTo}, 18 | }; 19 | 20 | /// This trait is used to enforce the maximum size required to 21 | /// store the serialization of a given type. 22 | pub trait MaxSize { 23 | /// The maximum possible size that the serialization of this 24 | /// type can have, in bytes. 25 | const POSTCARD_MAX_SIZE: usize; 26 | } 27 | 28 | impl MaxSize for bool { 29 | const POSTCARD_MAX_SIZE: usize = 1; 30 | } 31 | 32 | impl MaxSize for i8 { 33 | const POSTCARD_MAX_SIZE: usize = 1; 34 | } 35 | 36 | impl MaxSize for i16 { 37 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 38 | } 39 | 40 | impl MaxSize for i32 { 41 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 42 | } 43 | 44 | impl MaxSize for i64 { 45 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 46 | } 47 | 48 | impl MaxSize for i128 { 49 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 50 | } 51 | 52 | impl MaxSize for isize { 53 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 54 | } 55 | 56 | impl MaxSize for u8 { 57 | const POSTCARD_MAX_SIZE: usize = 1; 58 | } 59 | 60 | impl MaxSize for u16 { 61 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 62 | } 63 | 64 | impl MaxSize for u32 { 65 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 66 | } 67 | 68 | impl MaxSize for u64 { 69 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 70 | } 71 | 72 | impl MaxSize for u128 { 73 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 74 | } 75 | 76 | impl MaxSize for usize { 77 | const POSTCARD_MAX_SIZE: usize = varint_max::(); 78 | } 79 | 80 | impl MaxSize for f32 { 81 | const POSTCARD_MAX_SIZE: usize = 4; 82 | } 83 | 84 | impl MaxSize for f64 { 85 | const POSTCARD_MAX_SIZE: usize = 8; 86 | } 87 | 88 | impl MaxSize for char { 89 | const POSTCARD_MAX_SIZE: usize = 5; 90 | } 91 | 92 | impl MaxSize for Option { 93 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE + 1; 94 | } 95 | 96 | impl MaxSize for Result { 97 | const POSTCARD_MAX_SIZE: usize = max(T::POSTCARD_MAX_SIZE, E::POSTCARD_MAX_SIZE) + 1; 98 | } 99 | 100 | impl MaxSize for () { 101 | const POSTCARD_MAX_SIZE: usize = 0; 102 | } 103 | 104 | impl MaxSize for [T; N] { 105 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * N; 106 | } 107 | 108 | impl MaxSize for &'_ T { 109 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 110 | } 111 | 112 | impl MaxSize for &'_ mut T { 113 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 114 | } 115 | 116 | impl MaxSize for NonZeroI8 { 117 | const POSTCARD_MAX_SIZE: usize = i8::POSTCARD_MAX_SIZE; 118 | } 119 | 120 | impl MaxSize for NonZeroI16 { 121 | const POSTCARD_MAX_SIZE: usize = i16::POSTCARD_MAX_SIZE; 122 | } 123 | 124 | impl MaxSize for NonZeroI32 { 125 | const POSTCARD_MAX_SIZE: usize = i32::POSTCARD_MAX_SIZE; 126 | } 127 | 128 | impl MaxSize for NonZeroI64 { 129 | const POSTCARD_MAX_SIZE: usize = i64::POSTCARD_MAX_SIZE; 130 | } 131 | 132 | impl MaxSize for NonZeroI128 { 133 | const POSTCARD_MAX_SIZE: usize = i128::POSTCARD_MAX_SIZE; 134 | } 135 | 136 | impl MaxSize for NonZeroIsize { 137 | const POSTCARD_MAX_SIZE: usize = isize::POSTCARD_MAX_SIZE; 138 | } 139 | 140 | impl MaxSize for NonZeroU8 { 141 | const POSTCARD_MAX_SIZE: usize = u8::POSTCARD_MAX_SIZE; 142 | } 143 | 144 | impl MaxSize for NonZeroU16 { 145 | const POSTCARD_MAX_SIZE: usize = u16::POSTCARD_MAX_SIZE; 146 | } 147 | 148 | impl MaxSize for NonZeroU32 { 149 | const POSTCARD_MAX_SIZE: usize = u32::POSTCARD_MAX_SIZE; 150 | } 151 | 152 | impl MaxSize for NonZeroU64 { 153 | const POSTCARD_MAX_SIZE: usize = u64::POSTCARD_MAX_SIZE; 154 | } 155 | 156 | impl MaxSize for NonZeroU128 { 157 | const POSTCARD_MAX_SIZE: usize = u128::POSTCARD_MAX_SIZE; 158 | } 159 | 160 | impl MaxSize for NonZeroUsize { 161 | const POSTCARD_MAX_SIZE: usize = usize::POSTCARD_MAX_SIZE; 162 | } 163 | 164 | impl MaxSize for PhantomData { 165 | const POSTCARD_MAX_SIZE: usize = 0; 166 | } 167 | 168 | impl MaxSize for (A,) { 169 | const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE; 170 | } 171 | 172 | impl MaxSize for (A, B) { 173 | const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE; 174 | } 175 | 176 | impl MaxSize for (A, B, C) { 177 | const POSTCARD_MAX_SIZE: usize = 178 | A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE; 179 | } 180 | 181 | impl MaxSize for (A, B, C, D) { 182 | const POSTCARD_MAX_SIZE: usize = 183 | A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE + D::POSTCARD_MAX_SIZE; 184 | } 185 | 186 | impl MaxSize for (A, B, C, D, E) { 187 | const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE 188 | + B::POSTCARD_MAX_SIZE 189 | + C::POSTCARD_MAX_SIZE 190 | + D::POSTCARD_MAX_SIZE 191 | + E::POSTCARD_MAX_SIZE; 192 | } 193 | 194 | impl MaxSize 195 | for (A, B, C, D, E, F) 196 | { 197 | const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE 198 | + B::POSTCARD_MAX_SIZE 199 | + C::POSTCARD_MAX_SIZE 200 | + D::POSTCARD_MAX_SIZE 201 | + E::POSTCARD_MAX_SIZE 202 | + F::POSTCARD_MAX_SIZE; 203 | } 204 | 205 | impl MaxSize for Range { 206 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2; 207 | } 208 | 209 | impl MaxSize for RangeInclusive { 210 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2; 211 | } 212 | 213 | impl MaxSize for RangeFrom { 214 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 215 | } 216 | 217 | impl MaxSize for RangeTo { 218 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 219 | } 220 | 221 | #[cfg(feature = "alloc")] 222 | #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 223 | impl MaxSize for Box { 224 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 225 | } 226 | 227 | #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] 228 | #[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))] 229 | impl MaxSize for Arc { 230 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 231 | } 232 | 233 | #[cfg(feature = "alloc")] 234 | #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 235 | impl MaxSize for Rc { 236 | const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; 237 | } 238 | 239 | #[cfg(feature = "heapless")] 240 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] 241 | impl MaxSize for heapless::Vec { 242 | const POSTCARD_MAX_SIZE: usize = <[T; N]>::POSTCARD_MAX_SIZE + varint_size(N); 243 | } 244 | 245 | #[cfg(feature = "heapless")] 246 | #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] 247 | impl MaxSize for heapless::String { 248 | const POSTCARD_MAX_SIZE: usize = <[u8; N]>::POSTCARD_MAX_SIZE + varint_size(N); 249 | } 250 | 251 | #[cfg(feature = "heapless")] 252 | const fn varint_size(max_n: usize) -> usize { 253 | const BITS_PER_BYTE: usize = 8; 254 | const BITS_PER_VARINT_BYTE: usize = 7; 255 | 256 | if max_n == 0 { 257 | return 1; 258 | } 259 | 260 | // How many data bits do we need for `max_n`. 261 | let bits = core::mem::size_of::() * BITS_PER_BYTE - max_n.leading_zeros() as usize; 262 | 263 | // We add (BITS_PER_BYTE - 1), to ensure any integer divisions 264 | // with a remainder will always add exactly one full byte, but 265 | // an evenly divided number of bits will be the same 266 | let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1); 267 | 268 | // Apply division, using normal "round down" integer division 269 | roundup_bits / BITS_PER_VARINT_BYTE 270 | } 271 | 272 | const fn max(lhs: usize, rhs: usize) -> usize { 273 | if lhs > rhs { 274 | lhs 275 | } else { 276 | rhs 277 | } 278 | } 279 | 280 | #[cfg(any(feature = "alloc", feature = "use-std"))] 281 | #[cfg(test)] 282 | mod tests { 283 | extern crate alloc; 284 | 285 | use super::*; 286 | use alloc::rc::Rc; 287 | 288 | #[cfg(target_has_atomic = "ptr")] 289 | use alloc::sync::Arc; 290 | 291 | #[test] 292 | fn box_max_size() { 293 | assert_eq!(Box::::POSTCARD_MAX_SIZE, 1); 294 | assert_eq!(Box::::POSTCARD_MAX_SIZE, 5); 295 | assert_eq!(Box::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); 296 | } 297 | 298 | #[test] 299 | #[cfg(target_has_atomic = "ptr")] 300 | fn arc_max_size() { 301 | assert_eq!(Arc::::POSTCARD_MAX_SIZE, 1); 302 | assert_eq!(Arc::::POSTCARD_MAX_SIZE, 5); 303 | assert_eq!(Arc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); 304 | } 305 | 306 | #[test] 307 | fn rc_max_size() { 308 | assert_eq!(Rc::::POSTCARD_MAX_SIZE, 1); 309 | assert_eq!(Rc::::POSTCARD_MAX_SIZE, 5); 310 | assert_eq!(Rc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /source/postcard/src/ser/serializer.rs: -------------------------------------------------------------------------------- 1 | use serde::{ser, Serialize}; 2 | 3 | use crate::error::{Error, Result}; 4 | use crate::ser::flavors::Flavor; 5 | use crate::varint::*; 6 | 7 | /// A `serde` compatible serializer, generic over "Flavors" of serializing plugins. 8 | /// 9 | /// It should rarely be necessary to directly use this type unless you are implementing your 10 | /// own [`SerFlavor`]. 11 | /// 12 | /// See the docs for [`SerFlavor`] for more information about "flavors" of serialization 13 | /// 14 | /// [`SerFlavor`]: crate::ser_flavors::Flavor 15 | pub struct Serializer 16 | where 17 | F: Flavor, 18 | { 19 | /// This is the Flavor(s) that will be used to modify or store any bytes generated 20 | /// by serialization 21 | pub output: F, 22 | } 23 | 24 | impl Serializer { 25 | /// Attempt to push a variably encoded [usize] into the output data stream 26 | #[inline] 27 | pub(crate) fn try_push_varint_usize(&mut self, data: usize) -> Result<()> { 28 | let mut buf = [0u8; varint_max::()]; 29 | let used_buf = varint_usize(data, &mut buf); 30 | self.output.try_extend(used_buf) 31 | } 32 | 33 | /// Attempt to push a variably encoded [u128] into the output data stream 34 | #[inline] 35 | pub(crate) fn try_push_varint_u128(&mut self, data: u128) -> Result<()> { 36 | let mut buf = [0u8; varint_max::()]; 37 | let used_buf = varint_u128(data, &mut buf); 38 | self.output.try_extend(used_buf) 39 | } 40 | 41 | /// Attempt to push a variably encoded [u64] into the output data stream 42 | #[inline] 43 | pub(crate) fn try_push_varint_u64(&mut self, data: u64) -> Result<()> { 44 | let mut buf = [0u8; varint_max::()]; 45 | let used_buf = varint_u64(data, &mut buf); 46 | self.output.try_extend(used_buf) 47 | } 48 | 49 | /// Attempt to push a variably encoded [u32] into the output data stream 50 | #[inline] 51 | pub(crate) fn try_push_varint_u32(&mut self, data: u32) -> Result<()> { 52 | let mut buf = [0u8; varint_max::()]; 53 | let used_buf = varint_u32(data, &mut buf); 54 | self.output.try_extend(used_buf) 55 | } 56 | 57 | /// Attempt to push a variably encoded [u16] into the output data stream 58 | #[inline] 59 | pub(crate) fn try_push_varint_u16(&mut self, data: u16) -> Result<()> { 60 | let mut buf = [0u8; varint_max::()]; 61 | let used_buf = varint_u16(data, &mut buf); 62 | self.output.try_extend(used_buf) 63 | } 64 | } 65 | 66 | impl ser::Serializer for &mut Serializer 67 | where 68 | F: Flavor, 69 | { 70 | type Ok = (); 71 | 72 | type Error = Error; 73 | 74 | // Associated types for keeping track of additional state while serializing 75 | // compound data structures like sequences and maps. In this case no 76 | // additional state is required beyond what is already stored in the 77 | // Serializer struct. 78 | type SerializeSeq = Self; 79 | type SerializeTuple = Self; 80 | type SerializeTupleStruct = Self; 81 | type SerializeTupleVariant = Self; 82 | type SerializeMap = Self; 83 | type SerializeStruct = Self; 84 | type SerializeStructVariant = Self; 85 | 86 | #[inline] 87 | fn is_human_readable(&self) -> bool { 88 | false 89 | } 90 | 91 | #[inline] 92 | fn serialize_bool(self, v: bool) -> Result<()> { 93 | self.serialize_u8(if v { 1 } else { 0 }) 94 | } 95 | 96 | #[inline] 97 | fn serialize_i8(self, v: i8) -> Result<()> { 98 | self.serialize_u8(v.to_le_bytes()[0]) 99 | } 100 | 101 | #[inline] 102 | fn serialize_i16(self, v: i16) -> Result<()> { 103 | let zzv = zig_zag_i16(v); 104 | self.try_push_varint_u16(zzv) 105 | .map_err(|_| Error::SerializeBufferFull) 106 | } 107 | 108 | #[inline] 109 | fn serialize_i32(self, v: i32) -> Result<()> { 110 | let zzv = zig_zag_i32(v); 111 | self.try_push_varint_u32(zzv) 112 | .map_err(|_| Error::SerializeBufferFull) 113 | } 114 | 115 | #[inline] 116 | fn serialize_i64(self, v: i64) -> Result<()> { 117 | let zzv = zig_zag_i64(v); 118 | self.try_push_varint_u64(zzv) 119 | .map_err(|_| Error::SerializeBufferFull) 120 | } 121 | 122 | #[inline] 123 | fn serialize_i128(self, v: i128) -> Result<()> { 124 | let zzv = zig_zag_i128(v); 125 | self.try_push_varint_u128(zzv) 126 | .map_err(|_| Error::SerializeBufferFull) 127 | } 128 | 129 | #[inline] 130 | fn serialize_u8(self, v: u8) -> Result<()> { 131 | self.output 132 | .try_push(v) 133 | .map_err(|_| Error::SerializeBufferFull) 134 | } 135 | 136 | #[inline] 137 | fn serialize_u16(self, v: u16) -> Result<()> { 138 | self.try_push_varint_u16(v) 139 | .map_err(|_| Error::SerializeBufferFull) 140 | } 141 | 142 | #[inline] 143 | fn serialize_u32(self, v: u32) -> Result<()> { 144 | self.try_push_varint_u32(v) 145 | .map_err(|_| Error::SerializeBufferFull) 146 | } 147 | 148 | #[inline] 149 | fn serialize_u64(self, v: u64) -> Result<()> { 150 | self.try_push_varint_u64(v) 151 | .map_err(|_| Error::SerializeBufferFull) 152 | } 153 | 154 | #[inline] 155 | fn serialize_u128(self, v: u128) -> Result<()> { 156 | self.try_push_varint_u128(v) 157 | .map_err(|_| Error::SerializeBufferFull) 158 | } 159 | 160 | #[inline] 161 | fn serialize_f32(self, v: f32) -> Result<()> { 162 | let buf = v.to_bits().to_le_bytes(); 163 | self.output 164 | .try_extend(&buf) 165 | .map_err(|_| Error::SerializeBufferFull) 166 | } 167 | 168 | #[inline] 169 | fn serialize_f64(self, v: f64) -> Result<()> { 170 | let buf = v.to_bits().to_le_bytes(); 171 | self.output 172 | .try_extend(&buf) 173 | .map_err(|_| Error::SerializeBufferFull) 174 | } 175 | 176 | #[inline] 177 | fn serialize_char(self, v: char) -> Result<()> { 178 | let mut buf = [0u8; 4]; 179 | let strsl = v.encode_utf8(&mut buf); 180 | strsl.serialize(self) 181 | } 182 | 183 | #[inline] 184 | fn serialize_str(self, v: &str) -> Result<()> { 185 | self.try_push_varint_usize(v.len()) 186 | .map_err(|_| Error::SerializeBufferFull)?; 187 | self.output 188 | .try_extend(v.as_bytes()) 189 | .map_err(|_| Error::SerializeBufferFull)?; 190 | Ok(()) 191 | } 192 | 193 | #[inline] 194 | fn serialize_bytes(self, v: &[u8]) -> Result<()> { 195 | self.try_push_varint_usize(v.len()) 196 | .map_err(|_| Error::SerializeBufferFull)?; 197 | self.output 198 | .try_extend(v) 199 | .map_err(|_| Error::SerializeBufferFull) 200 | } 201 | 202 | #[inline] 203 | fn serialize_none(self) -> Result<()> { 204 | self.serialize_u8(0) 205 | } 206 | 207 | #[inline] 208 | fn serialize_some(self, value: &T) -> Result<()> 209 | where 210 | T: ?Sized + Serialize, 211 | { 212 | self.serialize_u8(1)?; 213 | value.serialize(self) 214 | } 215 | 216 | #[inline] 217 | fn serialize_unit(self) -> Result<()> { 218 | Ok(()) 219 | } 220 | 221 | #[inline] 222 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 223 | Ok(()) 224 | } 225 | 226 | #[inline] 227 | fn serialize_unit_variant( 228 | self, 229 | _name: &'static str, 230 | variant_index: u32, 231 | _variant: &'static str, 232 | ) -> Result<()> { 233 | self.try_push_varint_u32(variant_index) 234 | .map_err(|_| Error::SerializeBufferFull) 235 | } 236 | 237 | #[inline] 238 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> 239 | where 240 | T: ?Sized + Serialize, 241 | { 242 | value.serialize(self) 243 | } 244 | 245 | #[inline] 246 | fn serialize_newtype_variant( 247 | self, 248 | _name: &'static str, 249 | variant_index: u32, 250 | _variant: &'static str, 251 | value: &T, 252 | ) -> Result<()> 253 | where 254 | T: ?Sized + Serialize, 255 | { 256 | self.try_push_varint_u32(variant_index) 257 | .map_err(|_| Error::SerializeBufferFull)?; 258 | value.serialize(self) 259 | } 260 | 261 | #[inline] 262 | fn serialize_seq(self, len: Option) -> Result { 263 | self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?) 264 | .map_err(|_| Error::SerializeBufferFull)?; 265 | Ok(self) 266 | } 267 | 268 | #[inline] 269 | fn serialize_tuple(self, _len: usize) -> Result { 270 | Ok(self) 271 | } 272 | 273 | #[inline] 274 | fn serialize_tuple_struct( 275 | self, 276 | _name: &'static str, 277 | _len: usize, 278 | ) -> Result { 279 | Ok(self) 280 | } 281 | 282 | #[inline] 283 | fn serialize_tuple_variant( 284 | self, 285 | _name: &'static str, 286 | variant_index: u32, 287 | _variant: &'static str, 288 | _len: usize, 289 | ) -> Result { 290 | self.try_push_varint_u32(variant_index) 291 | .map_err(|_| Error::SerializeBufferFull)?; 292 | Ok(self) 293 | } 294 | 295 | #[inline] 296 | fn serialize_map(self, len: Option) -> Result { 297 | self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?) 298 | .map_err(|_| Error::SerializeBufferFull)?; 299 | Ok(self) 300 | } 301 | 302 | #[inline] 303 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 304 | Ok(self) 305 | } 306 | 307 | #[inline] 308 | fn serialize_struct_variant( 309 | self, 310 | _name: &'static str, 311 | variant_index: u32, 312 | _variant: &'static str, 313 | _len: usize, 314 | ) -> Result { 315 | self.try_push_varint_u32(variant_index) 316 | .map_err(|_| Error::SerializeBufferFull)?; 317 | Ok(self) 318 | } 319 | 320 | #[inline] 321 | fn collect_str(self, value: &T) -> Result 322 | where 323 | T: core::fmt::Display + ?Sized, 324 | { 325 | use core::fmt::Write; 326 | 327 | // Unfortunately, we need to know the size of the serialized data before 328 | // we can place it into the output. In order to do this, we run the formatting 329 | // of the output data TWICE, the first time to determine the length, the 330 | // second time to actually format the data 331 | // 332 | // There are potentially other ways to do this, such as: 333 | // 334 | // * Reserving a fixed max size, such as 5 bytes, for the length field, and 335 | // leaving non-canonical trailing zeroes at the end. This would work up 336 | // to some reasonable length, but might have some portability vs max size 337 | // tradeoffs, e.g. 64KiB if we pick 3 bytes, or 4GiB if we pick 5 bytes 338 | // * Expose some kind of "memmove" capability to flavors, to allow us to 339 | // format into the buffer, then "scoot over" that many times. 340 | // 341 | // Despite the current approaches downside in speed, it is likely flexible 342 | // enough for the rare-ish case where formatting a Debug impl is necessary. 343 | // This is better than the previous panicking behavior, and can be improved 344 | // in the future. 345 | struct CountWriter { 346 | ct: usize, 347 | } 348 | impl Write for CountWriter { 349 | fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { 350 | self.ct += s.len(); 351 | Ok(()) 352 | } 353 | } 354 | 355 | let mut ctr = CountWriter { ct: 0 }; 356 | 357 | // This is the first pass through, where we just count the length of the 358 | // data that we are given 359 | write!(&mut ctr, "{value}").map_err(|_| Error::CollectStrError)?; 360 | let len = ctr.ct; 361 | self.try_push_varint_usize(len) 362 | .map_err(|_| Error::SerializeBufferFull)?; 363 | 364 | struct FmtWriter<'a, IF> 365 | where 366 | IF: Flavor, 367 | { 368 | output: &'a mut IF, 369 | } 370 | impl Write for FmtWriter<'_, IF> 371 | where 372 | IF: Flavor, 373 | { 374 | fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { 375 | self.output 376 | .try_extend(s.as_bytes()) 377 | .map_err(|_| core::fmt::Error) 378 | } 379 | } 380 | 381 | // This second pass actually inserts the data. 382 | let mut fw = FmtWriter { 383 | output: &mut self.output, 384 | }; 385 | write!(&mut fw, "{value}").map_err(|_| Error::CollectStrError)?; 386 | 387 | Ok(()) 388 | } 389 | } 390 | 391 | impl ser::SerializeSeq for &mut Serializer 392 | where 393 | F: Flavor, 394 | { 395 | // Must match the `Ok` type of the serializer. 396 | type Ok = (); 397 | // Must match the `Error` type of the serializer. 398 | type Error = Error; 399 | 400 | // Serialize a single element of the sequence. 401 | #[inline] 402 | fn serialize_element(&mut self, value: &T) -> Result<()> 403 | where 404 | T: ?Sized + Serialize, 405 | { 406 | value.serialize(&mut **self) 407 | } 408 | 409 | // Close the sequence. 410 | #[inline] 411 | fn end(self) -> Result<()> { 412 | Ok(()) 413 | } 414 | } 415 | 416 | impl ser::SerializeTuple for &mut Serializer 417 | where 418 | F: Flavor, 419 | { 420 | type Ok = (); 421 | type Error = Error; 422 | 423 | #[inline] 424 | fn serialize_element(&mut self, value: &T) -> Result<()> 425 | where 426 | T: ?Sized + Serialize, 427 | { 428 | value.serialize(&mut **self) 429 | } 430 | 431 | #[inline] 432 | fn end(self) -> Result<()> { 433 | Ok(()) 434 | } 435 | } 436 | 437 | impl ser::SerializeTupleStruct for &mut Serializer 438 | where 439 | F: Flavor, 440 | { 441 | type Ok = (); 442 | type Error = Error; 443 | 444 | #[inline] 445 | fn serialize_field(&mut self, value: &T) -> Result<()> 446 | where 447 | T: ?Sized + Serialize, 448 | { 449 | value.serialize(&mut **self) 450 | } 451 | 452 | #[inline] 453 | fn end(self) -> Result<()> { 454 | Ok(()) 455 | } 456 | } 457 | 458 | impl ser::SerializeTupleVariant for &mut Serializer 459 | where 460 | F: Flavor, 461 | { 462 | type Ok = (); 463 | type Error = Error; 464 | 465 | #[inline] 466 | fn serialize_field(&mut self, value: &T) -> Result<()> 467 | where 468 | T: ?Sized + Serialize, 469 | { 470 | value.serialize(&mut **self) 471 | } 472 | 473 | #[inline] 474 | fn end(self) -> Result<()> { 475 | Ok(()) 476 | } 477 | } 478 | 479 | impl ser::SerializeMap for &mut Serializer 480 | where 481 | F: Flavor, 482 | { 483 | type Ok = (); 484 | type Error = Error; 485 | 486 | #[inline] 487 | fn serialize_key(&mut self, key: &T) -> Result<()> 488 | where 489 | T: ?Sized + Serialize, 490 | { 491 | key.serialize(&mut **self) 492 | } 493 | 494 | #[inline] 495 | fn serialize_value(&mut self, value: &T) -> Result<()> 496 | where 497 | T: ?Sized + Serialize, 498 | { 499 | value.serialize(&mut **self) 500 | } 501 | 502 | #[inline] 503 | fn end(self) -> Result<()> { 504 | Ok(()) 505 | } 506 | } 507 | 508 | impl ser::SerializeStruct for &mut Serializer 509 | where 510 | F: Flavor, 511 | { 512 | type Ok = (); 513 | type Error = Error; 514 | 515 | #[inline] 516 | fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> 517 | where 518 | T: ?Sized + Serialize, 519 | { 520 | value.serialize(&mut **self) 521 | } 522 | 523 | #[inline] 524 | fn end(self) -> Result<()> { 525 | Ok(()) 526 | } 527 | } 528 | 529 | impl ser::SerializeStructVariant for &mut Serializer 530 | where 531 | F: Flavor, 532 | { 533 | type Ok = (); 534 | type Error = Error; 535 | 536 | #[inline] 537 | fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> 538 | where 539 | T: ?Sized + Serialize, 540 | { 541 | value.serialize(&mut **self) 542 | } 543 | 544 | #[inline] 545 | fn end(self) -> Result<()> { 546 | Ok(()) 547 | } 548 | } 549 | 550 | fn zig_zag_i16(n: i16) -> u16 { 551 | ((n << 1) ^ (n >> 15)) as u16 552 | } 553 | 554 | fn zig_zag_i32(n: i32) -> u32 { 555 | ((n << 1) ^ (n >> 31)) as u32 556 | } 557 | 558 | fn zig_zag_i64(n: i64) -> u64 { 559 | ((n << 1) ^ (n >> 63)) as u64 560 | } 561 | 562 | fn zig_zag_i128(n: i128) -> u128 { 563 | ((n << 1) ^ (n >> 127)) as u128 564 | } 565 | -------------------------------------------------------------------------------- /source/postcard/src/varint.rs: -------------------------------------------------------------------------------- 1 | /// Returns the maximum number of bytes required to encode T. 2 | pub const fn varint_max() -> usize { 3 | const BITS_PER_BYTE: usize = 8; 4 | const BITS_PER_VARINT_BYTE: usize = 7; 5 | 6 | // How many data bits do we need for this type? 7 | let bits = core::mem::size_of::() * BITS_PER_BYTE; 8 | 9 | // We add (BITS_PER_VARINT_BYTE - 1), to ensure any integer divisions 10 | // with a remainder will always add exactly one full byte, but 11 | // an evenly divided number of bits will be the same 12 | let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1); 13 | 14 | // Apply division, using normal "round down" integer division 15 | roundup_bits / BITS_PER_VARINT_BYTE 16 | } 17 | 18 | /// Returns the maximum value stored in the last encoded byte. 19 | pub const fn max_of_last_byte() -> u8 { 20 | let max_bits = core::mem::size_of::() * 8; 21 | let extra_bits = max_bits % 7; 22 | (1 << extra_bits) - 1 23 | } 24 | 25 | #[inline] 26 | pub fn varint_usize(n: usize, out: &mut [u8; varint_max::()]) -> &mut [u8] { 27 | let mut value = n; 28 | for i in 0..varint_max::() { 29 | out[i] = value.to_le_bytes()[0]; 30 | if value < 128 { 31 | return &mut out[..=i]; 32 | } 33 | 34 | out[i] |= 0x80; 35 | value >>= 7; 36 | } 37 | debug_assert_eq!(value, 0); 38 | &mut out[..] 39 | } 40 | 41 | #[inline] 42 | pub fn varint_u16(n: u16, out: &mut [u8; varint_max::()]) -> &mut [u8] { 43 | let mut value = n; 44 | for i in 0..varint_max::() { 45 | out[i] = value.to_le_bytes()[0]; 46 | if value < 128 { 47 | return &mut out[..=i]; 48 | } 49 | 50 | out[i] |= 0x80; 51 | value >>= 7; 52 | } 53 | debug_assert_eq!(value, 0); 54 | &mut out[..] 55 | } 56 | 57 | #[inline] 58 | pub fn varint_u32(n: u32, out: &mut [u8; varint_max::()]) -> &mut [u8] { 59 | let mut value = n; 60 | for i in 0..varint_max::() { 61 | out[i] = value.to_le_bytes()[0]; 62 | if value < 128 { 63 | return &mut out[..=i]; 64 | } 65 | 66 | out[i] |= 0x80; 67 | value >>= 7; 68 | } 69 | debug_assert_eq!(value, 0); 70 | &mut out[..] 71 | } 72 | 73 | #[inline] 74 | pub fn varint_u64(n: u64, out: &mut [u8; varint_max::()]) -> &mut [u8] { 75 | let mut value = n; 76 | for i in 0..varint_max::() { 77 | out[i] = value.to_le_bytes()[0]; 78 | if value < 128 { 79 | return &mut out[..=i]; 80 | } 81 | 82 | out[i] |= 0x80; 83 | value >>= 7; 84 | } 85 | debug_assert_eq!(value, 0); 86 | &mut out[..] 87 | } 88 | 89 | #[inline] 90 | pub fn varint_u128(n: u128, out: &mut [u8; varint_max::()]) -> &mut [u8] { 91 | let mut value = n; 92 | for i in 0..varint_max::() { 93 | out[i] = value.to_le_bytes()[0]; 94 | if value < 128 { 95 | return &mut out[..=i]; 96 | } 97 | 98 | out[i] |= 0x80; 99 | value >>= 7; 100 | } 101 | debug_assert_eq!(value, 0); 102 | &mut out[..] 103 | } 104 | -------------------------------------------------------------------------------- /source/postcard/tests/accumulator.rs: -------------------------------------------------------------------------------- 1 | use postcard::accumulator::{CobsAccumulator, FeedResult}; 2 | use serde::{Deserialize, Serialize}; 3 | use std::io::Read; 4 | 5 | // Read a "huge" serialized struct in 32 byte chunks into a 256 byte buffer and deserialize it. 6 | #[test] 7 | fn reader() { 8 | let mut raw_buf = [0u8; 32]; 9 | let mut input_buf = [0u8; 256]; 10 | let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new(); 11 | 12 | #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] 13 | struct Huge { 14 | a: u32, 15 | b: [u32; 32], 16 | d: u64, 17 | } 18 | 19 | let expected = Huge { 20 | a: 0xabcdef00, 21 | b: [0x01234567; 32], 22 | d: u64::MAX, 23 | }; 24 | 25 | let input = postcard::to_slice_cobs(&expected, &mut input_buf).unwrap(); 26 | // TODO(https://github.com/rust-lang/rust-clippy/issues/12751): Remove once fixed. 27 | #[allow(clippy::redundant_slicing)] 28 | let mut input = &input[..]; 29 | 30 | // Magic number from serializing struct and printing length 31 | assert_eq!(input.len(), 145); 32 | 33 | let mut output = None; 34 | 35 | while let Ok(ct) = input.read(&mut raw_buf) { 36 | // Finished reading input 37 | if ct == 0 { 38 | break; 39 | } 40 | 41 | let buf = &raw_buf[..ct]; 42 | let mut window = buf; 43 | 44 | 'cobs: while !window.is_empty() { 45 | window = match cobs_buf.feed::(window) { 46 | FeedResult::Consumed => break 'cobs, 47 | FeedResult::OverFull(new_wind) => new_wind, 48 | FeedResult::DeserError(new_wind) => new_wind, 49 | FeedResult::Success { data, remaining } => { 50 | output = Some(data); 51 | 52 | remaining 53 | } 54 | }; 55 | } 56 | } 57 | 58 | assert_eq!(output.unwrap(), expected); 59 | } 60 | -------------------------------------------------------------------------------- /source/postcard/tests/crc.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | #[cfg(feature = "use-crc")] 3 | fn test_crc() { 4 | use crc::{Crc, CRC_32_ISCSI}; 5 | 6 | let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; 7 | let buffer = &mut [0u8; 32]; 8 | let crc = Crc::::new(&CRC_32_ISCSI); 9 | let digest = crc.digest(); 10 | let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); 11 | assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); 12 | 13 | let digest = crc.digest(); 14 | let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest).unwrap(); 15 | 16 | let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; 17 | let remaining_bytes = []; 18 | assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); 19 | } 20 | 21 | #[test] 22 | #[cfg(feature = "use-crc")] 23 | fn test_crc_8() { 24 | use crc::{Crc, CRC_8_SMBUS}; 25 | 26 | let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; 27 | let buffer = &mut [0u8; 32]; 28 | let crc = Crc::::new(&CRC_8_SMBUS); 29 | let digest = crc.digest(); 30 | let res = postcard::ser_flavors::crc::to_slice_u8(data, buffer, digest).unwrap(); 31 | assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 167]); 32 | 33 | let digest = crc.digest(); 34 | let res = postcard::de_flavors::crc::take_from_bytes_u8::<[u8; 5]>(res, digest).unwrap(); 35 | 36 | let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; 37 | let remaining_bytes = []; 38 | assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); 39 | } 40 | 41 | #[test] 42 | #[cfg(feature = "use-crc")] 43 | fn test_crc_error() { 44 | use crc::{Crc, CRC_32_ISCSI}; 45 | 46 | let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; 47 | let buffer = &mut [0u8; 32]; 48 | let crc = Crc::::new(&CRC_32_ISCSI); 49 | let digest = crc.digest(); 50 | let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); 51 | 52 | // intentionally corrupt the crc 53 | let last = res.len() - 1; 54 | res[last] = 0; 55 | 56 | let digest = crc.digest(); 57 | let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest); 58 | 59 | assert_eq!(res, Err(postcard::Error::DeserializeBadCrc)); 60 | } 61 | 62 | #[test] 63 | #[cfg(feature = "use-crc")] 64 | fn test_crc_in_method() { 65 | use crc::{Crc, CRC_32_ISCSI}; 66 | use postcard::{to_slice_crc32, Result}; 67 | use serde::Serialize; 68 | 69 | #[derive(Debug, Serialize)] 70 | pub struct Thing { 71 | value: u32, 72 | } 73 | 74 | impl Thing { 75 | pub fn to_bytes<'a>(&self, buf: &'a mut [u8]) -> Result<&'a mut [u8]> { 76 | let crc = Crc::::new(&CRC_32_ISCSI); 77 | to_slice_crc32(self, buf, crc.digest()) 78 | } 79 | } 80 | 81 | let buffer = &mut [0u8; 5]; 82 | let thing = Thing { value: 42 }; 83 | let slice = thing.to_bytes(buffer).unwrap(); 84 | assert_eq!(slice, &[0x2A, 0xB7, 0xF5, 0x22, 0x19]); 85 | } 86 | -------------------------------------------------------------------------------- /source/postcard/tests/loopback.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | use core::fmt::Write; 3 | use core::ops::Deref; 4 | 5 | #[cfg(feature = "heapless")] 6 | use heapless::{FnvIndexMap, String, Vec}; 7 | 8 | #[cfg(feature = "heapless")] 9 | use postcard::to_vec; 10 | 11 | use postcard::from_bytes; 12 | use serde::de::DeserializeOwned; 13 | use serde::{Deserialize, Serialize}; 14 | 15 | #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] 16 | struct BasicU8S { 17 | st: u16, 18 | ei: u8, 19 | sf: u64, 20 | tt: u32, 21 | } 22 | 23 | #[allow(dead_code)] 24 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 25 | enum BasicEnum { 26 | Bib, 27 | Bim, 28 | Bap, 29 | } 30 | 31 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 32 | struct EnumStruct { 33 | eight: u8, 34 | sixt: u16, 35 | } 36 | 37 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 38 | enum DataEnum { 39 | Bib(u16), 40 | Bim(u64), 41 | Bap(u8), 42 | Kim(EnumStruct), 43 | Chi { a: u8, b: u32 }, 44 | Sho(u16, u8), 45 | } 46 | 47 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 48 | struct NewTypeStruct(u32); 49 | 50 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 51 | struct TupleStruct((u8, u16)); 52 | 53 | #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] 54 | struct RefStruct<'a> { 55 | bytes: &'a [u8], 56 | str_s: &'a str, 57 | } 58 | 59 | #[cfg(feature = "heapless")] 60 | #[test] 61 | fn loopback() { 62 | // Basic types 63 | test_one((), &[]); 64 | test_one(false, &[0x00]); 65 | test_one(true, &[0x01]); 66 | test_one(5u8, &[0x05]); 67 | test_one(0xA5C7u16, &[0xC7, 0xCB, 0x02]); 68 | test_one(0xCDAB3412u32, &[0x92, 0xE8, 0xAC, 0xED, 0x0C]); 69 | test_one( 70 | 0x1234_5678_90AB_CDEFu64, 71 | &[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12], 72 | ); 73 | 74 | // https://github.com/jamesmunns/postcard/pull/83 75 | test_one(32767i16, &[0xFE, 0xFF, 0x03]); 76 | test_one(-32768i16, &[0xFF, 0xFF, 0x03]); 77 | 78 | // chars 79 | test_one('z', &[0x01, 0x7a]); 80 | test_one('¢', &[0x02, 0xc2, 0xa2]); 81 | test_one('𐍈', &[0x04, 0xF0, 0x90, 0x8D, 0x88]); 82 | test_one('🥺', &[0x04, 0xF0, 0x9F, 0xA5, 0xBA]); 83 | 84 | // Structs 85 | test_one( 86 | BasicU8S { 87 | st: 0xABCD, 88 | ei: 0xFE, 89 | sf: 0x1234_4321_ABCD_DCBA, 90 | tt: 0xACAC_ACAC, 91 | }, 92 | &[ 93 | 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 94 | 0xD9, 0xB2, 0xE5, 0x0A, 95 | ], 96 | ); 97 | 98 | // Enums! 99 | test_one(BasicEnum::Bim, &[0x01]); 100 | test_one( 101 | DataEnum::Bim(u64::MAX), 102 | &[ 103 | 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 104 | ], 105 | ); 106 | test_one(DataEnum::Bib(u16::MAX), &[0x00, 0xFF, 0xFF, 0x03]); 107 | test_one(DataEnum::Bap(u8::MAX), &[0x02, 0xFF]); 108 | test_one( 109 | DataEnum::Kim(EnumStruct { 110 | eight: 0xF0, 111 | sixt: 0xACAC, 112 | }), 113 | &[0x03, 0xF0, 0xAC, 0xD9, 0x02], 114 | ); 115 | test_one( 116 | DataEnum::Chi { 117 | a: 0x0F, 118 | b: 0xC7C7C7C7, 119 | }, 120 | &[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], 121 | ); 122 | test_one(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); 123 | 124 | // Tuples! 125 | test_one((0x12u8, 0xC7A5u16), &[0x12, 0xA5, 0x8F, 0x03]); 126 | 127 | // Structs! 128 | test_one(NewTypeStruct(5), &[0x05]); 129 | test_one(TupleStruct((0xA0, 0x1234)), &[0xA0, 0xB4, 0x24]); 130 | 131 | let mut input: Vec = Vec::new(); 132 | input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); 133 | test_one(input, &[0x04, 0x01, 0x02, 0x03, 0x04]); 134 | 135 | let mut input: String<8> = String::new(); 136 | write!(&mut input, "helLO!").unwrap(); 137 | test_one(input, &[0x06, b'h', b'e', b'l', b'L', b'O', b'!']); 138 | 139 | let mut input: FnvIndexMap = FnvIndexMap::new(); 140 | input.insert(0x01, 0x05).unwrap(); 141 | input.insert(0x02, 0x06).unwrap(); 142 | input.insert(0x03, 0x07).unwrap(); 143 | input.insert(0x04, 0x08).unwrap(); 144 | test_one( 145 | input, 146 | &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], 147 | ); 148 | 149 | // `CString` (uses `serialize_bytes`/`deserialize_byte_buf`) 150 | #[cfg(feature = "use-std")] 151 | test_one( 152 | std::ffi::CString::new("heLlo").unwrap(), 153 | &[0x05, b'h', b'e', b'L', b'l', b'o'], 154 | ); 155 | } 156 | 157 | #[cfg(feature = "heapless")] 158 | #[track_caller] 159 | fn test_one(data: T, ser_rep: &[u8]) 160 | where 161 | T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, 162 | { 163 | let serialized: Vec = to_vec(&data).unwrap(); 164 | assert_eq!(serialized.len(), ser_rep.len()); 165 | let mut x: ::std::vec::Vec = vec![]; 166 | x.extend(serialized.deref().iter().cloned()); 167 | // let bysl: &'de [u8] = serialized.deref(); 168 | assert_eq!(x, ser_rep); 169 | { 170 | // let deserialized: T = from_bytes(serialized.deref()).unwrap(); 171 | let deserialized: T = from_bytes(&x).unwrap(); 172 | assert_eq!(data, deserialized); 173 | } 174 | } 175 | 176 | #[cfg(feature = "use-std")] 177 | #[test] 178 | fn std_io_loopback() { 179 | use postcard::from_io; 180 | use postcard::to_io; 181 | 182 | fn test_io(data: T, ser_rep: &[u8]) 183 | where 184 | T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, 185 | { 186 | let serialized: ::std::vec::Vec = vec![]; 187 | let ser = to_io(&data, serialized).unwrap(); 188 | assert_eq!(ser.len(), ser_rep.len()); 189 | assert_eq!(ser, ser_rep); 190 | { 191 | let mut buff = [0; 2048]; 192 | let x = ser.clone(); 193 | let deserialized: T = from_io((x.as_slice(), &mut buff)).unwrap().0; 194 | assert_eq!(data, deserialized); 195 | } 196 | } 197 | 198 | test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); 199 | test_io( 200 | BasicU8S { 201 | st: 0xABCD, 202 | ei: 0xFE, 203 | sf: 0x1234_4321_ABCD_DCBA, 204 | tt: 0xACAC_ACAC, 205 | }, 206 | &[ 207 | 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 208 | 0xD9, 0xB2, 0xE5, 0x0A, 209 | ], 210 | ); 211 | } 212 | 213 | #[cfg(all( 214 | any(feature = "embedded-io-04", feature = "embedded-io-06"), 215 | feature = "alloc" 216 | ))] 217 | #[test] 218 | fn std_eio_loopback() { 219 | use postcard::from_eio; 220 | use postcard::to_eio; 221 | 222 | fn test_io(data: T, ser_rep: &[u8]) 223 | where 224 | T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, 225 | { 226 | let serialized: ::std::vec::Vec = vec![]; 227 | let ser = to_eio(&data, serialized).unwrap(); 228 | assert_eq!(ser.len(), ser_rep.len()); 229 | assert_eq!(ser, ser_rep); 230 | { 231 | let mut buff = [0; 2048]; 232 | let x = ser.clone(); 233 | let deserialized: T = from_eio((x.as_slice(), &mut buff)).unwrap().0; 234 | assert_eq!(data, deserialized); 235 | } 236 | } 237 | 238 | test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); 239 | test_io( 240 | BasicU8S { 241 | st: 0xABCD, 242 | ei: 0xFE, 243 | sf: 0x1234_4321_ABCD_DCBA, 244 | tt: 0xACAC_ACAC, 245 | }, 246 | &[ 247 | 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 248 | 0xD9, 0xB2, 0xE5, 0x0A, 249 | ], 250 | ); 251 | } 252 | -------------------------------------------------------------------------------- /source/postcard/tests/max_size.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | #[cfg(feature = "experimental-derive")] 4 | mod tests { 5 | use postcard::experimental::max_size::MaxSize; 6 | use postcard::to_slice; 7 | use serde::Serialize; 8 | 9 | #[test] 10 | fn test_struct_max_size() { 11 | #[derive(MaxSize)] 12 | struct Foo { 13 | _a: u16, 14 | _b: Option, 15 | } 16 | 17 | assert_eq!(Foo::POSTCARD_MAX_SIZE, 5); 18 | } 19 | 20 | #[test] 21 | fn test_enum_max_size() { 22 | #[allow(dead_code)] 23 | #[derive(MaxSize, Serialize)] 24 | enum Bar { 25 | A(u16), 26 | B(u8), 27 | } 28 | 29 | assert_eq!(Bar::POSTCARD_MAX_SIZE, 4); 30 | let mut buf = [0u8; 128]; 31 | let used = to_slice(&Bar::A(0xFFFF), &mut buf).unwrap(); 32 | assert!( 33 | used.len() <= Bar::POSTCARD_MAX_SIZE, 34 | "FAIL {} > {}", 35 | used.len(), 36 | Bar::POSTCARD_MAX_SIZE 37 | ); 38 | 39 | #[derive(MaxSize)] 40 | enum Baz {} 41 | 42 | assert_eq!(Baz::POSTCARD_MAX_SIZE, 0); 43 | } 44 | 45 | #[test] 46 | fn test_ref() { 47 | #[allow(dead_code)] 48 | #[derive(MaxSize)] 49 | struct Foo { 50 | a: &'static u32, 51 | } 52 | } 53 | 54 | #[cfg(feature = "heapless")] 55 | #[test] 56 | fn test_vec_edge_cases() { 57 | #[track_caller] 58 | fn test_equals(buf: &mut [u8]) { 59 | let mut v = heapless::Vec::::new(); 60 | for _ in 0..N { 61 | v.push(0).unwrap(); 62 | } 63 | 64 | let serialized = postcard::to_slice(&v, buf).unwrap(); 65 | 66 | assert_eq!(heapless::Vec::::POSTCARD_MAX_SIZE, serialized.len()); 67 | } 68 | 69 | let mut buf = [0; 16400]; 70 | 71 | test_equals::<1>(&mut buf); 72 | test_equals::<2>(&mut buf); 73 | 74 | test_equals::<127>(&mut buf); 75 | test_equals::<128>(&mut buf); 76 | test_equals::<129>(&mut buf); 77 | 78 | test_equals::<16383>(&mut buf); 79 | test_equals::<16384>(&mut buf); 80 | test_equals::<16385>(&mut buf); 81 | } 82 | 83 | // #[cfg(feature = "experimental-derive")] 84 | // #[test] 85 | // fn test_union_max_size() { 86 | // #[derive(postcard::MaxSize)] 87 | // union Foo { 88 | // a: u16, 89 | // b: Option, 90 | // } 91 | // } 92 | 93 | // #[cfg(feature = "experimental-derive")] 94 | // #[test] 95 | // fn test_not_implemented() { 96 | // #[derive(postcard::MaxSize)] 97 | // struct Foo { 98 | // a: &'static str, 99 | // } 100 | // } 101 | } 102 | -------------------------------------------------------------------------------- /spec/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /spec/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["James Munns"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "The Postcard Wire Specification" 7 | -------------------------------------------------------------------------------- /spec/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Introduction](./intro.md) 4 | - [The Serde Data Model](./serde-data-model.md) 5 | - [The Postcard Wire Format](./wire-format.md) 6 | - [Glossary](./glossary.md) 7 | -------------------------------------------------------------------------------- /spec/src/glossary.md: -------------------------------------------------------------------------------- 1 | # Glossary 2 | 3 | ## `byte` 4 | 5 | In this document, a `byte` is defined as an 8-bit octet. 6 | 7 | ## `varint` 8 | 9 | A `varint` is a variable-length-encoding of a signed or unsigned integer. See [The `varint` section of the wire format] for more information. 10 | 11 | [The `varint` section of the wire format]: ./wire-format.md#varint-encoded-integers 12 | 13 | ## Tagged Unions 14 | 15 | Also known in Rust as "enums". See [the Tagged Unions" section] of the Serde Data Model for more information. 16 | 17 | [the Tagged Unions" section]: ./serde-data-model.md##enums-or-tagged-unions 18 | -------------------------------------------------------------------------------- /spec/src/intro.md: -------------------------------------------------------------------------------- 1 | # The Postcard Wire Specification 2 | 3 | Portions of the [Serde Data Model] are included under the terms of the [CC-BY-SA 4.0 license]. 4 | 5 | This specification describes the intended behavior of version 1.0.0 of postcard. 6 | 7 | ## License 8 | 9 | The Postcard Wire Specification (this document) is licensed under the terms of the [CC-BY-SA 4.0 license]. 10 | 11 | [Serde Data Model]: https://serde.rs/data-model.html 12 | [CC-BY-SA 4.0 license]: ./LICENSE-CC-BY-SA 13 | -------------------------------------------------------------------------------- /spec/src/serde-data-model.md: -------------------------------------------------------------------------------- 1 | # The Serde Data Model 2 | 3 | ## Serde Data Model Types 4 | 5 | The serde data model, as defined by the [Serde Book], contains 29 types, each referred to as "A Serde Data Type". 6 | 7 | ### 1 - `bool` 8 | 9 | A type capable of expressing exclusively the values `true` or `false`. 10 | 11 | ### 2 - `i8` 12 | 13 | A signed integer type, capable of expressing any value in the range of `-128..=127` (or `-(2^7)..=((2^7) - 1)`). 14 | 15 | ### 3 - `i16` 16 | 17 | A signed integer type, capable of expressing any value in the range of `-32768..=32767` (or `-(2^15)..=((2^15) - 1)`). 18 | 19 | ### 4 - `i32` 20 | 21 | A signed integer type, capable of expressing any value in the range of `-2147483648..=2147483647` (or `-(2^31)..=((2^31) - 1)`). 22 | 23 | ### 5 - `i64` 24 | 25 | A signed integer type, capable of expressing any value in the range of `-9223372036854775808..=9223372036854775807` (or `-(2^63)..=((2^63) - 1)`). 26 | 27 | ### 6 - `i128` 28 | 29 | A signed integer type, capable of expressing any value in the range of `-170141183460469231731687303715884105728..=170141183460469231731687303715884105727` (or `-(2^127)..=((2^127) - 1)`). 30 | 31 | ### 7 - `u8` 32 | 33 | An unsigned integer type, capable of expressing any value in the range of `0..=255` (or `0..=((2^8) - 1)`). 34 | 35 | ### 8 - `u16` 36 | 37 | An unsigned integer type, capable of expressing any value in the range of `0..=65535` (or `0..=((2^16) - 1)`). 38 | 39 | ### 9 - `u32` 40 | 41 | An unsigned integer type, capable of expressing any value in the range of `0..=4294967295` (or `0..=((2^32) - 1)`). 42 | 43 | ### 10 - `u64` 44 | 45 | An unsigned integer type, capable of expressing any value in the range of `0..=18446744073709551615` (or `0..=((2^64) - 1)`). 46 | 47 | ### 11 - `u128` 48 | 49 | An unsigned integer type, capable of expressing any value in the range of `0..=340282366920938463463374607431768211456` (or `0..=((2^128) - 1)`). 50 | 51 | ### 12 - `f32` 52 | 53 | A "binary32" type defined as defined in [IEEE 754-2008]. 54 | 55 | ### 13 - `f64` 56 | 57 | A "binary64" type defined as defined in [IEEE 754-2008]. 58 | 59 | ### 14 - `char` 60 | 61 | A four-byte type representing a [Unicode scalar value]. 62 | 63 | A Unicode scalar value is defined by [Unicode 14.0 Chapter 3] Section 9 - "Unicode Encoding Forms", Definition [D76](https://www.unicode.org/versions/latest/ch03.pdf#G7404): 64 | 65 | > *Unicode scalar value*: Any Unicode code point except high-surrogate and low-surrogate code points. 66 | > 67 | > As a result of this definition, the set of Unicode scalar values consists of the ranges 0x0000_0000 to 0x0000_D7FF and 0x0000_E000 to 0x0010_FFFF inclusive. 68 | 69 | [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value 70 | 71 | ### 15 - `string` 72 | 73 | A type representing a variable quantity of bytes, which together represent a valid UTF-8 code point sequence, 74 | as defined by [Unicode 14.0 Chapter 3] Section 9 - "Unicode Encoding Forms", Definition 75 | [D92](https://www.unicode.org/versions/latest/ch03.pdf#G7404): 76 | 77 | > *UTF-8 encoding form*: The Unicode encoding form that assigns each Unicode scalar 78 | > value to an unsigned byte sequence of one to four bytes in length, as specified in 79 | > Table 3-6 and Table 3-7. 80 | 81 | This encoding form is stored using the "UTF-8 encoding scheme", as defined by [Unicode 14.0 Chapter 3] 82 | Section 10 - "Unicode Encoding Schemes", Definition [D95](https://www.unicode.org/versions/latest/ch03.pdf#G28070): 83 | 84 | > *UTF-8 encoding scheme*: The Unicode encoding scheme that serializes a UTF-8 85 | > code unit sequence in exactly the same order as the code unit sequence itself. 86 | 87 | ### 16 - `byte array` 88 | 89 | A type representing a variable quantity of bytes. 90 | 91 | ### 17 - `option` 92 | 93 | A type representing zero or one Serde Data Type. 94 | 95 | ### 18 - `unit` 96 | 97 | A type representing an anonymous value containing no data. 98 | 99 | ### 19 - `unit_struct` 100 | 101 | A type representing a named value containing no data. 102 | 103 | ### 20 - `unit_variant` 104 | 105 | A type representing a named, tagged union variant, containing no data. 106 | 107 | ### 21 - `newtype_struct` 108 | 109 | A type representing a named value, containing exactly one anonymous Serde Data Type. 110 | 111 | ### 22 - `newtype_variant` 112 | 113 | A type representing a named, tagged union variant, containing exactly one anonymous Serde Data Type. 114 | 115 | ### 23 - `seq` 116 | 117 | A type representing a variable quantity of values of a single Serde Data Type, e.g. a "Homogeneous Array". 118 | 119 | Values of each element of the `seq` may have differing values. 120 | 121 | ### 24 - `tuple` 122 | 123 | A type representing a fixed quantity of values, each of any Serde Data Type, e.g. a "Heterogeneous Array". 124 | 125 | Values of each element of the `tuple` may have differing values. 126 | 127 | ### 25 - `tuple_struct` 128 | 129 | A type representing a named type specifcally containing exactly one `tuple` Serde Data Type 130 | 131 | ### 26 - `tuple_variant` 132 | 133 | A type representing a named, tagged union variant, containing exactly one `tuple` Serde Data Type. 134 | 135 | ### 27 - `map` 136 | 137 | A type representing a variable quantity of key-value pairs. All keys are values of a single Serde Data Type. All values are a values of a single Serde Data Type. 138 | 139 | ### 28 - `struct` 140 | 141 | A type representing a fixed quantity of named values, each of any Serde Data Type. 142 | 143 | Values of each element of the `tuple` may have differing values. 144 | 145 | > NOTE: Similar to `tuple`s , `struct`s have a known number of members, however all members of a `struct` also have a known name. 146 | > 147 | > `struct`s are also similar to `map`s, in that each member has a name (as a key) and a value, however `struct`s always have a fixed number of members, and their names are always constant. 148 | 149 | ### 29 - `struct_variant` 150 | 151 | A type representing a named, tagged union variant, containing exactly one `struct` Serde Data type 152 | 153 | ## Meta types 154 | 155 | ### Named and Anonymous types 156 | 157 | The above discussion differentiates between "named types" and "anonymous types". 158 | 159 | "named types" are used to describe types that are bound to a name within the data type they 160 | are contained, such as a field of a `struct`. 161 | 162 | "anonymous types" are used to describe types that are NOT bound to a name within the data type 163 | they are contained, such as a single element of a `tuple`. 164 | 165 | 166 | ### `enum`s or Tagged Unions 167 | 168 | In the Rust language, the `enum` type (also known as "tagged unions" in other languages) describes a type that has 169 | a differing internal type based on a value known as a `discriminant`. 170 | 171 | In the serde data model (as well as the rust language) `discriminants` are always of the type `u32`. 172 | 173 | In the serde data model, the "internal type" of an `enum` can be one of any of the following: 174 | 175 | * `unit_variant` 176 | * `newtype_variant` 177 | * `tuple_variant` 178 | * `struct_variant` 179 | 180 | ## References 181 | 182 | | Document Name | Full Name | Version | 183 | | :--- | :--- | :--- | 184 | | [Serde Book] | The Serde Book | v1.0.137 | 185 | | [Unicode 14.0 Chapter 3] | The Unicode® Standard Core Specification, Chapter 3: Conformance | v14.0 | 186 | | [IEEE 754-2008] | IEEE Standard for Floating-Point Arithmetic | 2008 | 187 | 188 | [Serde Book]: https://serde.rs/ 189 | [Unicode 14.0 Chapter 3]: https://www.unicode.org/versions/Unicode14.0.0/ch03.pdf 190 | [IEEE 754-2008]: https://standards.ieee.org/ieee/754/4211/ 191 | -------------------------------------------------------------------------------- /spec/src/wire-format.md: -------------------------------------------------------------------------------- 1 | # The Postcard Wire Format 2 | 3 | Postcard is responsible for translating between items that exist as part of [The Serde Data Model] into a binary representation. 4 | 5 | [The Serde Data Model]: ../serde-data-model.md 6 | 7 | This is commonly referred to as **Serialization**, or converting from Serde Data Model elements to a binary representation; or **Deserialization**, or converting from a binary representation to Serde Data Model elements. 8 | 9 | ## Stability 10 | 11 | The Postcard wire format is considered stable as of v1.0.0 and above of Postcard. Breaking changes to the wire format would 12 | be considered a breaking change to the library, and would necessitate the library being revised to v2.0.0, along with a 13 | new version of this wire format specification addressing the v2.0.0 wire format. 14 | 15 | ## Non Self-Describing Format 16 | 17 | Postcard is **NOT** considered a "Self Describing Format", meaning that users (Serializers and Deserializers) of postcard data are expected to have a mutual understanding of the encoded data. 18 | 19 | In practice this requires all systems sending or receiving postcard encoded data share a common schema, often as a common Rust data-type library. 20 | 21 | Backwards/forwards compatibility between revisions of a postcard schema are considered outside of the scope of the postcard wire format, and must be considered by the end users, if compatible revisions to an agreed-upon schema are necessary. 22 | 23 | ## `varint` encoded integers 24 | 25 | For reasons of portability and compactness, many integers are encoded into a variable length format, commonly known as ["leb" or "varint"] encoded. 26 | 27 | ["leb" or "varint"]: https://en.wikipedia.org/wiki/Variable-length_quantity 28 | 29 | For the remainder of this document, these variable length encoded values will be referred to as `varint(N)`, where `N` represents the encoded Serde Data Model type, such as `u16` (`varint(u16)`) or `i32` (`varint(i32)`). 30 | 31 | Conceptually, all `varint(N)` types encode data in a similar way when considering a stream of bytes: 32 | 33 | * The most significant bit of each stream byte is used as a "continuation" flag. 34 | * If the flag is `1`, then this byte is NOT the last byte that comprises this varint 35 | * If the flag is `0`, then this byte IS the last byte that comprises this varint 36 | 37 | All `varint(N)` types are encoded in "little endian" order, meaning that the first byte will contain the least significant seven data bits. 38 | 39 | Specifically, the following types are encoded as `varint`s in postcard: 40 | 41 | | Type | Varint Type | 42 | | :--- | :--- | 43 | | `u16` | `varint(u16)` | 44 | | `i16` | `varint(i16)` | 45 | | `u32` | `varint(u32)` | 46 | | `i32` | `varint(i32)` | 47 | | `u64` | `varint(u64)` | 48 | | `i64` | `varint(i64)` | 49 | | `u128` | `varint(u128)` | 50 | | `i128` | `varint(i128)` | 51 | 52 | As `u8` and `i8` types always fit into a single byte, they are encoded as-is rather than encoded using a `varint`. 53 | 54 | Additionally the following two types are not part of the Serde Data Model, but are used within the context of postcard: 55 | 56 | | Type | Varint Type | 57 | | :--- | :--- | 58 | | `usize` | `varint(usize)` | 59 | | `isize` | `varint(isize)` | 60 | 61 | See the section [isize and usize] below for more details on how these types are used. 62 | 63 | [isize and usize]: #isize-and-usize 64 | 65 | ### Unsigned Integer Encoding 66 | 67 | For example, the following 16-bit unsigned numbers would be encoded as follows: 68 | 69 | | Dec | Hex | `varint` Encoded | Length | 70 | | ---: | :--- | :--- | :--- | 71 | | 0 | `0x00_00` | `[0x00]` | 1 | 72 | | 127 | `0x00_7F` | `[0x7F]` | 1 | 73 | | 128 | `0x00_80` | `[0x80, 0x01]` | 2 | 74 | | 16383 | `0x3F_FF` | `[0xFF, 0x7F]` | 2 | 75 | | 16384 | `0x40_00` | `[0x80, 0x80, 0x01]` | 3 | 76 | | 16385 | `0x40_01` | `[0x81, 0x80, 0x01]` | 3 | 77 | | 65535 | `0xFF_FF` | `[0xFF, 0xFF, 0x03]` | 3 | 78 | 79 | ### Signed Integer Encoding 80 | 81 | Signed integers are typically "natively" encoded using a [Two's Complement] form, meaning that the most significant bit is used 82 | to offset the value by a large negative shift. If this form was used directly for encoding signed integer values, it would 83 | have the negative effect that negative values would ALWAYS take the maximum encoded length to store on the wire. 84 | 85 | [Two's Complement]: https://en.wikipedia.org/wiki/Two%27s_complement 86 | 87 | For this reason, signed integers, when encoded as a `varint`, are first [Zigzag encoded]. Zigzag encoding stores the sign bit in the 88 | LEAST significant bit of the integer, rather than the MOST significant bit. 89 | 90 | [Zigzag encoded]: https://en.wikipedia.org/wiki/Variable-length_quantity#Zigzag_encoding 91 | 92 | This means that signed integers of low absolute magnitude (e.g. 1, -1) can be encoded using a much smaller space. 93 | 94 | For example, the following 16-bit signed numbers would be encoded as follows: 95 | 96 | | Dec | Hex\* | Zigzag (hex) | `varint` Encoded | Length | 97 | | ---: | :--- | :--- | :--- | :--- | 98 | | 0 | `0x00_00` | `0x00_00` | `[0x00]` | 1 | 99 | | -1 | `0xFF_FF` | `0x00_01` | `[0x01]` | 1 | 100 | | 1 | `0x00_01` | `0x00_02` | `[0x02]` | 1 | 101 | | 63 | `0x00_3F` | `0x00_7E` | `[0x7E]` | 1 | 102 | | -64 | `0xFF_C0` | `0x00_7F` | `[0x7F]` | 1 | 103 | | 64 | `0x00_40` | `0x00_80` | `[0x80, 0x01]` | 2 | 104 | | -65 | `0xFF_BF` | `0x00_81` | `[0x81, 0x01]` | 2 | 105 | | 32767 | `0x7F_FF` | `0xFF_FE` | `[0xFE, 0xFF, 0x03]` | 3 | 106 | | -32768 | `0x80_00` | `0xFF_FF` | `[0xFF, 0xFF, 0x03]` | 3 | 107 | 108 | `*`: This column is represented as a sixteen bit, two's complement form 109 | 110 | ### Maximum Encoded Length 111 | 112 | As the values that an integer type (e.g. `u16`, `u32`) are limited to the expressible range of the type, 113 | the maximum encoded length of these types are knowable ahead of time. Postcard uses this information to 114 | limit the number of bytes it will process when decoding a `varint`. 115 | 116 | As `varint`s encode seven data bits for every encoded byte, the maximum encoded length can be stated 117 | as follows: 118 | 119 | ``` 120 | bits_per_byte = 8 121 | enc_bits_per_byte = 7 122 | encoded_max = ceil((len_bytes * bits_per_byte) / enc_bits_per_byte) 123 | ``` 124 | 125 | The following table expresses the maximum encoded length for each type: 126 | 127 | | Type | Varint Type | Type length (bytes) | Varint length max (bytes) | 128 | | :--- | :--- | :--- | :--- | 129 | | `u16` | `varint(u16)` | 2 | 3 | 130 | | `i16` | `varint(i16)` | 2 | 3 | 131 | | `u32` | `varint(u32)` | 4 | 5 | 132 | | `i32` | `varint(i32)` | 4 | 5 | 133 | | `u64` | `varint(u64)` | 8 | 10 | 134 | | `i64` | `varint(i64)` | 8 | 10 | 135 | | `u128` | `varint(u128)` | 16 | 19 | 136 | | `i128` | `varint(i128)` | 16 | 19 | 137 | 138 | ### Canonicalization 139 | 140 | The postcard wire format does NOT enforce [canonicalization], however values are still required to fit within the [Maximum Encoded Length] of the data type, and to contain no data that exceeds the maximum value of the integer type. 141 | 142 | [Maximum Encoded Length]: #maximum-encoded-length 143 | 144 | In this context, an encoded form would be considered canonical if it is encoded with no excess encoding bytes necessary to encode the value, and with the excess encoding bits all containing `0`s. 145 | 146 | For example in the following `u16` encoded data: 147 | 148 | | Value (`u16`) | Encoded Form | Canonical? | Accepted? | 149 | | :--- | :--- | :--- | :--- | 150 | | 0 | `[0x00]` | Yes | Yes | 151 | | 0 | `[0x80, 0x00]` | No\* | Yes | 152 | | 0 | `[0x80, 0x80, 0x00]` | No\* | Yes | 153 | | 0 | `[0x80, 0x80, 0x80, 0x00]` | No\* | No\*\* | 154 | | 65535 | `[0xFF, 0xFF, 0x03]` | Yes | Yes | 155 | | 131071 | `[0xFF, 0xFF, 0x07]` | No\*\*\* | No\*\*\* | 156 | | 65535 | `[0xFF, 0xFF, 0x83, 0x00]` | No\* | No\*\* | 157 | 158 | * \*: Contains excess encoding bytes 159 | * \*\*: Exceeds the [Maximum Encoded Length] of the type 160 | * \*\*\*: Exceeds the maximum value of the encoded type 161 | 162 | [canonicalization]: https://en.wikipedia.org/wiki/Canonicalization 163 | 164 | ## `isize` and `usize` 165 | 166 | The Serde Data Model does not address platform-specific sized integers, and instead supports them by mapping to an integer type matching 167 | the platform's bit width. 168 | 169 | For example, on a platform with 32-bit pointers, `usize` will map to `u32`, and `isize` will map to `i32`. On a platform with 64-bit pointers, `usize` will map to `u64`, and `isize` will map to `i64`. 170 | 171 | As these types are all `varint` encoded on the wire, two platforms of dissimilar pointer-widths will be able to interoperate without compatibility problems, as long as the value encoded in these types do not exceed the maximum encodable value of the smaller platform. If this occurs, for example sending `0x1_0000_0000usize` from a 64-bit target (as a `u64`), when decoding on a 32-bit platform, the value will fail to decode, as it exceeds the maximum value of a `usize` (as a `u32`). 172 | 173 | ## Variable Quantities 174 | 175 | Several Serde Data Model types, such as `seq` and `string` contain a variable quantity of data elements. 176 | 177 | Variable quantities are prefixed by a `varint(usize)`, encoding the count of subsequent data elements, followed by the encoded data elements. 178 | 179 | ## Tagged Unions 180 | 181 | Tagged unions consist of two parts: The tag, or discriminant, and the value matching with that discriminant. 182 | 183 | Tagged unions in postcard are encoded as a `varint(u32)` containing the discriminant, followed by the encoded value matching that discriminant. 184 | 185 | [Tagged Union]: #tagged-unions 186 | 187 | ## Serde Data Model Types 188 | 189 | The following describes how each of the Serde Data Model types are encoded in the Postcard Wire Format. 190 | 191 | ### 1 - `bool` 192 | 193 | A `bool` is stored as a single byte, with the value of `0x00` for `false`, and `0x01` as `true`. 194 | 195 | All other values are considered an error. 196 | 197 | ### 2 - `i8` 198 | 199 | An `i8` is stored as a single byte, in two's complement form. 200 | 201 | All values are considered valid. 202 | 203 | ### 3 - `i16` 204 | 205 | An `i16` is stored as a `varint(i16)`. 206 | 207 | ### 4 - `i32` 208 | 209 | An `i32` is stored as a `varint(i32)`. 210 | 211 | ### 5 - `i64` 212 | 213 | An `i64` is stored as a `varint(i64)`. 214 | 215 | ### 6 - `i128` 216 | 217 | An `i128` is stored as a `varint(i128)`. 218 | 219 | ### 7 - `u8` 220 | 221 | An `u8` is stored as a single byte. 222 | 223 | All values are considered valid. 224 | 225 | ### 8 - `u16` 226 | 227 | A `u16` is stored as a `varint(u16)`. 228 | 229 | ### 9 - `u32` 230 | 231 | A `u32` is stored as a `varint(u32)`. 232 | 233 | ### 10 - `u64` 234 | 235 | A `u64` is stored as a `varint(u64)`. 236 | 237 | ### 11 - `u128` 238 | 239 | A `u128` is stored as a `varint(u128)`. 240 | 241 | ### 12 - `f32` 242 | 243 | An `f32` will be bitwise converted into a `u32`, and encoded as a little-endian array of four bytes. 244 | 245 | For example, the float value `-32.005859375f32` would be bitwise represented as `0xc200_0600u32`, and encoded as `[0x00, 0x06, 0x00, 0xc2]`. 246 | 247 | > NOTE: `f32` values are NOT converted to `varint` form, and are always encoded as four bytes on the wire. 248 | 249 | ### 13 - `f64` 250 | 251 | An `f64` will be bitwise converted into a `u64`, and encoded as a little-endian array of eight bytes. 252 | 253 | For example, the float value `-32.005859375f64` would be bitwise represented as `0xc040_00c0_0000_0000u64`, and encoded as `[0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x40, 0xc0]`. 254 | 255 | > NOTE: `f64` values are NOT converted to `varint` form, and are always encoded as eight bytes on the wire. 256 | 257 | ### 14 - `char` 258 | 259 | A `char` will be encoded in UTF-8 form, and encoded as a `string`. 260 | 261 | ### 15 - `string` 262 | 263 | A `string` is encoded with a `varint(usize)` containing the length, followed by the array of bytes, each encoded as a single `u8`. 264 | 265 | ### 16 - `byte array` 266 | 267 | A `byte array` is encoded with a `varint(usize)` containing the length, followed by the array of bytes, each encoded as a single `u8`. 268 | 269 | ### 17 - `option` 270 | 271 | An `option` is encoded in one of two ways, depending in its value. 272 | 273 | If an option has the value of `None`, it is encoded as the single byte `0x00`, with no following data. 274 | 275 | If an option has the value of `Some`, it is encoded as the single byte `0x01`, followed by exactly one encoded Serde Data Type. 276 | 277 | ### 18 - `unit` 278 | 279 | The `unit` type is NOT encoded to the wire, meaning that it occupies zero bytes. 280 | 281 | ### 19 - `unit_struct` 282 | 283 | The `unit_struct` type is NOT encoded to the wire, meaning that it occupies zero bytes. 284 | 285 | ### 20 - `unit_variant` 286 | 287 | A `unit_variant` is an instance of a [Tagged Union], consisting of a `varint(u32)` discriminant, with no additional encoded data. 288 | 289 | ### 21 - `newtype_struct` 290 | 291 | A `newtype_struct` is encoded as the Serde Data Type it contains, with no additional data preceding or following it. 292 | 293 | ### 22 - `newtype_variant` 294 | 295 | A `newtype_variant` is an instance of a [Tagged Union], consisting of a `varint(u32)` discriminant, followed by the encoded representation of the Serde Data Type it contains. 296 | 297 | ### 23 - `seq` 298 | 299 | A `seq` is encoded with a `varint(usize)` containing the number of elements of the `seq`, followed by the array of elements, each encoded as an individual Serde Data Type. 300 | 301 | ### 24 - `tuple` 302 | 303 | A `tuple` is encoded as the elements that comprise it, in their order of definition (left to right). 304 | 305 | As `tuple`s have a known size, their length is not encoded on the wire. 306 | 307 | ### 25 - `tuple_struct` 308 | 309 | A `tuple_struct` is encoded as a `tuple` consisting of the elements contained by the `tuple_struct`. 310 | 311 | ### 26 - `tuple_variant` 312 | 313 | A `tuple_variant` is an instance of a [Tagged Union], consisting of a `varint(u32)` discriminant, followed by a `tuple` consisting of the elements contained by the `tuple_variant`. 314 | 315 | ### 27 - `map` 316 | 317 | A `map` is encoded with a `varint(usize)` containing the number of (key, value) elements of the `map`, followed by the array of (key, value) pairs, each encoded as a `tuple` of `(key, value)`. 318 | 319 | ### 28 - `struct` 320 | 321 | A `struct` is encoded as the elements that comprise it, in their order of definition (top to bottom). 322 | 323 | As `struct`s have a known number of elements with known names, their length and field names are not encoded on the wire. 324 | 325 | ### 29 - `struct_variant` 326 | 327 | A `struct_variant` is an instance of a [Tagged Union], consisting of a `varint(u32)` discriminant, followed by a `struct` consisting of the elements contained by the `struct_variant`. 328 | --------------------------------------------------------------------------------