postcard-1.1.3/.cargo_vcs_info.json0000644000000001550000000000100126560ustar { "git": { "sha1": "718aa6a6850456017c19eeff67303c633f875736" }, "path_in_vcs": "source/postcard" }postcard-1.1.3/Cargo.lock0000644000000226470000000000100106430ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "approx" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" dependencies = [ "num-traits", ] [[package]] name = "atomic-polyfill" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" dependencies = [ "critical-section", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cobs" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" dependencies = [ "thiserror", ] [[package]] name = "crc" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "critical-section" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "defmt" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" dependencies = [ "bitflags", "defmt-macros", ] [[package]] name = "defmt-macros" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" dependencies = [ "defmt-parser", "proc-macro-error2", "proc-macro2", "quote", "syn", ] [[package]] name = "defmt-parser" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ "thiserror", ] [[package]] name = "embedded-io" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" [[package]] name = "embedded-io" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" [[package]] name = "hash32" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" dependencies = [ "byteorder", ] [[package]] name = "heapless" version = "0.7.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" dependencies = [ "atomic-polyfill", "hash32", "rustc_version", "serde", "spin", "stable_deref_trait", ] [[package]] name = "lock_api" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", ] [[package]] name = "nalgebra" version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" dependencies = [ "approx", "num-complex", "num-rational", "num-traits", "simba", "typenum", ] [[package]] name = "num-complex" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-rational" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "paste" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "postcard" version = "1.1.3" dependencies = [ "cobs", "crc", "defmt", "embedded-io 0.4.0", "embedded-io 0.6.1", "heapless", "nalgebra", "postcard-derive", "serde", ] [[package]] name = "postcard-derive" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68f049d94cb6dda6938cc8a531d2898e7c08d71c6de63d8e67123cca6cdde2cc" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", "quote", ] [[package]] name = "proc-macro-error2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", "syn", ] [[package]] name = "proc-macro2" version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "simba" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" dependencies = [ "approx", "num-complex", "num-traits", "paste", ] [[package]] name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" dependencies = [ "lock_api", ] [[package]] name = "stable_deref_trait" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "syn" version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "typenum" version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" postcard-1.1.3/Cargo.toml0000644000000052210000000000100106530ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "postcard" version = "1.1.3" authors = ["James Munns "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "A no_std + serde compatible message library for Rust" documentation = "https://docs.rs/postcard/" readme = "README.md" keywords = [ "serde", "cobs", "framing", ] categories = [ "embedded", "no-std", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jamesmunns/postcard" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [features] alloc = [ "serde/alloc", "embedded-io-04?/alloc", "embedded-io-06?/alloc", ] core-num-saturating = [] crc = ["dep:crc"] default = ["heapless-cas"] defmt = ["dep:defmt"] embedded-io = ["dep:embedded-io-04"] embedded-io-04 = ["dep:embedded-io-04"] embedded-io-06 = ["dep:embedded-io-06"] experimental-derive = ["postcard-derive"] heapless = ["dep:heapless"] heapless-cas = [ "heapless", "dep:heapless", "heapless/cas", ] nalgebra-v0_33 = ["nalgebra_v0_33"] paste = [] postcard-derive = ["dep:postcard-derive"] use-crc = ["crc"] use-defmt = ["defmt"] use-std = [ "serde/std", "alloc", ] [lib] name = "postcard" path = "src/lib.rs" [[test]] name = "accumulator" path = "tests/accumulator.rs" [[test]] name = "crc" path = "tests/crc.rs" [[test]] name = "loopback" path = "tests/loopback.rs" [[test]] name = "max_size" path = "tests/max_size.rs" [dependencies.cobs] version = "0.3.0" default-features = false [dependencies.crc] version = "3.0.1" optional = true [dependencies.defmt] version = "1" optional = true [dependencies.embedded-io-04] version = "0.4" optional = true package = "embedded-io" [dependencies.embedded-io-06] version = "0.6" optional = true package = "embedded-io" [dependencies.heapless] version = "0.7.0" features = ["serde"] optional = true default-features = false [dependencies.nalgebra_v0_33] version = "0.33.0" optional = true default-features = false package = "nalgebra" [dependencies.postcard-derive] version = "0.2.0" optional = true [dependencies.serde] version = "1.0.100" features = ["derive"] default-features = false postcard-1.1.3/Cargo.toml.orig000064400000000000000000000041321046102023000143340ustar 00000000000000[package] name = "postcard" version = "1.1.3" authors = ["James Munns "] edition = "2021" readme = "README.md" repository = "https://github.com/jamesmunns/postcard" description = "A no_std + serde compatible message library for Rust" license = "MIT OR Apache-2.0" categories = [ "embedded", "no-std", ] keywords = [ "serde", "cobs", "framing", ] documentation = "https://docs.rs/postcard/" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] [dependencies.heapless] version = "0.7.0" default-features = false features = ["serde"] optional = true [dependencies.serde] version = "1.0.100" default-features = false features = ["derive"] [dependencies.cobs] version = "0.3.0" default-features = false [dependencies.defmt] version = "1" optional = true [dependencies.embedded-io-04] package = "embedded-io" version = "0.4" optional = true [dependencies.embedded-io-06] package = "embedded-io" version = "0.6" optional = true [dependencies.postcard-derive] version = "0.2.0" optional = true [dependencies.crc] version = "3.0.1" optional = true [dependencies.nalgebra_v0_33] package = "nalgebra" version = "0.33.0" optional = true default-features = false [features] default = ["heapless-cas"] # Enables support for `embedded-io` traits # This feature will track the latest `embedded-io` when major releases are made embedded-io = ["dep:embedded-io-04"] # Specific versions of `embedded-io` can be selected through the features below embedded-io-04 = ["dep:embedded-io-04"] embedded-io-06 = ["dep:embedded-io-06"] use-std = ["serde/std", "alloc"] heapless-cas = ["heapless", "dep:heapless", "heapless/cas"] alloc = ["serde/alloc", "embedded-io-04?/alloc", "embedded-io-06?/alloc"] use-defmt = ["defmt"] use-crc = ["crc"] # Does nothing, for compat only paste = [] # Experimental features! # # NOT subject to SemVer guarantees! experimental-derive = ["postcard-derive"] core-num-saturating = [] nalgebra-v0_33 = ["nalgebra_v0_33"] crc = ["dep:crc"] defmt = ["dep:defmt"] heapless = ["dep:heapless"] postcard-derive = ["dep:postcard-derive"] postcard-1.1.3/LICENSE-APACHE000064400000000000000000000251371046102023000134010ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. postcard-1.1.3/LICENSE-MIT000064400000000000000000000020471046102023000131040ustar 00000000000000Copyright (c) 2019 Anthony James Munns Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. postcard-1.1.3/README.md000064400000000000000000000106621046102023000127310ustar 00000000000000# Postcard [![Documentation](https://docs.rs/postcard/badge.svg)](https://docs.rs/postcard) Postcard is a `#![no_std]` focused serializer and deserializer for Serde. Postcard aims to be convenient for developers in constrained environments, while allowing for flexibility to customize behavior as needed. ## Design Goals 1. Design primarily for `#![no_std]` usage, in embedded or other constrained contexts 2. Support a maximal set of `serde` features, so `postcard` can be used as a drop in replacement 3. Avoid special differences in code between communication code written for a microcontroller or a desktop/server PC 4. Be resource efficient - memory usage, code size, developer time, and CPU time; in that order 5. Allow library users to customize the serialization and deserialization behavior to fit their bespoke needs ## Format Stability As of v1.0.0, `postcard` has a documented and stable wire format. More information about this wire format can be found in the `spec/` folder of the Postcard repository, or viewed online at . Work towards the Postcard Specification and portions of the Postcard 1.0 Release were sponsored by Mozilla Corporation. ## Variable Length Data All signed and unsigned integers larger than eight bits are encoded using a [Varint]. This includes the length of array slices, as well as the discriminant of `enums`. For more information, see the [Varint] chapter of the wire specification. [Varint]: https://postcard.jamesmunns.com/wire-format.html#varint-encoded-integers ## Example - Serialization/Deserialization Postcard can serialize and deserialize messages similar to other `serde` formats. Using the default `heapless` feature to serialize to a `heapless::Vec`: ```rust use core::ops::Deref; use serde::{Serialize, Deserialize}; use postcard::{from_bytes, to_vec}; use heapless::Vec; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let output: Vec = to_vec(&RefStruct { bytes: &bytes, str_s: message, }).unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); let out: RefStruct = from_bytes(output.deref()).unwrap(); assert_eq!( out, RefStruct { bytes: &bytes, str_s: message, } ); ``` Or the optional `alloc` feature to serialize to an `alloc::vec::Vec`: ```rust use core::ops::Deref; use serde::{Serialize, Deserialize}; use postcard::{from_bytes, to_allocvec}; extern crate alloc; use alloc::vec::Vec; #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let output: Vec = to_allocvec(&RefStruct { bytes: &bytes, str_s: message, }).unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); let out: RefStruct = from_bytes(output.deref()).unwrap(); assert_eq!( out, RefStruct { bytes: &bytes, str_s: message, } ); ``` ## Flavors `postcard` supports a system called `Flavors`, which are used to modify the way postcard serializes or processes serialized data. These flavors act as "plugins" or "middlewares" during the serialization or deserialization process, and can be combined to obtain complex protocol formats. See the documentation of the `ser_flavors` or `de_flavors` modules for more information on usage. ## Setup - `Cargo.toml` 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`! ```toml [dependencies] postcard = "1.0.0" # By default, `serde` has the `std` feature enabled, which makes it unsuitable for embedded targets # disabling default-features fixes this serde = { version = "1.0.*", default-features = false } ``` ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or ) - MIT license ([LICENSE-MIT](LICENSE-MIT) or ) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. postcard-1.1.3/src/accumulator.rs000064400000000000000000000252541046102023000151310ustar 00000000000000//! An accumulator used to collect chunked COBS data and deserialize it. use serde::Deserialize; /// An accumulator used to collect chunked COBS data and deserialize it. /// /// This is often useful when you receive "parts" of the message at a time, for example when draining /// a serial port buffer that may not contain an entire uninterrupted message. /// /// # Examples /// /// Deserialize a struct by reading chunks from a [`Read`]er. /// /// ```rust /// use postcard::accumulator::{CobsAccumulator, FeedResult}; /// use serde::Deserialize; /// use std::io::Read; /// /// # let mut input_buf = [0u8; 256]; /// # #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] /// # struct MyData { /// # a: u32, /// # b: bool, /// # c: [u8; 16], /// # } /// let input = /* Anything that implements the `Read` trait */ /// # postcard::to_slice_cobs(&MyData { /// # a: 0xabcdef00, /// # b: true, /// # c: [0xab; 16], /// # }, &mut input_buf).unwrap(); /// # let mut input = &input[..]; /// /// let mut raw_buf = [0u8; 32]; /// let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new(); /// /// while let Ok(ct) = input.read(&mut raw_buf) { /// // Finished reading input /// if ct == 0 { /// break; /// } /// /// let buf = &raw_buf[..ct]; /// let mut window = &buf[..]; /// /// 'cobs: while !window.is_empty() { /// window = match cobs_buf.feed::(&window) { /// FeedResult::Consumed => break 'cobs, /// FeedResult::OverFull(new_wind) => new_wind, /// FeedResult::DeserError(new_wind) => new_wind, /// FeedResult::Success { data, remaining } => { /// // Do something with `data: MyData` here. /// /// dbg!(data); /// /// remaining /// } /// }; /// } /// } /// ``` /// /// [`Read`]: std::io::Read #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] pub struct CobsAccumulator { buf: [u8; N], idx: usize, } /// The result of feeding the accumulator. #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] pub enum FeedResult<'a, T> { /// Consumed all data, still pending. Consumed, /// Buffer was filled. Contains remaining section of input, if any. OverFull(&'a [u8]), /// Reached end of chunk, but deserialization failed. Contains remaining section of input, if any. DeserError(&'a [u8]), /// Deserialization complete. Contains deserialized data and remaining section of input, if any. Success { /// Deserialize data. data: T, /// Remaining data left in the buffer after deserializing. remaining: &'a [u8], }, } impl Default for CobsAccumulator { fn default() -> Self { Self::new() } } impl CobsAccumulator { /// Create a new accumulator. pub const fn new() -> Self { CobsAccumulator { buf: [0; N], idx: 0, } } /// Appends data to the internal buffer and attempts to deserialize the accumulated data into /// `T`. #[inline] pub fn feed<'a, T>(&mut self, input: &'a [u8]) -> FeedResult<'a, T> where T: for<'de> Deserialize<'de>, { self.feed_ref(input) } /// Appends data to the internal buffer and attempts to deserialize the accumulated data into /// `T`. /// /// This differs from feed, as it allows the `T` to reference data within the internal buffer, but /// mutably borrows the accumulator for the lifetime of the deserialization. /// If `T` does not require the reference, the borrow of `self` ends at the end of the function. pub fn feed_ref<'de, 'a, T>(&'de mut self, input: &'a [u8]) -> FeedResult<'a, T> where T: Deserialize<'de>, { if input.is_empty() { return FeedResult::Consumed; } let zero_pos = input.iter().position(|&i| i == 0); if let Some(n) = zero_pos { // Yes! We have an end of message here. // Add one to include the zero in the "take" portion // of the buffer, rather than in "release". let (take, release) = input.split_at(n + 1); // Does it fit? if (self.idx + take.len()) <= N { // Aw yiss - add to array self.extend_unchecked(take); let retval = match crate::from_bytes_cobs::(&mut self.buf[..self.idx]) { Ok(t) => FeedResult::Success { data: t, remaining: release, }, Err(_) => FeedResult::DeserError(release), }; self.idx = 0; retval } else { self.idx = 0; FeedResult::OverFull(release) } } else { // Does it fit? if (self.idx + input.len()) > N { // nope let new_start = N - self.idx; self.idx = 0; FeedResult::OverFull(&input[new_start..]) } else { // yup! self.extend_unchecked(input); FeedResult::Consumed } } } /// Extend the internal buffer with the given input. /// /// # Panics /// /// Will panic if the input does not fit in the internal buffer. fn extend_unchecked(&mut self, input: &[u8]) { let new_end = self.idx + input.len(); self.buf[self.idx..new_end].copy_from_slice(input); self.idx = new_end; } } #[cfg(test)] mod test { use super::*; #[test] fn loop_test() { #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] struct Demo { a: u32, b: u8, } let mut raw_buf = [0u8; 64]; let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); let ser = crate::to_slice_cobs(&Demo { a: 10, b: 20 }, &mut raw_buf).unwrap(); if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) { assert_eq!(Demo { a: 10, b: 20 }, data); assert_eq!(remaining.len(), 0); } else { panic!() } } #[test] #[cfg(feature = "heapless")] fn double_loop_test() { #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] struct Demo { a: u32, b: u8, } let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); let mut ser = crate::to_vec_cobs::<_, 128>(&Demo { a: 10, b: 20 }).unwrap(); let ser2 = crate::to_vec_cobs::<_, 128>(&Demo { a: 256854231, b: 115, }) .unwrap(); ser.extend(ser2); let (demo1, ser) = if let FeedResult::Success { data, remaining } = cobs_buf.feed(&ser[..]) { (data, remaining) } else { panic!() }; assert_eq!(Demo { a: 10, b: 20 }, demo1); let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed(ser) { assert_eq!(remaining.len(), 0); data } else { panic!() }; assert_eq!(Demo { a: 10, b: 20 }, demo1); assert_eq!( Demo { a: 256854231, b: 115 }, demo2 ); } #[test] #[cfg(feature = "heapless")] fn loop_test_ref() { #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] struct Demo<'a> { a: u32, b: u8, c: &'a str, } let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); let ser = crate::to_vec_cobs::<_, 128>(&Demo { a: 10, b: 20, c: "test", }) .unwrap(); if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) { assert_eq!( Demo { a: 10, b: 20, c: "test" }, data ); assert_eq!(remaining.len(), 0); } else { panic!() } } #[test] #[cfg(feature = "heapless")] fn double_loop_test_ref() { #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] struct Demo<'a> { a: u32, b: u8, c: &'a str, } let mut cobs_buf: CobsAccumulator<64> = CobsAccumulator::new(); let mut ser = crate::to_vec_cobs::<_, 128>(&Demo { a: 10, b: 20, c: "test", }) .unwrap(); let ser2 = crate::to_vec_cobs::<_, 128>(&Demo { a: 256854231, b: 115, c: "different test", }) .unwrap(); ser.extend(ser2); let (data, ser) = if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(&ser[..]) { (data, remaining) } else { panic!() }; assert!( Demo { a: 10, b: 20, c: "test" } == data ); let demo2 = if let FeedResult::Success { data, remaining } = cobs_buf.feed_ref(ser) { assert!(remaining.is_empty()); data } else { panic!() }; // Uncommenting the below line causes the test to no-longer compile, as cobs_buf would then be mutably borrowed twice //assert!(Demo { a: 10, b: 20, c : "test" } == data); assert!( Demo { a: 256854231, b: 115, c: "different test" } == demo2 ); } #[test] #[cfg(feature = "heapless")] fn extend_unchecked_in_bounds_test() { // Test bug present in revision abcb407: // extend_unchecked may be passed slice with size 1 greater than accumulator buffer causing panic #[derive(serde::Serialize, Deserialize, Debug, PartialEq, Eq)] struct Demo { data: [u8; 10], } let data = crate::to_vec_cobs::<_, 128>(&Demo { data: [0xcc; 10] }).unwrap(); assert_eq!(data.len(), 12); // 1 byte for offset + 1 sentinel byte appended // Accumulator has 1 byte less space than encoded message let mut acc: CobsAccumulator<11> = CobsAccumulator::new(); assert!(matches!( acc.feed::(&data[..]), FeedResult::OverFull(_) )); // Accumulator is juuuuust right let mut acc: CobsAccumulator<12> = CobsAccumulator::new(); assert!(matches!( acc.feed::(&data[..]), FeedResult::Success { .. } )); } } postcard-1.1.3/src/de/deserializer.rs000064400000000000000000000373511046102023000156650ustar 00000000000000use serde::de::{self, DeserializeSeed, IntoDeserializer, Visitor}; use crate::de::flavors::{Flavor, Slice}; use crate::error::{Error, Result}; use crate::varint::{max_of_last_byte, varint_max}; use core::marker::PhantomData; /// A `serde` compatible deserializer, generic over “Flavors” of deserializing plugins. /// /// Please note that postcard messages are not self-describing and therefore incompatible with /// [internally tagged enums](https://serde.rs/enum-representations.html#internally-tagged). pub struct Deserializer<'de, F: Flavor<'de>> { flavor: F, _plt: PhantomData<&'de ()>, } impl<'de, F> Deserializer<'de, F> where F: Flavor<'de> + 'de, { /// Obtain a Deserializer from a slice of bytes pub fn from_flavor(flavor: F) -> Self { Deserializer { flavor, _plt: PhantomData, } } /// Return the remaining (unused) bytes in the Deserializer along with any /// additional data provided by the [`Flavor`] pub fn finalize(self) -> Result { self.flavor.finalize() } } impl<'de> Deserializer<'de, Slice<'de>> { /// Obtain a Deserializer from a slice of bytes pub fn from_bytes(input: &'de [u8]) -> Self { Deserializer { flavor: Slice::new(input), _plt: PhantomData, } } } impl<'de, F: Flavor<'de>> Deserializer<'de, F> { #[cfg(target_pointer_width = "16")] #[inline(always)] fn try_take_varint_usize(&mut self) -> Result { self.try_take_varint_u16().map(|u| u as usize) } #[cfg(target_pointer_width = "32")] #[inline(always)] fn try_take_varint_usize(&mut self) -> Result { self.try_take_varint_u32().map(|u| u as usize) } #[cfg(target_pointer_width = "64")] #[inline(always)] fn try_take_varint_usize(&mut self) -> Result { self.try_take_varint_u64().map(|u| u as usize) } #[inline] fn try_take_varint_u16(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u16; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u32(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u32; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u64(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u64; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } #[inline] fn try_take_varint_u128(&mut self) -> Result { let mut out = 0; for i in 0..varint_max::() { let val = self.flavor.pop()?; let carry = (val & 0x7F) as u128; out |= carry << (7 * i); if (val & 0x80) == 0 { if i == varint_max::() - 1 && val > max_of_last_byte::() { return Err(Error::DeserializeBadVarint); } else { return Ok(out); } } } Err(Error::DeserializeBadVarint) } } struct SeqAccess<'a, 'b, F: Flavor<'b>> { deserializer: &'a mut Deserializer<'b, F>, len: usize, } impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::SeqAccess<'b> for SeqAccess<'a, 'b, F> { type Error = Error; #[inline] fn next_element_seed>(&mut self, seed: V) -> Result> { if self.len > 0 { self.len -= 1; Ok(Some(DeserializeSeed::deserialize( seed, &mut *self.deserializer, )?)) } else { Ok(None) } } #[inline] fn size_hint(&self) -> Option { match self.deserializer.flavor.size_hint() { Some(size) if size < self.len => None, _ => Some(self.len), } } } struct MapAccess<'a, 'b, F: Flavor<'b>> { deserializer: &'a mut Deserializer<'b, F>, len: usize, } impl<'a, 'b: 'a, F: Flavor<'b>> serde::de::MapAccess<'b> for MapAccess<'a, 'b, F> { type Error = Error; #[inline] fn next_key_seed>(&mut self, seed: K) -> Result> { if self.len > 0 { self.len -= 1; Ok(Some(DeserializeSeed::deserialize( seed, &mut *self.deserializer, )?)) } else { Ok(None) } } #[inline] fn next_value_seed>(&mut self, seed: V) -> Result { DeserializeSeed::deserialize(seed, &mut *self.deserializer) } #[inline] fn size_hint(&self) -> Option { Some(self.len) } } impl<'de, F: Flavor<'de>> de::Deserializer<'de> for &mut Deserializer<'de, F> { type Error = Error; #[inline] fn is_human_readable(&self) -> bool { false } // Postcard does not support structures not known at compile time #[inline] fn deserialize_any(self, _visitor: V) -> Result where V: Visitor<'de>, { // We wont ever support this. Err(Error::WontImplement) } // Take a boolean encoded as a u8 #[inline] fn deserialize_bool(self, visitor: V) -> Result where V: Visitor<'de>, { let val = match self.flavor.pop()? { 0 => false, 1 => true, _ => return Err(Error::DeserializeBadBool), }; visitor.visit_bool(val) } #[inline] fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_i8(self.flavor.pop()? as i8) } #[inline] fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u16()?; visitor.visit_i16(de_zig_zag_i16(v)) } #[inline] fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u32()?; visitor.visit_i32(de_zig_zag_i32(v)) } #[inline] fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u64()?; visitor.visit_i64(de_zig_zag_i64(v)) } #[inline] fn deserialize_i128(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u128()?; visitor.visit_i128(de_zig_zag_i128(v)) } #[inline] fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_u8(self.flavor.pop()?) } #[inline] fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u16()?; visitor.visit_u16(v) } #[inline] fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u32()?; visitor.visit_u32(v) } #[inline] fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u64()?; visitor.visit_u64(v) } #[inline] fn deserialize_u128(self, visitor: V) -> Result where V: Visitor<'de>, { let v = self.try_take_varint_u128()?; visitor.visit_u128(v) } #[inline] fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.flavor.try_take_n_temp(4)?; let mut buf = [0u8; 4]; buf.copy_from_slice(bytes); visitor.visit_f32(f32::from_bits(u32::from_le_bytes(buf))) } #[inline] fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { let bytes = self.flavor.try_take_n_temp(8)?; let mut buf = [0u8; 8]; buf.copy_from_slice(bytes); visitor.visit_f64(f64::from_bits(u64::from_le_bytes(buf))) } #[inline] fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; if sz > 4 { return Err(Error::DeserializeBadChar); } let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?; // we pass the character through string conversion because // this handles transforming the array of code units to a // codepoint. we can't use char::from_u32() because it expects // an already-processed codepoint. let character = core::str::from_utf8(bytes) .map_err(|_| Error::DeserializeBadChar)? .chars() .next() .ok_or(Error::DeserializeBadChar)?; visitor.visit_char(character) } #[inline] fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; let bytes: &'de [u8] = self.flavor.try_take_n(sz)?; let str_sl = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?; visitor.visit_borrowed_str(str_sl) } #[inline] fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?; let str_sl = core::str::from_utf8(bytes).map_err(|_| Error::DeserializeBadUtf8)?; visitor.visit_str(str_sl) } #[inline] fn deserialize_bytes(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; let bytes: &'de [u8] = self.flavor.try_take_n(sz)?; visitor.visit_borrowed_bytes(bytes) } #[inline] fn deserialize_byte_buf(self, visitor: V) -> Result where V: Visitor<'de>, { let sz = self.try_take_varint_usize()?; let bytes: &[u8] = self.flavor.try_take_n_temp(sz)?; visitor.visit_bytes(bytes) } #[inline] fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, { match self.flavor.pop()? { 0 => visitor.visit_none(), 1 => visitor.visit_some(self), _ => Err(Error::DeserializeBadOption), } } // In Serde, unit means an anonymous value containing no data. // Unit is not actually encoded in Postcard. #[inline] fn deserialize_unit(self, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_unit() } // Unit struct means a named value containing no data. // Unit structs are not actually encoded in Postcard. #[inline] fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { self.deserialize_unit(visitor) } #[inline] fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_newtype_struct(self) } #[inline] fn deserialize_seq(self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.try_take_varint_usize()?; visitor.visit_seq(SeqAccess { deserializer: self, len, }) } #[inline] fn deserialize_tuple(self, len: usize, visitor: V) -> Result where V: Visitor<'de>, { visitor.visit_seq(SeqAccess { deserializer: self, len, }) } #[inline] fn deserialize_tuple_struct( self, _name: &'static str, len: usize, visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(len, visitor) } #[inline] fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { let len = self.try_take_varint_usize()?; visitor.visit_map(MapAccess { deserializer: self, len, }) } #[inline] fn deserialize_struct( self, _name: &'static str, fields: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { self.deserialize_tuple(fields.len(), visitor) } #[inline] fn deserialize_enum( self, _name: &'static str, _variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { visitor.visit_enum(self) } // As a binary format, Postcard does not encode identifiers #[inline] fn deserialize_identifier(self, _visitor: V) -> Result where V: Visitor<'de>, { // Will not support Err(Error::WontImplement) } #[inline] fn deserialize_ignored_any(self, _visitor: V) -> Result where V: Visitor<'de>, { // Will not support Err(Error::WontImplement) } } impl<'de, F: Flavor<'de>> serde::de::VariantAccess<'de> for &mut Deserializer<'de, F> { type Error = Error; #[inline] fn unit_variant(self) -> Result<()> { Ok(()) } #[inline] fn newtype_variant_seed>(self, seed: V) -> Result { DeserializeSeed::deserialize(seed, self) } #[inline] fn tuple_variant>(self, len: usize, visitor: V) -> Result { serde::de::Deserializer::deserialize_tuple(self, len, visitor) } #[inline] fn struct_variant>( self, fields: &'static [&'static str], visitor: V, ) -> Result { serde::de::Deserializer::deserialize_tuple(self, fields.len(), visitor) } } impl<'de, F: Flavor<'de>> serde::de::EnumAccess<'de> for &mut Deserializer<'de, F> { type Error = Error; type Variant = Self; #[inline] fn variant_seed>(self, seed: V) -> Result<(V::Value, Self)> { let varint = self.try_take_varint_u32()?; let v = DeserializeSeed::deserialize(seed, varint.into_deserializer())?; Ok((v, self)) } } fn de_zig_zag_i16(n: u16) -> i16 { ((n >> 1) as i16) ^ (-((n & 0b1) as i16)) } fn de_zig_zag_i32(n: u32) -> i32 { ((n >> 1) as i32) ^ (-((n & 0b1) as i32)) } fn de_zig_zag_i64(n: u64) -> i64 { ((n >> 1) as i64) ^ (-((n & 0b1) as i64)) } fn de_zig_zag_i128(n: u128) -> i128 { ((n >> 1) as i128) ^ (-((n & 0b1) as i128)) } postcard-1.1.3/src/de/flavors.rs000064400000000000000000000547001046102023000146540ustar 00000000000000//! # Deserialization Flavors //! //! "Flavors" in `postcard` are used as modifiers to the serialization or deserialization //! process. Flavors typically modify one or both of the following: //! //! 1. The source medium of the deserialization, e.g. whether the data is serialized from a `[u8]` slice, or some other container //! 2. The format of the deserialization, such as if the original data is encoded in a COBS format, contains a CRC32 checksum //! appended to the message, etc. //! //! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for retrieving the bytes before they //! are passed to `serde` for deserialization //! //! Multiple flavors may be combined to obtain a desired combination of behavior and storage. //! When flavors are combined, it is expected that the storage flavor (such as [`Slice`]) is the innermost flavor. //! //! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in //! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome! //! //! ## Usability //! //! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the //! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent //! the user from having to specify generic parameters, setting correct initialization values, or handling the output of //! the flavor correctly. See `postcard::from_bytes()` for an example of this. //! //! ## When to use (multiple) flavors //! //! Combining flavors are nice for convenience, as they perform potentially multiple steps of //! serialization at one time. //! //! This can often be more memory efficient, as intermediate buffers are not typically required. //! //! ## When NOT to use (multiple) flavors //! //! The downside of passing deserialization through multiple steps is that it is typically slower than //! performing each step serially. Said simply, "cobs decoding while deserializing" is often slower //! than "cobs decode then deserialize", due to the ability to handle longer "runs" of data in each //! stage. The downside is that if these stages can not be performed in-place on the buffer, you //! will need additional buffers for each stage. //! //! Additionally, deserializating flavors can be more restrictive or difficult to work with than //! serialization flavors, as deserialization may require that the deserialized types borrow some //! portion of the original message. //! //! ## Examples //! //! ### Using a single flavor //! //! In the first example, we use the `Slice` flavor, to retrieve the serialized output from a `[u8]` slice. //! No other modification is made to the serialization process. //! //! ```rust //! use postcard::{ //! de_flavors::Slice, //! Deserializer, //! }; //! use serde::Deserialize; //! //! #[derive(Deserialize, Debug, PartialEq)] //! struct Tup(u8, u8, u8); //! //! let msg = [0x04, 0x00, 0x04, 0x01, 0x02, 0x03]; //! let slice = Slice::new(&msg); //! let mut deserializer = Deserializer::from_flavor(slice); //! let t = Tup::deserialize(&mut deserializer).unwrap(); //! assert_eq!(t, Tup(4, 0, 4)); //! let remainder = deserializer.finalize().unwrap(); //! assert_eq!(remainder, &[1, 2, 3]); //! ``` use crate::{Error, Result}; use core::marker::PhantomData; /// The deserialization Flavor trait /// /// This is used as the primary way to decode serialized data from some kind of buffer, /// or modify that data in a middleware style pattern. /// /// See the module level docs for an example of how flavors are used. pub trait Flavor<'de>: 'de { /// The remaining data of this flavor after deserializing has completed. /// /// Typically, this includes the remaining buffer that was not used for /// deserialization, and in cases of more complex flavors, any additional /// information that was decoded or otherwise calculated during /// the deserialization process. type Remainder: 'de; /// The source of data retrieved for deserialization. /// /// This is typically some sort of data buffer, or another Flavor, when /// chained behavior is desired type Source: 'de; /// Obtain the next byte for deserialization fn pop(&mut self) -> Result; /// Returns the number of bytes remaining in the message, if known. /// /// # Implementation notes /// /// It is not enforced that this number is exactly correct. /// A flavor may yield less or more bytes than the what is hinted at by /// this function. /// /// `size_hint()` is primarily intended to be used for optimizations such as /// reserving space for deserialized items, but must not be trusted to /// e.g., omit bounds checks in unsafe code. An incorrect implementation of /// `size_hint()` should not lead to memory safety violations. /// /// That said, the implementation should provide a correct estimation, /// because otherwise it would be a violation of the trait’s protocol. /// /// The default implementation returns `None` which is correct for any flavor. fn size_hint(&self) -> Option { None } /// Attempt to take the next `ct` bytes from the serialized message. /// /// This variant borrows the data from the input for zero-copy deserialization. If zero-copy /// deserialization is not necessary, prefer to use `try_take_n_temp` instead. fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]>; /// Attempt to take the next `ct` bytes from the serialized message. /// /// This variant does not guarantee that the returned value is borrowed from the input, so it /// cannot be used for zero-copy deserialization, but it also avoids needing to potentially /// allocate a data in a temporary buffer. /// /// This variant should be used instead of `try_take_n` /// if zero-copy deserialization is not necessary. /// /// It is only necessary to implement this method if the flavor requires storing data in a /// temporary buffer in order to implement the borrow semantics, e.g. the `std::io::Read` /// flavor. fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]> where 'de: 'a, { self.try_take_n(ct) } /// Complete the deserialization process. /// /// This is typically called separately, after the `serde` deserialization /// has completed. fn finalize(self) -> Result; } /// A simple [`Flavor`] representing the deserialization from a borrowed slice pub struct Slice<'de> { // This string starts with the input data and characters are truncated off // the beginning as data is parsed. pub(crate) cursor: *const u8, pub(crate) end: *const u8, pub(crate) _pl: PhantomData<&'de [u8]>, } impl<'de> Slice<'de> { /// Create a new [Slice] from the given buffer pub fn new(sli: &'de [u8]) -> Self { let range = sli.as_ptr_range(); Self { cursor: range.start, end: range.end, _pl: PhantomData, } } } impl<'de> Flavor<'de> for Slice<'de> { type Remainder = &'de [u8]; type Source = &'de [u8]; #[inline] fn pop(&mut self) -> Result { if self.cursor == self.end { Err(Error::DeserializeUnexpectedEnd) } else { // SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we // have checked above. unsafe { let res = Ok(*self.cursor); self.cursor = self.cursor.add(1); res } } } #[inline] fn size_hint(&self) -> Option { Some((self.end as usize) - (self.cursor as usize)) } #[inline] fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> { let remain = (self.end as usize) - (self.cursor as usize); if remain < ct { Err(Error::DeserializeUnexpectedEnd) } else { // SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented past `self.end` as we // have checked above. unsafe { let sli = core::slice::from_raw_parts(self.cursor, ct); self.cursor = self.cursor.add(ct); Ok(sli) } } } /// Return the remaining (unused) bytes in the Deserializer fn finalize(self) -> Result<&'de [u8]> { let remain = (self.end as usize) - (self.cursor as usize); // SAFETY: `self.cursor` is valid for `remain` elements unsafe { Ok(core::slice::from_raw_parts(self.cursor, remain)) } } } /// Support for [`std::io`] or `embedded-io` traits #[cfg(any( feature = "embedded-io-04", feature = "embedded-io-06", feature = "use-std" ))] pub mod io { use crate::{Error, Result}; use core::marker::PhantomData; struct SlidingBuffer<'de> { cursor: *mut u8, end: *const u8, _pl: PhantomData<&'de [u8]>, } impl<'de> SlidingBuffer<'de> { pub fn new(sli: &'de mut [u8]) -> Self { let range = sli.as_mut_ptr_range(); Self { cursor: range.start, end: range.end, _pl: PhantomData, } } #[inline] fn take_n(&mut self, ct: usize) -> Result<&'de mut [u8]> { let remain = (self.end as usize) - (self.cursor as usize); let buff = if remain < ct { return Err(Error::DeserializeUnexpectedEnd); } else { // SAFETY: `self.cursor` is valid for `ct` elements and won't be incremented // past `self.end` as we have checked above. unsafe { let sli = core::slice::from_raw_parts_mut(self.cursor, ct); self.cursor = self.cursor.add(ct); sli } }; Ok(buff) } #[inline] fn take_n_temp(&mut self, ct: usize) -> Result<&mut [u8]> { let remain = (self.end as usize) - (self.cursor as usize); let buff = if remain < ct { return Err(Error::DeserializeUnexpectedEnd); } else { unsafe { let sli = core::slice::from_raw_parts_mut(self.cursor, ct); sli } }; Ok(buff) } fn complete(self) -> Result<&'de mut [u8]> { let remain = (self.end as usize) - (self.cursor as usize); // SAFETY: `self.cursor` is valid for `remain` elements unsafe { Ok(core::slice::from_raw_parts_mut(self.cursor, remain)) } } } /// Support for [`embedded_io`](crate::eio::embedded_io) traits #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub mod eio { use super::super::Flavor; use super::SlidingBuffer; use crate::{Error, Result}; /// Wrapper over a [`embedded_io`](crate::eio::embedded_io)::[`Read`](crate::eio::Read) and a sliding buffer to implement the [`Flavor`] trait pub struct EIOReader<'de, T> where T: crate::eio::Read, { reader: T, buff: SlidingBuffer<'de>, } impl<'de, T> EIOReader<'de, T> where T: crate::eio::Read, { /// Create a new [`EIOReader`] from a reader and a buffer. /// /// `buff` must have enough space to hold all data read during the deserialisation. pub fn new(reader: T, buff: &'de mut [u8]) -> Self { Self { reader, buff: SlidingBuffer::new(buff), } } } impl<'de, T> Flavor<'de> for EIOReader<'de, T> where T: crate::eio::Read + 'de, { type Remainder = (T, &'de mut [u8]); type Source = &'de [u8]; #[inline] fn pop(&mut self) -> Result { let mut val = [0; 1]; self.reader .read_exact(&mut val) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(val[0]) } #[inline] fn size_hint(&self) -> Option { None } #[inline] fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> { let buff = self.buff.take_n(ct)?; self.reader .read_exact(buff) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(buff) } #[inline] fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]> where 'de: 'a, { let buff = self.buff.take_n_temp(ct)?; self.reader .read_exact(buff) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(buff) } /// Return the remaining (unused) bytes in the Deserializer fn finalize(self) -> Result<(T, &'de mut [u8])> { let buf = self.buff.complete()?; Ok((self.reader, buf)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_pop() { let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []); assert_eq!(reader.pop(), Ok(0xAA)); assert_eq!(reader.pop(), Ok(0xBB)); assert_eq!(reader.pop(), Ok(0xCC)); assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd)); } #[test] fn test_try_take_n() { let mut buf = [0; 8]; let mut reader = EIOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf); assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..])); assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..])); assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd)); } } } /// Support for [`std::io`] traits #[allow(clippy::module_inception)] #[cfg(feature = "use-std")] pub mod io { use super::super::Flavor; use super::SlidingBuffer; use crate::{Error, Result}; /// Wrapper over a [`std::io::Read`] and a sliding buffer to implement the [Flavor] trait pub struct IOReader<'de, T> where T: std::io::Read, { reader: T, buff: SlidingBuffer<'de>, } impl<'de, T> IOReader<'de, T> where T: std::io::Read, { /// Create a new [`IOReader`] from a reader and a buffer. /// /// `buff` must have enough space to hold all data read during the deserialisation. pub fn new(reader: T, buff: &'de mut [u8]) -> Self { Self { reader, buff: SlidingBuffer::new(buff), } } } impl<'de, T> Flavor<'de> for IOReader<'de, T> where T: std::io::Read + 'de, { type Remainder = (T, &'de mut [u8]); type Source = &'de [u8]; #[inline] fn pop(&mut self) -> Result { let mut val = [0; 1]; self.reader .read_exact(&mut val) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(val[0]) } #[inline] fn size_hint(&self) -> Option { None } #[inline] fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> { let buff = self.buff.take_n(ct)?; self.reader .read_exact(buff) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(buff) } #[inline] fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8]> where 'de: 'a, { let buff = self.buff.take_n_temp(ct)?; self.reader .read_exact(buff) .map_err(|_| Error::DeserializeUnexpectedEnd)?; Ok(buff) } /// Return the remaining (unused) bytes in the Deserializer fn finalize(self) -> Result<(T, &'de mut [u8])> { let buf = self.buff.complete()?; Ok((self.reader, buf)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_pop() { let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC][..], &mut []); assert_eq!(reader.pop(), Ok(0xAA)); assert_eq!(reader.pop(), Ok(0xBB)); assert_eq!(reader.pop(), Ok(0xCC)); assert_eq!(reader.pop(), Err(Error::DeserializeUnexpectedEnd)); } #[test] fn test_try_take_n() { let mut buf = [0; 8]; let mut reader = IOReader::new(&[0xAA, 0xBB, 0xCC, 0xDD, 0xEE][..], &mut buf); assert_eq!(reader.try_take_n(2), Ok(&[0xAA, 0xBB][..])); assert_eq!(reader.try_take_n(2), Ok(&[0xCC, 0xDD][..])); assert_eq!(reader.try_take_n(2), Err(Error::DeserializeUnexpectedEnd)); } } } } //////////////////////////////////////// // CRC //////////////////////////////////////// /// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on /// the serialized data. /// /// The flavor will check the CRC assuming that it has been appended to the bytes. /// CRCs are used for error detection when reading data back. /// Requires the `crc` feature. /// /// More on CRCs: . #[cfg(feature = "use-crc")] #[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))] pub mod crc { use core::convert::TryInto; use crc::Digest; use crc::Width; use serde::Deserialize; use super::Flavor; use super::Slice; use crate::Deserializer; use crate::Error; use crate::Result; /// Manages CRC modifications as a flavor. pub struct CrcModifier<'de, B, W> where B: Flavor<'de>, W: Width, { flav: B, digest: Digest<'de, W>, } impl<'de, B, W> CrcModifier<'de, B, W> where B: Flavor<'de>, W: Width, { /// Create a new Crc modifier Flavor. pub fn new(bee: B, digest: Digest<'de, W>) -> Self { Self { flav: bee, digest } } } macro_rules! impl_flavor { ($int:ty, $from_bytes:ident, $take_from_bytes:ident) => { impl<'de, B> Flavor<'de> for CrcModifier<'de, B, $int> where B: Flavor<'de>, { type Remainder = B::Remainder; type Source = B::Source; #[inline] fn pop(&mut self) -> Result { match self.flav.pop() { Ok(byte) => { self.digest.update(&[byte]); Ok(byte) } e @ Err(_) => e, } } #[inline] fn size_hint(&self) -> Option { self.flav.size_hint() } #[inline] fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8]> { match self.flav.try_take_n(ct) { Ok(bytes) => { self.digest.update(bytes); Ok(bytes) } e @ Err(_) => e, } } fn finalize(mut self) -> Result { match self.flav.try_take_n(core::mem::size_of::<$int>()) { Ok(prev_crc_bytes) => match self.flav.finalize() { Ok(remainder) => { let crc = self.digest.finalize(); let le_bytes = prev_crc_bytes .try_into() .map_err(|_| Error::DeserializeBadEncoding)?; let prev_crc = <$int>::from_le_bytes(le_bytes); if crc == prev_crc { Ok(remainder) } else { Err(Error::DeserializeBadCrc) } } e @ Err(_) => e, }, Err(e) => Err(e), } } } /// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any) /// of the byte slice is not returned. pub fn $from_bytes<'a, T>(s: &'a [u8], digest: Digest<'a, $int>) -> Result where T: Deserialize<'a>, { let flav = CrcModifier::new(Slice::new(s), digest); let mut deserializer = Deserializer::from_flavor(flav); let r = T::deserialize(&mut deserializer)?; let _ = deserializer.finalize()?; Ok(r) } /// Deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any) /// of the byte slice is returned for further usage pub fn $take_from_bytes<'a, T>( s: &'a [u8], digest: Digest<'a, $int>, ) -> Result<(T, &'a [u8])> where T: Deserialize<'a>, { let flav = CrcModifier::new(Slice::new(s), digest); let mut deserializer = Deserializer::from_flavor(flav); let t = T::deserialize(&mut deserializer)?; Ok((t, deserializer.finalize()?)) } }; } impl_flavor!(u8, from_bytes_u8, take_from_bytes_u8); impl_flavor!(u16, from_bytes_u16, take_from_bytes_u16); impl_flavor!(u32, from_bytes_u32, take_from_bytes_u32); impl_flavor!(u64, from_bytes_u64, take_from_bytes_u64); impl_flavor!(u128, from_bytes_u128, take_from_bytes_u128); } postcard-1.1.3/src/de/mod.rs000064400000000000000000000473171046102023000137650ustar 00000000000000use cobs::{decode_in_place, decode_in_place_report}; use serde::Deserialize; pub(crate) mod deserializer; pub mod flavors; use crate::error::{Error, Result}; use deserializer::Deserializer; /// Deserialize a message of type `T` from a byte slice. The unused portion (if any) /// of the byte slice is not returned. pub fn from_bytes<'a, T>(s: &'a [u8]) -> Result where T: Deserialize<'a>, { let mut deserializer = Deserializer::from_bytes(s); let t = T::deserialize(&mut deserializer)?; Ok(t) } /// Deserialize a message of type `T` from a cobs-encoded byte slice. /// /// The unused portion (if any) of the byte slice is not returned. /// The used portion of the input slice is modified during deserialization (even if an error is returned). /// Therefore, if this is not desired, pass a clone of the original slice. pub fn from_bytes_cobs<'a, T>(s: &'a mut [u8]) -> Result where T: Deserialize<'a>, { let sz = decode_in_place(s).map_err(|_| Error::DeserializeBadEncoding)?; from_bytes::(&s[..sz]) } /// Deserialize a message of type `T` from a cobs-encoded byte slice. /// /// The unused portion (if any) of the byte slice is returned for further usage. /// The used portion of the input slice is modified during deserialization (even if an error is returned). /// Therefore, if this is not desired, pass a clone of the original slice. pub fn take_from_bytes_cobs<'a, T>(s: &'a mut [u8]) -> Result<(T, &'a mut [u8])> where T: Deserialize<'a>, { let mut report = decode_in_place_report(s).map_err(|_| Error::DeserializeBadEncoding)?; // The report does not include terminator bytes. If there is one in the // buffer right AFTER the message, also include it. if s.get(report.src_used) == Some(&0) { report.src_used += 1; } // First split off the amount used for the "destination", which includes our now // decoded message to deserialize let (dst_used, dst_unused) = s.split_at_mut(report.dst_used); // Then create a slice that includes the unused bytes, but DON'T include the // excess bytes that were "shrunk" away from the original message let (_unused, src_unused) = dst_unused.split_at_mut(report.src_used - report.dst_used); Ok((from_bytes::(dst_used)?, src_unused)) } /// Deserialize a message of type `T` from a byte slice. The unused portion (if any) /// of the byte slice is returned for further usage pub fn take_from_bytes<'a, T>(s: &'a [u8]) -> Result<(T, &'a [u8])> where T: Deserialize<'a>, { let mut deserializer = Deserializer::from_bytes(s); let t = T::deserialize(&mut deserializer)?; Ok((t, deserializer.finalize()?)) } /// Deserialize a message of type `T` from a [`embedded_io`](crate::eio::embedded_io)::[`Read`](crate::eio::Read). #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub fn from_eio<'a, T, R>(val: (R, &'a mut [u8])) -> Result<(T, (R, &'a mut [u8]))> where T: Deserialize<'a>, R: crate::eio::Read + 'a, { let flavor = flavors::io::eio::EIOReader::new(val.0, val.1); let mut deserializer = Deserializer::from_flavor(flavor); let t = T::deserialize(&mut deserializer)?; Ok((t, deserializer.finalize()?)) } /// Deserialize a message of type `T` from a [`std::io::Read`]. #[cfg(feature = "use-std")] pub fn from_io<'a, T, R>(val: (R, &'a mut [u8])) -> Result<(T, (R, &'a mut [u8]))> where T: Deserialize<'a>, R: std::io::Read + 'a, { let flavor = flavors::io::io::IOReader::new(val.0, val.1); let mut deserializer = Deserializer::from_flavor(flavor); let t = T::deserialize(&mut deserializer)?; Ok((t, deserializer.finalize()?)) } /// Conveniently deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any) /// of the byte slice is not returned. /// /// See the `de_flavors::crc` module for the complete set of functions. #[cfg(feature = "use-crc")] #[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))] #[inline] pub fn from_bytes_crc32<'a, T>(s: &'a [u8], digest: crc::Digest<'a, u32>) -> Result where T: Deserialize<'a>, { flavors::crc::from_bytes_u32(s, digest) } /// Conveniently deserialize a message of type `T` from a byte slice with a Crc. The unused portion (if any) /// of the byte slice is returned for further usage /// /// See the `de_flavors::crc` module for the complete set of functions. #[cfg(feature = "use-crc")] #[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))] #[inline] pub fn take_from_bytes_crc32<'a, T>( s: &'a [u8], digest: crc::Digest<'a, u32>, ) -> Result<(T, &'a [u8])> where T: Deserialize<'a>, { flavors::crc::take_from_bytes_u32(s, digest) } //////////////////////////////////////////////////////////////////////////////// #[cfg(feature = "heapless")] #[cfg(test)] mod test_heapless { use super::*; use crate::{ser::to_vec, to_vec_cobs, varint::varint_max}; use core::fmt::Write; use core::ops::Deref; use heapless::{FnvIndexMap, String, Vec}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[test] fn de_u8() { let output: Vec = to_vec(&0x05u8).unwrap(); assert_eq!(&[5], output.deref()); let out: u8 = from_bytes(output.deref()).unwrap(); assert_eq!(out, 0x05); } #[test] fn de_u16() { let output: Vec() }> = to_vec(&0xA5C7u16).unwrap(); assert_eq!(&[0xC7, 0xCB, 0x02], output.deref()); let out: u16 = from_bytes(output.deref()).unwrap(); assert_eq!(out, 0xA5C7); } #[test] fn de_u32() { let output: Vec() }> = to_vec(&0xCDAB3412u32).unwrap(); assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref()); let out: u32 = from_bytes(output.deref()).unwrap(); assert_eq!(out, 0xCDAB3412u32); } #[test] fn de_u64() { let output: Vec() }> = to_vec(&0x1234_5678_90AB_CDEFu64).unwrap(); assert_eq!( &[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12], output.deref() ); let out: u64 = from_bytes(output.deref()).unwrap(); assert_eq!(out, 0x1234_5678_90AB_CDEFu64); } #[test] fn de_u128() { let output: Vec() }> = to_vec(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap(); assert_eq!( &[ 0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x92, 0xDE, 0xB7, 0xDE, 0x8A, 0x92, 0x9E, 0xAB, 0xB4, 0x24, ], output.deref() ); let out: u128 = from_bytes(output.deref()).unwrap(); assert_eq!(out, 0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128); } #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] struct BasicU8S { st: u16, ei: u8, ote: u128, sf: u64, tt: u32, } #[test] fn de_struct_unsigned() { let data = BasicU8S { st: 0xABCD, ei: 0xFE, ote: 0x1234_4321_ABCD_DCBA_1234_4321_ABCD_DCBA, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }; const SZ: usize = varint_max::() + 1 + varint_max::() + varint_max::() + varint_max::(); let output: Vec = to_vec(&data).unwrap(); assert_eq!( &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x92, 0xF4, 0xF2, 0xEE, 0xBC, 0xB5, 0xC8, 0xA1, 0xB4, 0x24, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A ], output.deref() ); let out: BasicU8S = from_bytes(output.deref()).unwrap(); assert_eq!(out, data); } #[test] fn de_byte_slice() { let input: &[u8] = &[1u8, 2, 3, 4, 5, 6, 7, 8]; let output: Vec = to_vec(input).unwrap(); assert_eq!( &[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], output.deref() ); let out: Vec = from_bytes(output.deref()).unwrap(); assert_eq!(input, out.deref()); let mut input: Vec = Vec::new(); for i in 0..1024 { input.push((i & 0xFF) as u8).unwrap(); } let output: Vec = to_vec(input.deref()).unwrap(); assert_eq!(&[0x80, 0x08], &output.deref()[..2]); assert_eq!(output.len(), 1026); for (i, val) in output.deref()[2..].iter().enumerate() { assert_eq!((i & 0xFF) as u8, *val); } let de: Vec = from_bytes(output.deref()).unwrap(); assert_eq!(input.deref(), de.deref()); } #[test] fn de_str() { let input: &str = "hello, postcard!"; let output: Vec = to_vec(input).unwrap(); assert_eq!(0x10, output.deref()[0]); assert_eq!(input.as_bytes(), &output.deref()[1..]); let mut input: String<1024> = String::new(); for _ in 0..256 { write!(&mut input, "abcd").unwrap(); } let output: Vec = to_vec(input.deref()).unwrap(); assert_eq!(&[0x80, 0x08], &output.deref()[..2]); assert_eq!(output.len(), 1026); for ch in output.deref()[2..].chunks(4) { assert_eq!("abcd", core::str::from_utf8(ch).unwrap()); } let de: String<1024> = from_bytes(output.deref()).unwrap(); assert_eq!(input.deref(), de.deref()); } #[allow(dead_code)] #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] enum BasicEnum { Bib, Bim, Bap, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct EnumStruct { eight: u8, sixt: u16, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] enum DataEnum { Bib(u16), Bim(u64), Bap(u8), Kim(EnumStruct), Chi { a: u8, b: u32 }, Sho(u16, u8), } #[test] fn enums() { let output: Vec = to_vec(&BasicEnum::Bim).unwrap(); assert_eq!(&[0x01], output.deref()); let out: BasicEnum = from_bytes(output.deref()).unwrap(); assert_eq!(out, BasicEnum::Bim); let output: Vec() }> = to_vec(&DataEnum::Bim(u64::MAX)).unwrap(); assert_eq!( &[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01], output.deref() ); let output: Vec() }> = to_vec(&DataEnum::Bib(u16::MAX)).unwrap(); assert_eq!(&[0x00, 0xFF, 0xFF, 0x03], output.deref()); let out: DataEnum = from_bytes(output.deref()).unwrap(); assert_eq!(out, DataEnum::Bib(u16::MAX)); let output: Vec = to_vec(&DataEnum::Bap(u8::MAX)).unwrap(); assert_eq!(&[0x02, 0xFF], output.deref()); let out: DataEnum = from_bytes(output.deref()).unwrap(); assert_eq!(out, DataEnum::Bap(u8::MAX)); let output: Vec = to_vec(&DataEnum::Kim(EnumStruct { eight: 0xF0, sixt: 0xACAC, })) .unwrap(); assert_eq!(&[0x03, 0xF0, 0xAC, 0xD9, 0x02], output.deref()); let out: DataEnum = from_bytes(output.deref()).unwrap(); assert_eq!( out, DataEnum::Kim(EnumStruct { eight: 0xF0, sixt: 0xACAC }) ); let output: Vec = to_vec(&DataEnum::Chi { a: 0x0F, b: 0xC7C7C7C7, }) .unwrap(); assert_eq!(&[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], output.deref()); let out: DataEnum = from_bytes(output.deref()).unwrap(); assert_eq!( out, DataEnum::Chi { a: 0x0F, b: 0xC7C7C7C7 } ); let output: Vec = to_vec(&DataEnum::Sho(0x6969, 0x07)).unwrap(); assert_eq!(&[0x05, 0xE9, 0xD2, 0x01, 0x07], output.deref()); let out: DataEnum = from_bytes(output.deref()).unwrap(); assert_eq!(out, DataEnum::Sho(0x6969, 0x07)); } #[test] fn tuples() { let output: Vec = to_vec(&(1u8, 10u32, "Hello!")).unwrap(); assert_eq!( &[1u8, 0x0A, 0x06, b'H', b'e', b'l', b'l', b'o', b'!'], output.deref() ); let out: (u8, u32, &str) = from_bytes(output.deref()).unwrap(); assert_eq!(out, (1u8, 10u32, "Hello!")); } #[derive(Debug, Eq, PartialEq)] pub struct ByteSliceStruct<'a> { bytes: &'a [u8], } impl Serialize for ByteSliceStruct<'_> { fn serialize(&self, serializer: S) -> core::result::Result where S: Serializer, { // Serialization is generic for all slice types, so the default serialization of byte // slices does not use `Serializer::serialize_bytes`. serializer.serialize_bytes(self.bytes) } } impl<'a, 'de> Deserialize<'de> for ByteSliceStruct<'a> where 'de: 'a, { fn deserialize(deserializer: D) -> core::result::Result where D: Deserializer<'de>, { // Deserialization of byte slices is specialized for byte slices, so the default // deserialization will call `Deserializer::deserialize_bytes`. Ok(Self { bytes: Deserialize::deserialize(deserializer)?, }) } } #[test] fn bytes() { let x: &[u8; 32] = &[0u8; 32]; let output: Vec = to_vec(x).unwrap(); assert_eq!(output.len(), 32); let out: [u8; 32] = from_bytes(output.deref()).unwrap(); assert_eq!(out, [0u8; 32]); let x: &[u8] = &[0u8; 32]; let output: Vec = to_vec(x).unwrap(); assert_eq!(output.len(), 33); let out: &[u8] = from_bytes(output.deref()).unwrap(); assert_eq!(out, [0u8; 32]); let x = ByteSliceStruct { bytes: &[0u8; 32] }; let output: Vec = to_vec(&x).unwrap(); assert_eq!(output.len(), 33); let out: ByteSliceStruct<'_> = from_bytes(output.deref()).unwrap(); assert_eq!(out, ByteSliceStruct { bytes: &[0u8; 32] }); } #[test] fn chars() { let x: char = 'a'; let output: Vec = to_vec(&x).unwrap(); assert_eq!(output.len(), 2); let out: char = from_bytes(output.deref()).unwrap(); assert_eq!(out, 'a'); } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct NewTypeStruct(u32); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct TupleStruct((u8, u16)); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] pub struct DualTupleStruct(u8, u16); #[test] fn structs() { let output: Vec = to_vec(&NewTypeStruct(5)).unwrap(); assert_eq!(&[0x05], output.deref()); let out: NewTypeStruct = from_bytes(output.deref()).unwrap(); assert_eq!(out, NewTypeStruct(5)); let output: Vec = to_vec(&TupleStruct((0xA0, 0x1234))).unwrap(); assert_eq!(&[0xA0, 0xB4, 0x24], output.deref()); let out: TupleStruct = from_bytes(output.deref()).unwrap(); assert_eq!(out, TupleStruct((0xA0, 0x1234))); let output: Vec = to_vec(&DualTupleStruct(0xA0, 0x1234)).unwrap(); assert_eq!(&[0xA0, 0xB4, 0x24], output.deref()); let out: DualTupleStruct = from_bytes(output.deref()).unwrap(); assert_eq!(out, DualTupleStruct(0xA0, 0x1234)); } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } #[test] fn ref_struct() { let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let output: Vec = to_vec(&RefStruct { bytes: &bytes, str_s: message, }) .unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); let out: RefStruct<'_> = from_bytes(output.deref()).unwrap(); assert_eq!( out, RefStruct { bytes: &bytes, str_s: message, } ); } #[test] fn unit() { let output: Vec = to_vec(&()).unwrap(); assert_eq!(output.len(), 0); let _: () = from_bytes(output.deref()).unwrap(); } #[test] fn heapless_data() { let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref()); let out: Vec = from_bytes(output.deref()).unwrap(); assert_eq!(out, input); let mut input: String<8> = String::new(); write!(&mut input, "helLO!").unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x06, b'h', b'e', b'l', b'L', b'O', b'!'], output.deref()); let out: String<8> = from_bytes(output.deref()).unwrap(); assert_eq!(input, out); let mut input: FnvIndexMap = FnvIndexMap::new(); input.insert(0x01, 0x05).unwrap(); input.insert(0x02, 0x06).unwrap(); input.insert(0x03, 0x07).unwrap(); input.insert(0x04, 0x08).unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!( &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], output.deref() ); let out: FnvIndexMap = from_bytes(output.deref()).unwrap(); assert_eq!(input, out); } #[test] fn cobs_test() { let message = "hElLo"; let bytes = [0x01, 0x00, 0x02, 0x20]; let input = RefStruct { bytes: &bytes, str_s: message, }; let output: Vec = to_vec(&input).unwrap(); let mut encode_buf = [0u8; 32]; let sz = cobs::encode(output.deref(), &mut encode_buf); let out = from_bytes_cobs::>(&mut encode_buf[..sz]).unwrap(); assert_eq!(input, out); } #[test] fn take_from_includes_terminator() { // With the null terminator let mut output: Vec = to_vec_cobs(&(4i32, 0u8, 4u64)).unwrap(); let (val, remain) = take_from_bytes_cobs::<(i32, u8, u64)>(&mut output).unwrap(); assert_eq!((4, 0, 4), val); assert_eq!(remain.len(), 0); // without the null terminator let mut output: Vec = to_vec_cobs(&(4i32, 0u8, 4u64)).unwrap(); assert_eq!(output.pop(), Some(0)); let (val, remain) = take_from_bytes_cobs::<(i32, u8, u64)>(&mut output).unwrap(); assert_eq!((4, 0, 4), val); assert_eq!(remain.len(), 0); } } #[cfg(any(feature = "alloc", feature = "use-std"))] #[cfg(test)] mod test_alloc { extern crate alloc; use super::*; use alloc::vec; use serde::Deserialize; #[derive(Debug, Deserialize, PartialEq)] struct ZSTStruct; #[test] fn zst_vec() { assert_eq!(from_bytes(&[3]), Ok(vec![ZSTStruct, ZSTStruct, ZSTStruct])); assert_eq!( from_bytes(&[4]), Ok(vec![ZSTStruct, ZSTStruct, ZSTStruct, ZSTStruct]) ); } #[test] fn vec() { assert_eq!( from_bytes::>(&[8, 255, 255, 255, 0, 0, 0, 0, 0]), Ok(vec![255, 255, 255, 0, 0, 0, 0, 0]) ); // This won't actually prove anything since tests will likely always be // run on devices with larger amounts of memory, but it can't hurt. assert_eq!( from_bytes::>(&[(1 << 7) | 8, 255, 255, 255, 0, 0, 0, 0, 0]), Err(Error::DeserializeUnexpectedEnd) ); } } postcard-1.1.3/src/eio.rs000064400000000000000000000014431046102023000133600ustar 00000000000000// We disable all embedded-io versions but the most recent in docs.rs, because we use // --all-features which doesn't work with non-additive features. #[cfg(all(feature = "embedded-io-04", feature = "embedded-io-06", not(docsrs)))] compile_error!("Only one version of `embedded-io` must be enabled through features"); #[cfg(all(feature = "embedded-io-04", not(docsrs)))] mod version_impl { pub use embedded_io_04 as embedded_io; pub use embedded_io_04::blocking::{Read, Write}; } #[cfg(feature = "embedded-io-06")] mod version_impl { pub use embedded_io_06 as embedded_io; pub use embedded_io_06::{Read, Write}; } // All versions should export the appropriate items #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub use version_impl::{embedded_io, Read, Write}; postcard-1.1.3/src/error.rs000064400000000000000000000067171046102023000137460ustar 00000000000000use core::fmt::{Display, Formatter}; use serde::{Deserialize, Serialize}; /// This is the error type used by Postcard #[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] #[cfg_attr(feature = "use-defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { /// This is a feature that postcard will never implement WontImplement, /// This is a feature that postcard intends to support, but does not yet NotYetImplemented, /// The serialize buffer is full SerializeBufferFull, /// The length of a sequence must be known SerializeSeqLengthUnknown, /// Hit the end of buffer, expected more data DeserializeUnexpectedEnd, /// Found a varint that didn't terminate. Is the usize too big for this platform? DeserializeBadVarint, /// Found a bool that wasn't 0 or 1 DeserializeBadBool, /// Found an invalid unicode char DeserializeBadChar, /// Tried to parse invalid utf-8 DeserializeBadUtf8, /// Found an Option discriminant that wasn't 0 or 1 DeserializeBadOption, /// Found an enum discriminant that was > `u32::MAX` DeserializeBadEnum, /// The original data was not well encoded DeserializeBadEncoding, /// Bad CRC while deserializing DeserializeBadCrc, /// Serde Serialization Error SerdeSerCustom, /// Serde Deserialization Error SerdeDeCustom, /// Error while processing `collect_str` during serialization CollectStrError, } impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { use Error::*; write!( f, "{}", match self { WontImplement => "This is a feature that PostCard will never implement", NotYetImplemented => { "This is a feature that Postcard intends to support, but does not yet" } SerializeBufferFull => "The serialize buffer is full", SerializeSeqLengthUnknown => "The length of a sequence must be known", DeserializeUnexpectedEnd => "Hit the end of buffer, expected more data", DeserializeBadVarint => { "Found a varint that didn't terminate. Is the usize too big for this platform?" } DeserializeBadBool => "Found a bool that wasn't 0 or 1", DeserializeBadChar => "Found an invalid unicode char", DeserializeBadUtf8 => "Tried to parse invalid utf-8", DeserializeBadOption => "Found an Option discriminant that wasn't 0 or 1", DeserializeBadEnum => "Found an enum discriminant that was > u32::max_value()", DeserializeBadEncoding => "The original data was not well encoded", DeserializeBadCrc => "Bad CRC while deserializing", SerdeSerCustom => "Serde Serialization Error", SerdeDeCustom => "Serde Deserialization Error", CollectStrError => "Error while processing `collect_str` during serialization", } ) } } /// This is the Result type used by Postcard. pub type Result = ::core::result::Result; impl serde::ser::Error for Error { fn custom(_msg: T) -> Self where T: Display, { Error::SerdeSerCustom } } impl serde::de::Error for Error { fn custom(_msg: T) -> Self where T: Display, { Error::SerdeDeCustom } } impl serde::ser::StdError for Error {} postcard-1.1.3/src/fixint.rs000064400000000000000000000132731046102023000141110ustar 00000000000000//! # Fixed Size Integers //! //! In some cases, the use of variably length encoded data may not be //! preferable. These modules, for use with `#[serde(with = ...)]` //! "opt out" of variable length encoding. //! //! Support explicitly not provided for `usize` or `isize`, as //! these types would not be portable between systems of different //! pointer widths. //! //! Although all data in Postcard is typically encoded in little-endian //! order, these modules provide a choice to the user to encode the data //! in either little or big endian form, which may be useful for zero-copy //! applications. use serde::{Deserialize, Serialize, Serializer}; /// Use with the `#[serde(with = "postcard::fixint::le")]` field attribute. /// /// Disables varint serialization/deserialization for the specified integer /// field. The integer will always be serialized in the same way as a fixed /// size array, in **Little Endian** order on the wire. /// /// ```rust /// # use serde::Serialize; /// #[derive(Serialize)] /// pub struct DefinitelyLittleEndian { /// #[serde(with = "postcard::fixint::le")] /// x: u16, /// } /// ``` pub mod le { use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::LE; /// Serialize the integer value as a little-endian fixed-size array. pub fn serialize(val: &T, serializer: S) -> Result where S: Serializer, T: Copy, LE: Serialize, { LE(*val).serialize(serializer) } /// Deserialize the integer value from a little-endian fixed-size array. pub fn deserialize<'de, D, T>(deserializer: D) -> Result where D: Deserializer<'de>, LE: Deserialize<'de>, { LE::::deserialize(deserializer).map(|x| x.0) } } /// Disables varint serialization/deserialization for the specified integer field. /// /// Use with the `#[serde(with = "postcard::fixint::be")]` field attribute. /// The integer will always be serialized in the same way as a fixed /// size array, in **Big Endian** order on the wire. /// /// ```rust /// # use serde::Serialize; /// #[derive(Serialize)] /// pub struct DefinitelyBigEndian { /// #[serde(with = "postcard::fixint::be")] /// x: u16, /// } /// ``` pub mod be { use serde::{Deserialize, Deserializer, Serialize, Serializer}; use super::BE; /// Serialize the integer value as a big-endian fixed-size array. pub fn serialize(val: &T, serializer: S) -> Result where S: Serializer, T: Copy, BE: Serialize, { BE(*val).serialize(serializer) } /// Deserialize the integer value from a big-endian fixed-size array. pub fn deserialize<'de, D, T>(deserializer: D) -> Result where D: Deserializer<'de>, BE: Deserialize<'de>, { BE::::deserialize(deserializer).map(|x| x.0) } } #[doc(hidden)] pub struct LE(T); #[doc(hidden)] pub struct BE(T); macro_rules! impl_fixint { ($( $int:ty ),*) => { $( impl Serialize for LE<$int> { #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.to_le_bytes().serialize(serializer) } } impl<'de> Deserialize<'de> for LE<$int> { #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { <_ as Deserialize>::deserialize(deserializer) .map(<$int>::from_le_bytes) .map(Self) } } impl Serialize for BE<$int> { #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, { self.0.to_be_bytes().serialize(serializer) } } impl<'de> Deserialize<'de> for BE<$int> { #[inline] fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { <_ as Deserialize>::deserialize(deserializer) .map(<$int>::from_be_bytes) .map(Self) } } )* }; } impl_fixint![i16, i32, i64, i128, u16, u32, u64, u128]; #[cfg(test)] mod tests { use serde::{Deserialize, Serialize}; #[test] fn test_little_endian() { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct DefinitelyLE { #[serde(with = "crate::fixint::le")] x: u16, } let input = DefinitelyLE { x: 0xABCD }; let mut buf = [0; 32]; let serialized = crate::to_slice(&input, &mut buf).unwrap(); assert_eq!(serialized, &[0xCD, 0xAB]); let deserialized: DefinitelyLE = crate::from_bytes(serialized).unwrap(); assert_eq!(deserialized, input); } #[test] fn test_big_endian() { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub struct DefinitelyBE { #[serde(with = "crate::fixint::be")] x: u16, } let input = DefinitelyBE { x: 0xABCD }; let mut buf = [0; 32]; let serialized = crate::to_slice(&input, &mut buf).unwrap(); assert_eq!(serialized, &[0xAB, 0xCD]); let deserialized: DefinitelyBE = crate::from_bytes(serialized).unwrap(); assert_eq!(deserialized, input); } } postcard-1.1.3/src/lib.rs000064400000000000000000000073541046102023000133610ustar 00000000000000#![cfg_attr(not(any(test, feature = "use-std")), no_std)] #![warn(missing_docs)] #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] #![cfg_attr(docsrs, feature(doc_cfg))] pub mod accumulator; mod de; mod eio; mod error; pub mod fixint; mod ser; mod varint; // Still experimental! Don't make pub pub. pub(crate) mod max_size; /// # Experimental Postcard Features /// /// Items inside this module require various feature flags, and are not /// subject to SemVer stability. Items may be removed or deprecated at /// any point. /// /// ## Derive /// /// The `experimental-derive` feature enables one experimental feature: /// /// * Max size calculation /// /// ### Max Size Calculation /// /// This features enables calculation of the Max serialized size of a message as /// an associated `usize` constant called `POSTCARD_MAX_SIZE`. It also provides a /// `#[derive(MaxSize)]` macro that can be used for calculating user types. /// /// This is useful for determining the maximum buffer size needed when receiving /// or sending a message that has been serialized. /// /// NOTE: This only covers the size of "plain" flavored messages, e.g. not with COBS /// or any other Flavors applied. The overhead for these flavors must be calculated /// separately. /// /// Please report any missing types, or any incorrectly calculated values. /// /// ### Message Schema Generation /// /// This now lives in the `postcard-schema` crate. pub mod experimental { /// Compile time max-serialization size calculation #[cfg(feature = "experimental-derive")] #[cfg_attr(docsrs, doc(cfg(feature = "experimental-derive")))] pub mod max_size { // NOTE: This is the trait... pub use crate::max_size::MaxSize; // NOTE: ...and this is the derive macro pub use postcard_derive::MaxSize; } pub use crate::ser::serialized_size; } pub use de::deserializer::Deserializer; pub use de::flavors as de_flavors; pub use de::{from_bytes, from_bytes_cobs, take_from_bytes, take_from_bytes_cobs}; pub use error::{Error, Result}; pub use ser::flavors as ser_flavors; pub use ser::{serialize_with_flavor, serializer::Serializer, to_extend, to_slice, to_slice_cobs}; #[cfg(feature = "heapless")] pub use ser::{to_vec, to_vec_cobs}; #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub use ser::to_eio; #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub use de::from_eio; #[cfg(feature = "use-std")] pub use ser::{to_io, to_stdvec, to_stdvec_cobs}; #[cfg(feature = "use-std")] pub use de::from_io; #[cfg(feature = "alloc")] pub use ser::{to_allocvec, to_allocvec_cobs}; #[cfg(feature = "use-crc")] pub use { de::{from_bytes_crc32, take_from_bytes_crc32}, ser::to_slice_crc32, }; #[cfg(all(feature = "use-crc", feature = "heapless"))] pub use ser::to_vec_crc32; #[cfg(all(feature = "use-crc", feature = "use-std"))] pub use ser::to_stdvec_crc32; #[cfg(all(feature = "use-crc", feature = "alloc"))] pub use ser::to_allocvec_crc32; #[cfg(test)] mod test { #[test] fn varint_boundary_canon() { let x = u32::MAX; let mut buf = [0u8; 5]; let used = crate::to_slice(&x, &mut buf).unwrap(); let deser: u32 = crate::from_bytes(used).unwrap(); assert_eq!(deser, u32::MAX); assert_eq!(used, &mut [0xFF, 0xFF, 0xFF, 0xFF, 0x0F]); let deser: Result = crate::from_bytes(&[0xFF, 0xFF, 0xFF, 0xFF, 0x1F]); assert_eq!(deser, Err(crate::Error::DeserializeBadVarint)); } #[test] fn signed_int128() { let x = -19490127978232325886905073712831_i128; let mut buf = [0u8; 32]; let used = crate::to_slice(&x, &mut buf).unwrap(); let deser: i128 = crate::from_bytes(used).unwrap(); assert_eq!(deser, x); } } postcard-1.1.3/src/max_size.rs000064400000000000000000000231521046102023000144240ustar 00000000000000#[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] use alloc::{boxed::Box, rc::Rc}; #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] use alloc::sync::Arc; use crate::varint::varint_max; use core::{ marker::PhantomData, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }, ops::{Range, RangeFrom, RangeInclusive, RangeTo}, time::Duration, }; /// This trait is used to enforce the maximum size required to /// store the serialization of a given type. pub trait MaxSize { /// The maximum possible size that the serialization of this /// type can have, in bytes. const POSTCARD_MAX_SIZE: usize; } impl MaxSize for bool { const POSTCARD_MAX_SIZE: usize = 1; } impl MaxSize for i8 { const POSTCARD_MAX_SIZE: usize = 1; } impl MaxSize for i16 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for i32 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for i64 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for i128 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for isize { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u8 { const POSTCARD_MAX_SIZE: usize = 1; } impl MaxSize for u16 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u32 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u64 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for u128 { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for usize { const POSTCARD_MAX_SIZE: usize = varint_max::(); } impl MaxSize for f32 { const POSTCARD_MAX_SIZE: usize = 4; } impl MaxSize for f64 { const POSTCARD_MAX_SIZE: usize = 8; } impl MaxSize for char { const POSTCARD_MAX_SIZE: usize = 5; } impl MaxSize for Option { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE + 1; } impl MaxSize for Result { const POSTCARD_MAX_SIZE: usize = max(T::POSTCARD_MAX_SIZE, E::POSTCARD_MAX_SIZE) + 1; } impl MaxSize for () { const POSTCARD_MAX_SIZE: usize = 0; } impl MaxSize for [T; N] { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * N; } impl MaxSize for &'_ T { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } impl MaxSize for &'_ mut T { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroI8 { const POSTCARD_MAX_SIZE: usize = i8::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroI16 { const POSTCARD_MAX_SIZE: usize = i16::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroI32 { const POSTCARD_MAX_SIZE: usize = i32::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroI64 { const POSTCARD_MAX_SIZE: usize = i64::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroI128 { const POSTCARD_MAX_SIZE: usize = i128::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroIsize { const POSTCARD_MAX_SIZE: usize = isize::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroU8 { const POSTCARD_MAX_SIZE: usize = u8::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroU16 { const POSTCARD_MAX_SIZE: usize = u16::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroU32 { const POSTCARD_MAX_SIZE: usize = u32::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroU64 { const POSTCARD_MAX_SIZE: usize = u64::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroU128 { const POSTCARD_MAX_SIZE: usize = u128::POSTCARD_MAX_SIZE; } impl MaxSize for NonZeroUsize { const POSTCARD_MAX_SIZE: usize = usize::POSTCARD_MAX_SIZE; } impl MaxSize for Duration { const POSTCARD_MAX_SIZE: usize = u64::POSTCARD_MAX_SIZE + u32::POSTCARD_MAX_SIZE; } impl MaxSize for PhantomData { const POSTCARD_MAX_SIZE: usize = 0; } impl MaxSize for (A,) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B, C) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B, C, D) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE + D::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B, C, D, E) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE + D::POSTCARD_MAX_SIZE + E::POSTCARD_MAX_SIZE; } impl MaxSize for (A, B, C, D, E, F) { const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE + D::POSTCARD_MAX_SIZE + E::POSTCARD_MAX_SIZE + F::POSTCARD_MAX_SIZE; } impl MaxSize for Range { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2; } impl MaxSize for RangeInclusive { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2; } impl MaxSize for RangeFrom { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } impl MaxSize for RangeTo { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } impl MaxSize for core::num::Wrapping { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(all(feature = "core-num-saturating", feature = "experimental-derive"))] #[cfg_attr(docsrs, doc(cfg(feature = "core-num-saturating")))] impl MaxSize for core::num::Saturating { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl MaxSize for Box { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(all(feature = "alloc", target_has_atomic = "ptr"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "alloc", target_has_atomic = "ptr"))))] impl MaxSize for Arc { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] impl MaxSize for Rc { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl MaxSize for heapless::Vec { const POSTCARD_MAX_SIZE: usize = <[T; N]>::POSTCARD_MAX_SIZE + varint_size(N); } #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] impl MaxSize for heapless::String { const POSTCARD_MAX_SIZE: usize = <[u8; N]>::POSTCARD_MAX_SIZE + varint_size(N); } #[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))] #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))] impl MaxSize for nalgebra_v0_33::Matrix< T, nalgebra_v0_33::Const, nalgebra_v0_33::Const, nalgebra_v0_33::ArrayStorage, > where T: MaxSize + nalgebra_v0_33::Scalar, { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * R * C; } #[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))] #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))] impl MaxSize for nalgebra_v0_33::Unit { const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE; } #[cfg(all(feature = "nalgebra-v0_33", feature = "experimental-derive"))] #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra-v0_33")))] impl MaxSize for nalgebra_v0_33::Quaternion { const POSTCARD_MAX_SIZE: usize = nalgebra_v0_33::Vector4::::POSTCARD_MAX_SIZE; } #[cfg(feature = "heapless")] const fn varint_size(max_n: usize) -> usize { const BITS_PER_BYTE: usize = 8; const BITS_PER_VARINT_BYTE: usize = 7; if max_n == 0 { return 1; } // How many data bits do we need for `max_n`. let bits = core::mem::size_of::() * BITS_PER_BYTE - max_n.leading_zeros() as usize; // We add (BITS_PER_BYTE - 1), to ensure any integer divisions // with a remainder will always add exactly one full byte, but // an evenly divided number of bits will be the same let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1); // Apply division, using normal "round down" integer division roundup_bits / BITS_PER_VARINT_BYTE } const fn max(lhs: usize, rhs: usize) -> usize { if lhs > rhs { lhs } else { rhs } } #[cfg(any(feature = "alloc", feature = "use-std"))] #[cfg(test)] mod tests { extern crate alloc; use super::*; use alloc::rc::Rc; #[cfg(target_has_atomic = "ptr")] use alloc::sync::Arc; #[test] fn box_max_size() { assert_eq!(Box::::POSTCARD_MAX_SIZE, 1); assert_eq!(Box::::POSTCARD_MAX_SIZE, 5); assert_eq!(Box::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); } #[test] #[cfg(target_has_atomic = "ptr")] fn arc_max_size() { assert_eq!(Arc::::POSTCARD_MAX_SIZE, 1); assert_eq!(Arc::::POSTCARD_MAX_SIZE, 5); assert_eq!(Arc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); } #[test] fn rc_max_size() { assert_eq!(Rc::::POSTCARD_MAX_SIZE, 1); assert_eq!(Rc::::POSTCARD_MAX_SIZE, 5); assert_eq!(Rc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27); } } postcard-1.1.3/src/ser/flavors.rs000064400000000000000000000530541046102023000150560ustar 00000000000000//! # Serialization Flavors //! //! "Flavors" in `postcard` are used as modifiers to the serialization or deserialization //! process. Flavors typically modify one or both of the following: //! //! 1. The output medium of the serialization, e.g. whether the data is serialized to a `[u8]` slice, or a `heapless::Vec`. //! 2. The format of the serialization, such as encoding the serialized output in a COBS format, performing CRC32 checksumming while serializing, etc. //! //! Flavors are implemented using the [`Flavor`] trait, which acts as a "middleware" for receiving the bytes as serialized by `serde`. //! Multiple flavors may be combined to obtain a desired combination of behavior and storage. //! When flavors are combined, it is expected that the storage flavor (such as `Slice` or `HVec`) is the innermost flavor. //! //! Custom flavors may be defined by users of the `postcard` crate, however some commonly useful flavors have been provided in //! this module. If you think your custom flavor would be useful to others, PRs adding flavors are very welcome! //! //! ## Usability //! //! Flavors may not always be convenient to use directly, as they may expose some implementation details of how the //! inner workings of the flavor behaves. It is typical to provide a convenience method for using a flavor, to prevent //! the user from having to specify generic parameters, setting correct initialization values, or handling the output of //! the flavor correctly. See `postcard::to_vec()` for an example of this. //! //! It is recommended to use the [`serialize_with_flavor()`](../fn.serialize_with_flavor.html) method for serialization. See it's documentation for information //! regarding usage and generic type parameters. //! //! ## When to use (multiple) flavors //! //! Combining flavors are nice for convenience, as they perform potentially multiple steps of //! serialization at one time. //! //! This can often be more memory efficient, as intermediate buffers are not typically required. //! //! ## When NOT to use (multiple) flavors //! //! The downside of passing serialization through multiple steps is that it is typically slower than //! performing each step serially. Said simply, "cobs encoding while serializing" is often slower //! than "serialize then cobs encode", due to the ability to handle longer "runs" of data in each //! stage. The downside is that if these stages can not be performed in-place on the buffer, you //! will need additional buffers for each stage. //! //! ## Examples //! //! ### Using a single flavor //! //! In the first example, we use the `Slice` flavor, to store the serialized output into a mutable `[u8]` slice. //! No other modification is made to the serialization process. //! //! ```rust //! use postcard::{ //! serialize_with_flavor, //! ser_flavors::Slice, //! }; //! //! let mut buf = [0u8; 32]; //! //! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; //! let buffer = &mut [0u8; 32]; //! let res = serialize_with_flavor::<[u8], Slice, &mut [u8]>( //! data, //! Slice::new(buffer) //! ).unwrap(); //! //! assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30]); //! ``` //! //! ### Using combined flavors //! //! In the second example, we mix `Slice` with `Cobs`, to cobs encode the output while //! the data is serialized. Notice how `Slice` (the storage flavor) is the innermost flavor used. //! //! ```rust //! use postcard::{ //! serialize_with_flavor, //! ser_flavors::{Cobs, Slice}, //! }; //! //! let mut buf = [0u8; 32]; //! //! let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; //! let buffer = &mut [0u8; 32]; //! let res = serialize_with_flavor::<[u8], Cobs, &mut [u8]>( //! data, //! Cobs::try_new(Slice::new(buffer)).unwrap(), //! ).unwrap(); //! //! assert_eq!(res, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]); //! ``` use crate::error::{Error, Result}; use cobs::{EncoderState, PushResult}; use core::marker::PhantomData; use core::ops::Index; use core::ops::IndexMut; #[cfg(feature = "heapless")] pub use heapless_vec::*; #[cfg(feature = "use-std")] pub use std_vec::*; #[cfg(feature = "alloc")] pub use alloc_vec::*; #[cfg(feature = "alloc")] extern crate alloc; /// The serialization Flavor trait /// /// This is used as the primary way to encode serialized data into some kind of buffer, /// or modify that data in a middleware style pattern. /// /// See the module level docs for an example of how flavors are used. pub trait Flavor { /// The `Output` type is what this storage "resolves" to when the serialization is complete, /// such as a slice or a Vec of some sort. type Output; /// Override this method when you want to customize processing /// multiple bytes at once, such as copying a slice to the output, /// rather than iterating over one byte at a time. #[inline] fn try_extend(&mut self, data: &[u8]) -> Result<()> { data.iter().try_for_each(|d| self.try_push(*d)) } /// Push a single byte to be modified and/or stored. fn try_push(&mut self, data: u8) -> Result<()>; /// Finalize the serialization process. fn finalize(self) -> Result; } //////////////////////////////////////// // Slice //////////////////////////////////////// /// The `Slice` flavor is a storage flavor, storing the serialized (or otherwise modified) bytes into a plain /// `[u8]` slice. The `Slice` flavor resolves into a sub-slice of the original slice buffer. pub struct Slice<'a> { start: *mut u8, cursor: *mut u8, end: *mut u8, _pl: PhantomData<&'a [u8]>, } impl<'a> Slice<'a> { /// Create a new `Slice` flavor from a given backing buffer pub fn new(buf: &'a mut [u8]) -> Self { let ptr = buf.as_mut_ptr_range(); Slice { start: ptr.start, cursor: ptr.start, end: ptr.end, _pl: PhantomData, } } } impl<'a> Flavor for Slice<'a> { type Output = &'a mut [u8]; #[inline(always)] fn try_push(&mut self, b: u8) -> Result<()> { if self.cursor == self.end { Err(Error::SerializeBufferFull) } else { // SAFETY: `self.cursor` is in-bounds and won't be incremented past `self.end` as we // have checked above. unsafe { self.cursor.write(b); self.cursor = self.cursor.add(1); } Ok(()) } } #[inline(always)] fn try_extend(&mut self, b: &[u8]) -> Result<()> { let remain = (self.end as usize) - (self.cursor as usize); let blen = b.len(); if blen > remain { Err(Error::SerializeBufferFull) } else { // SAFETY: `self.cursor` is in-bounds for `blen` elements and won't be incremented past // `self.end` as we have checked above. unsafe { core::ptr::copy_nonoverlapping(b.as_ptr(), self.cursor, blen); self.cursor = self.cursor.add(blen); } Ok(()) } } fn finalize(self) -> Result { let used = (self.cursor as usize) - (self.start as usize); // SAFETY: `self.cursor` is in-bounds for `used` elements let sli = unsafe { core::slice::from_raw_parts_mut(self.start, used) }; Ok(sli) } } impl Index for Slice<'_> { type Output = u8; fn index(&self, idx: usize) -> &u8 { let len = (self.end as usize) - (self.start as usize); assert!(idx < len); // SAFETY: `self.start` is in-bounds at `idx` unsafe { &*self.start.add(idx) } } } impl IndexMut for Slice<'_> { fn index_mut(&mut self, idx: usize) -> &mut u8 { let len = (self.end as usize) - (self.start as usize); assert!(idx < len); // SAFETY: `self.start` is in-bounds at `idx` unsafe { &mut *self.start.add(idx) } } } /// Wrapper over a [`core::iter::Extend`] that implements the flavor trait pub struct ExtendFlavor { iter: T, } impl ExtendFlavor where T: core::iter::Extend, { /// Create a new [`Self`] flavor from a given [`core::iter::Extend`] pub fn new(iter: T) -> Self { Self { iter } } } impl Flavor for ExtendFlavor where T: core::iter::Extend, { type Output = T; #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.iter.extend([data]); Ok(()) } #[inline(always)] fn try_extend(&mut self, b: &[u8]) -> Result<()> { self.iter.extend(b.iter().copied()); Ok(()) } fn finalize(self) -> Result { Ok(self.iter) } } /// Support for the [`embedded-io`](crate::eio::embedded_io) traits #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub mod eio { use super::Flavor; use crate::{Error, Result}; /// Wrapper over a [`embedded_io Write`](crate::eio::Write) that implements the flavor trait pub struct WriteFlavor { writer: T, } impl WriteFlavor where T: crate::eio::Write, { /// Create a new [`Self`] flavor from a given [`embedded_io Write`](crate::eio::Write) pub fn new(writer: T) -> Self { Self { writer } } } impl Flavor for WriteFlavor where T: crate::eio::Write, { type Output = T; #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.writer .write_all(&[data]) .map_err(|_| Error::SerializeBufferFull)?; Ok(()) } #[inline(always)] fn try_extend(&mut self, b: &[u8]) -> Result<()> { self.writer .write_all(b) .map_err(|_| Error::SerializeBufferFull)?; Ok(()) } fn finalize(mut self) -> Result { self.writer .flush() .map_err(|_| Error::SerializeBufferFull)?; Ok(self.writer) } } } /// Support for the [`std::io`] traits #[cfg(feature = "use-std")] pub mod io { use super::Flavor; use crate::{Error, Result}; /// Wrapper over a [`std::io::Write`] that implements the flavor trait pub struct WriteFlavor { writer: T, } impl WriteFlavor where T: std::io::Write, { /// Create a new [`Self`] flavor from a given [`std::io::Write`] pub fn new(writer: T) -> Self { Self { writer } } } impl Flavor for WriteFlavor where T: std::io::Write, { type Output = T; #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.writer .write_all(&[data]) .map_err(|_| Error::SerializeBufferFull)?; Ok(()) } #[inline(always)] fn try_extend(&mut self, b: &[u8]) -> Result<()> { self.writer .write_all(b) .map_err(|_| Error::SerializeBufferFull)?; Ok(()) } fn finalize(mut self) -> Result { self.writer .flush() .map_err(|_| Error::SerializeBufferFull)?; Ok(self.writer) } } } #[cfg(feature = "heapless")] mod heapless_vec { use super::Flavor; use super::Index; use super::IndexMut; use crate::{Error, Result}; use heapless::Vec; //////////////////////////////////////// // HVec //////////////////////////////////////// /// The `HVec` flavor is a wrapper type around a `heapless::Vec`. This is a stack /// allocated data structure, with a fixed maximum size and variable amount of contents. #[derive(Default)] pub struct HVec { /// the contained data buffer vec: Vec, } impl HVec { /// Create a new, currently empty, [`heapless::Vec`] to be used for storing serialized /// output data. pub fn new() -> Self { Self::default() } } impl Flavor for HVec { type Output = Vec; #[inline(always)] fn try_extend(&mut self, data: &[u8]) -> Result<()> { self.vec .extend_from_slice(data) .map_err(|_| Error::SerializeBufferFull) } #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.vec.push(data).map_err(|_| Error::SerializeBufferFull) } fn finalize(self) -> Result> { Ok(self.vec) } } impl Index for HVec { type Output = u8; fn index(&self, idx: usize) -> &u8 { &self.vec[idx] } } impl IndexMut for HVec { fn index_mut(&mut self, idx: usize) -> &mut u8 { &mut self.vec[idx] } } } #[cfg(feature = "use-std")] mod std_vec { /// The `StdVec` flavor is a wrapper type around a `std::vec::Vec`. /// /// This type is only available when the (non-default) `use-std` feature is active pub type StdVec = super::alloc_vec::AllocVec; } #[cfg(feature = "alloc")] mod alloc_vec { extern crate alloc; use super::Flavor; use super::Index; use super::IndexMut; use crate::Result; use alloc::vec::Vec; /// The `AllocVec` flavor is a wrapper type around an [`alloc::vec::Vec`]. /// /// This type is only available when the (non-default) `alloc` feature is active #[derive(Default)] pub struct AllocVec { /// The vec to be used for serialization vec: Vec, } impl AllocVec { /// Create a new, currently empty, [`alloc::vec::Vec`] to be used for storing serialized /// output data. pub fn new() -> Self { Self::default() } } impl Flavor for AllocVec { type Output = Vec; #[inline(always)] fn try_extend(&mut self, data: &[u8]) -> Result<()> { self.vec.extend_from_slice(data); Ok(()) } #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.vec.push(data); Ok(()) } fn finalize(self) -> Result { Ok(self.vec) } } impl Index for AllocVec { type Output = u8; #[inline] fn index(&self, idx: usize) -> &u8 { &self.vec[idx] } } impl IndexMut for AllocVec { #[inline] fn index_mut(&mut self, idx: usize) -> &mut u8 { &mut self.vec[idx] } } } //////////////////////////////////////////////////////////////////////////////// // Modification Flavors //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////// // COBS //////////////////////////////////////// /// The `Cobs` flavor implements [Consistent Overhead Byte Stuffing] on /// the serialized data. The output of this flavor includes the termination/sentinel /// byte of `0x00`. /// /// This protocol is useful when sending data over a serial interface without framing such as a UART /// /// [Consistent Overhead Byte Stuffing]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing pub struct Cobs where B: Flavor + IndexMut, { flav: B, cobs: EncoderState, } impl Cobs where B: Flavor + IndexMut, { /// Create a new Cobs modifier Flavor. If there is insufficient space /// to push the leading header byte, the method will return an Error pub fn try_new(mut bee: B) -> Result { bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?; Ok(Self { flav: bee, cobs: EncoderState::default(), }) } } impl Flavor for Cobs where B: Flavor + IndexMut, { type Output = ::Output; #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { use PushResult::*; match self.cobs.push(data) { AddSingle(n) => self.flav.try_push(n), ModifyFromStartAndSkip((idx, mval)) => { self.flav[idx] = mval; self.flav.try_push(0) } ModifyFromStartAndPushAndSkip((idx, mval, nval)) => { self.flav[idx] = mval; self.flav.try_push(nval)?; self.flav.try_push(0) } } } fn finalize(mut self) -> Result { let (idx, mval) = self.cobs.finalize(); self.flav[idx] = mval; self.flav.try_push(0)?; self.flav.finalize() } } //////////////////////////////////////// // CRC //////////////////////////////////////// /// This Cyclic Redundancy Check flavor applies [the CRC crate's `Algorithm`](https://docs.rs/crc/latest/crc/struct.Algorithm.html) struct on /// the serialized data. /// /// The output of this flavor receives the CRC appended to the bytes. /// CRCs are used for error detection when reading data back. /// Requires the `crc` feature. /// /// More on CRCs: . #[cfg(feature = "use-crc")] #[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))] pub mod crc { use crc::Digest; use crc::Width; use serde::Serialize; #[cfg(feature = "alloc")] use super::alloc; use super::Flavor; use super::Slice; use crate::serialize_with_flavor; use crate::Result; /// Manages CRC modifications as a flavor. pub struct CrcModifier<'a, B, W> where B: Flavor, W: Width, { flav: B, digest: Digest<'a, W>, } impl<'a, B, W> CrcModifier<'a, B, W> where B: Flavor, W: Width, { /// Create a new CRC modifier Flavor. pub fn new(bee: B, digest: Digest<'a, W>) -> Self { Self { flav: bee, digest } } } macro_rules! impl_flavor { ($int:ty, $to_slice:ident, $to_vec:ident, $to_allocvec:ident) => { impl<'a, B> Flavor for CrcModifier<'a, B, $int> where B: Flavor, { type Output = ::Output; #[inline(always)] fn try_push(&mut self, data: u8) -> Result<()> { self.digest.update(&[data]); self.flav.try_push(data) } fn finalize(mut self) -> Result { let crc = self.digest.finalize(); for byte in crc.to_le_bytes() { self.flav.try_push(byte)?; } self.flav.finalize() } } /// Serialize a `T` to the given slice, with the resulting slice containing /// data followed by a CRC. The CRC bytes are included in the output buffer. /// /// When successful, this function returns the slice containing the /// serialized and encoded message. pub fn $to_slice<'a, T>( value: &T, buf: &'a mut [u8], digest: Digest<'_, $int>, ) -> Result<&'a mut [u8]> where T: Serialize + ?Sized, { serialize_with_flavor(value, CrcModifier::new(Slice::new(buf), digest)) } /// Serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data followed by a CRC. The CRC bytes are included in the output `Vec`. #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] pub fn $to_vec( value: &T, digest: Digest<'_, $int>, ) -> Result> where T: Serialize + ?Sized, { use super::HVec; serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest)) } /// Serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data followed by a CRC. The CRC bytes are included in the output `Vec`. #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn $to_allocvec( value: &T, digest: Digest<'_, $int>, ) -> Result> where T: Serialize + ?Sized, { use super::AllocVec; serialize_with_flavor(value, CrcModifier::new(AllocVec::new(), digest)) } }; } impl_flavor!(u8, to_slice_u8, to_vec_u8, to_allocvec_u8); impl_flavor!(u16, to_slice_u16, to_vec_u16, to_allocvec_u16); impl_flavor!(u32, to_slice_u32, to_vec_u32, to_allocvec_u32); impl_flavor!(u64, to_slice_u64, to_vec_u64, to_allocvec_u64); impl_flavor!(u128, to_slice_u128, to_vec_u128, to_allocvec_u128); } /// The `Size` flavor is a measurement flavor, which accumulates the number of bytes needed to /// serialize the data. /// /// ``` /// use postcard::{serialize_with_flavor, ser_flavors}; /// /// let value = false; /// let size = serialize_with_flavor(&value, ser_flavors::Size::default()).unwrap(); /// /// assert_eq!(size, 1); /// ``` #[derive(Default)] pub struct Size { size: usize, } impl Flavor for Size { type Output = usize; #[inline(always)] fn try_push(&mut self, _b: u8) -> Result<()> { self.size += 1; Ok(()) } #[inline(always)] fn try_extend(&mut self, b: &[u8]) -> Result<()> { self.size += b.len(); Ok(()) } fn finalize(self) -> Result { Ok(self.size) } } postcard-1.1.3/src/ser/mod.rs000064400000000000000000000656701046102023000141700ustar 00000000000000use crate::error::{Error, Result}; use crate::ser::flavors::{Cobs, Flavor, Slice}; use serde::Serialize; #[cfg(feature = "heapless")] use crate::ser::flavors::HVec; #[cfg(feature = "heapless")] use heapless::Vec; #[cfg(feature = "alloc")] use crate::ser::flavors::AllocVec; #[cfg(feature = "alloc")] extern crate alloc; use crate::ser::serializer::Serializer; pub mod flavors; pub(crate) mod serializer; /// Serialize a `T` to the given slice, with the resulting slice containing /// data in a serialized then COBS encoded format. The terminating sentinel /// `0x00` byte is included in the output buffer. /// /// When successful, this function returns the slice containing the /// serialized and encoded message. /// /// ## Example /// /// ```rust /// use postcard::to_slice_cobs; /// let mut buf = [0u8; 32]; /// /// let used = to_slice_cobs(&false, &mut buf).unwrap(); /// assert_eq!(used, &[0x01, 0x01, 0x00]); /// /// let used = to_slice_cobs("1", &mut buf).unwrap(); /// assert_eq!(used, &[0x03, 0x01, b'1', 0x00]); /// /// let used = to_slice_cobs("Hi!", &mut buf).unwrap(); /// assert_eq!(used, &[0x05, 0x03, b'H', b'i', b'!', 0x00]); /// /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let used = to_slice_cobs(data, &mut buf).unwrap(); /// assert_eq!(used, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]); /// ``` pub fn to_slice_cobs<'a, 'b, T>(value: &'b T, buf: &'a mut [u8]) -> Result<&'a mut [u8]> where T: Serialize + ?Sized, { serialize_with_flavor::>, &'a mut [u8]>( value, Cobs::try_new(Slice::new(buf))?, ) } /// Serialize a `T` to the given slice, with the resulting slice containing /// data in a serialized format. /// /// When successful, this function returns the slice containing the /// serialized message /// /// ## Example /// /// ```rust /// use postcard::to_slice; /// let mut buf = [0u8; 32]; /// /// let used = to_slice(&true, &mut buf).unwrap(); /// assert_eq!(used, &[0x01]); /// /// let used = to_slice("Hi!", &mut buf).unwrap(); /// assert_eq!(used, &[0x03, b'H', b'i', b'!']); /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let used = to_slice(data, &mut buf).unwrap(); /// assert_eq!(used, &[0x04, 0x01, 0x00, 0x20, 0x30]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let used = to_slice(data, &mut buf).unwrap(); /// assert_eq!(used, &[0x01, 0x00, 0x20, 0x30]); /// ``` pub fn to_slice<'a, 'b, T>(value: &'b T, buf: &'a mut [u8]) -> Result<&'a mut [u8]> where T: Serialize + ?Sized, { serialize_with_flavor::, &'a mut [u8]>(value, Slice::new(buf)) } /// Serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data in a serialized then COBS encoded format. The terminating sentinel /// `0x00` byte is included in the output `Vec`. /// /// ## Example /// /// ```rust /// use postcard::to_vec_cobs; /// use heapless::Vec; /// use core::ops::Deref; /// /// let ser: Vec = to_vec_cobs(&false).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x01, 0x00]); /// /// let ser: Vec = to_vec_cobs("Hi!").unwrap(); /// assert_eq!(ser.deref(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]); /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = to_vec_cobs(data).unwrap(); /// assert_eq!(ser.deref(), &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = to_vec_cobs(data).unwrap(); /// assert_eq!(ser.deref(), &[0x02, 0x01, 0x03, 0x20, 0x30, 0x00]); /// ``` #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] pub fn to_vec_cobs(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::>, Vec>(value, Cobs::try_new(HVec::default())?) } /// Serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data in a serialized format. /// /// ## Example /// /// ```rust /// use postcard::to_vec; /// use heapless::Vec; /// use core::ops::Deref; /// /// let ser: Vec = to_vec(&true).unwrap(); /// assert_eq!(ser.deref(), &[0x01]); /// /// let ser: Vec = to_vec("Hi!").unwrap(); /// assert_eq!(ser.deref(), &[0x03, b'H', b'i', b'!']); /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = to_vec(data).unwrap(); /// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = to_vec(data).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30]); /// ``` #[cfg(feature = "heapless")] #[cfg_attr(docsrs, doc(cfg(feature = "heapless")))] pub fn to_vec(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::, Vec>(value, HVec::default()) } /// Serialize a `T` to a `std::vec::Vec`. /// /// ## Example /// /// ```rust /// use postcard::to_stdvec; /// /// let ser: Vec = to_stdvec(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x01]); /// /// let ser: Vec = to_stdvec("Hi!").unwrap(); /// assert_eq!(ser.as_slice(), &[0x03, b'H', b'i', b'!']); /// ``` #[cfg(feature = "use-std")] #[cfg_attr(docsrs, doc(cfg(feature = "use-std")))] #[inline] pub fn to_stdvec(value: &T) -> Result> where T: Serialize + ?Sized, { to_allocvec(value) } /// Serialize and COBS encode a `T` to a `std::vec::Vec`. /// /// The terminating sentinel `0x00` byte is included in the output. /// /// ## Example /// /// ```rust /// use postcard::to_stdvec_cobs; /// /// let ser: Vec = to_stdvec_cobs(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]); /// /// let ser: Vec = to_stdvec_cobs("Hi!").unwrap(); /// assert_eq!(ser.as_slice(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]); /// ``` #[cfg(feature = "use-std")] #[cfg_attr(docsrs, doc(cfg(feature = "use-std")))] #[inline] pub fn to_stdvec_cobs(value: &T) -> Result> where T: Serialize + ?Sized, { to_allocvec_cobs(value) } /// Serialize a `T` to an `alloc::vec::Vec`. /// /// ## Example /// /// ```rust /// use postcard::to_allocvec; /// /// let ser: Vec = to_allocvec(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x01]); /// /// let ser: Vec = to_allocvec("Hi!").unwrap(); /// assert_eq!(ser.as_slice(), &[0x03, b'H', b'i', b'!']); /// ``` #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn to_allocvec(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::>(value, AllocVec::new()) } /// Serialize and COBS encode a `T` to an `alloc::vec::Vec`. /// /// The terminating sentinel `0x00` byte is included in the output. /// /// ## Example /// /// ```rust /// use postcard::to_allocvec_cobs; /// /// let ser: Vec = to_allocvec_cobs(&true).unwrap(); /// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]); /// /// let ser: Vec = to_allocvec_cobs("Hi!").unwrap(); /// assert_eq!(ser.as_slice(), &[0x05, 0x03, b'H', b'i', b'!', 0x00]); /// ``` #[cfg(feature = "alloc")] #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] pub fn to_allocvec_cobs(value: &T) -> Result> where T: Serialize + ?Sized, { serialize_with_flavor::, alloc::vec::Vec>( value, Cobs::try_new(AllocVec::new())?, ) } /// Serialize a `T` to a [`core::iter::Extend`], /// ## Example /// /// ```rust /// use postcard::to_extend; /// let mut vec = Vec::new(); /// /// let ser = to_extend(&true, vec).unwrap(); /// let vec = to_extend("Hi!", ser).unwrap(); /// assert_eq!(&vec[0..5], &[0x01, 0x03, b'H', b'i', b'!']); /// ``` pub fn to_extend(value: &T, writer: W) -> Result where T: Serialize + ?Sized, W: core::iter::Extend, { serialize_with_flavor::(value, flavors::ExtendFlavor::new(writer)) } /// Serialize a `T` to an [`embedded_io Write`](crate::eio::Write), /// ## Example /// /// ```rust /// use postcard::to_eio; /// let mut buf: [u8; 32] = [0; 32]; /// let mut writer: &mut [u8] = &mut buf; /// /// let ser = to_eio(&true, &mut writer).unwrap(); /// to_eio("Hi!", ser).unwrap(); /// assert_eq!(&buf[0..5], &[0x01, 0x03, b'H', b'i', b'!']); /// ``` #[cfg(any(feature = "embedded-io-04", feature = "embedded-io-06"))] pub fn to_eio(value: &T, writer: W) -> Result where T: Serialize + ?Sized, W: crate::eio::Write, { serialize_with_flavor::(value, flavors::eio::WriteFlavor::new(writer)) } /// Serialize a `T` to a [`std::io::Write`], /// ## Example /// /// ```rust /// use postcard::to_io; /// let mut buf: [u8; 32] = [0; 32]; /// let mut writer: &mut [u8] = &mut buf; /// /// let ser = to_io(&true, &mut writer).unwrap(); /// to_io("Hi!", ser).unwrap(); /// assert_eq!(&buf[0..5], &[0x01, 0x03, b'H', b'i', b'!']); /// ``` #[cfg(feature = "use-std")] pub fn to_io(value: &T, writer: W) -> Result where T: Serialize + ?Sized, W: std::io::Write, { serialize_with_flavor::(value, flavors::io::WriteFlavor::new(writer)) } /// Conveniently serialize a `T` to the given slice, with the resulting slice containing /// data followed by a 32-bit CRC. The CRC bytes are included in the output buffer. /// /// When successful, this function returns the slice containing the /// serialized and encoded message. /// /// ## Example /// /// ```rust /// use crc::{Crc, CRC_32_ISCSI}; /// /// let mut buf = [0; 9]; /// /// let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; /// let crc = Crc::::new(&CRC_32_ISCSI); /// let used = postcard::to_slice_crc32(data, &mut buf, crc.digest()).unwrap(); /// assert_eq!(used, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); /// ``` /// /// See the `ser_flavors::crc` module for the complete set of functions. #[cfg(feature = "use-crc")] #[cfg_attr(docsrs, doc(cfg(feature = "use-crc")))] #[inline] pub fn to_slice_crc32<'a, T>( value: &T, buf: &'a mut [u8], digest: crc::Digest<'_, u32>, ) -> Result<&'a mut [u8]> where T: Serialize + ?Sized, { flavors::crc::to_slice_u32(value, buf, digest) } /// Conveniently serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`. /// /// ## Example /// /// ```rust /// use crc::{Crc, CRC_32_ISCSI}; /// use heapless::Vec; /// use core::ops::Deref; /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let crc = Crc::::new(&CRC_32_ISCSI); /// let ser: Vec = postcard::to_vec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = postcard::to_vec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]); /// ``` /// /// See the `ser_flavors::crc` module for the complete set of functions. #[cfg(all(feature = "use-crc", feature = "heapless"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "heapless"))))] #[inline] pub fn to_vec_crc32( value: &T, digest: crc::Digest<'_, u32>, ) -> Result> where T: Serialize + ?Sized, { flavors::crc::to_vec_u32(value, digest) } /// Conveniently serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`. /// /// ## Example /// /// ```rust /// use crc::{Crc, CRC_32_ISCSI}; /// use core::ops::Deref; /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let crc = Crc::::new(&CRC_32_ISCSI); /// let ser: Vec = postcard::to_stdvec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = postcard::to_stdvec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]); /// ``` /// /// See the `ser_flavors::crc` module for the complete set of functions. #[cfg(all(feature = "use-crc", feature = "use-std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "use-std"))))] #[inline] pub fn to_stdvec_crc32(value: &T, digest: crc::Digest<'_, u32>) -> Result> where T: Serialize + ?Sized, { flavors::crc::to_allocvec_u32(value, digest) } /// Conveniently serialize a `T` to a `heapless::Vec`, with the `Vec` containing /// data followed by a 32-bit CRC. The CRC bytes are included in the output `Vec`. /// /// ## Example /// /// ```rust /// use crc::{Crc, CRC_32_ISCSI}; /// use core::ops::Deref; /// /// // NOTE: postcard handles `&[u8]` and `&[u8; N]` differently. /// let data: &[u8] = &[0x01u8, 0x00, 0x20, 0x30]; /// let crc = Crc::::new(&CRC_32_ISCSI); /// let ser: Vec = postcard::to_allocvec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); /// /// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30]; /// let ser: Vec = postcard::to_allocvec_crc32(data, crc.digest()).unwrap(); /// assert_eq!(ser.deref(), &[0x01, 0x00, 0x20, 0x30, 0xCC, 0x4B, 0x4A, 0xDA]); /// ``` /// /// See the `ser_flavors::crc` module for the complete set of functions. #[cfg(all(feature = "use-crc", feature = "alloc"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "use-crc", feature = "alloc"))))] #[inline] pub fn to_allocvec_crc32(value: &T, digest: crc::Digest<'_, u32>) -> Result> where T: Serialize + ?Sized, { flavors::crc::to_allocvec_u32(value, digest) } /// `serialize_with_flavor()` has three generic parameters, `T, F, O`. /// /// * `T`: This is the type that is being serialized /// * `S`: This is the Storage that is used during serialization /// * `O`: This is the resulting storage type that is returned containing the serialized data /// /// For more information about how Flavors work, please see the /// [`flavors` module documentation](./flavors/index.html). /// /// ```rust /// use postcard::{ /// serialize_with_flavor, /// ser_flavors::{Cobs, Slice}, /// }; /// /// let mut buf = [0u8; 32]; /// /// let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; /// let buffer = &mut [0u8; 32]; /// let res = serialize_with_flavor::<[u8], Cobs, &mut [u8]>( /// data, /// Cobs::try_new(Slice::new(buffer)).unwrap(), /// ).unwrap(); /// /// assert_eq!(res, &[0x03, 0x04, 0x01, 0x03, 0x20, 0x30, 0x00]); /// ``` pub fn serialize_with_flavor(value: &T, storage: S) -> Result where T: Serialize + ?Sized, S: Flavor, { let mut serializer = Serializer { output: storage }; value.serialize(&mut serializer)?; serializer .output .finalize() .map_err(|_| Error::SerializeBufferFull) } /// Compute the size of the postcard serialization of `T`. pub fn serialized_size(value: &T) -> Result where T: Serialize + ?Sized, { serialize_with_flavor::(value, flavors::Size::default()) } #[cfg(feature = "heapless")] #[cfg(test)] mod test { use super::*; use crate::max_size::MaxSize; use crate::varint::{varint_max, varint_usize}; use core::fmt::Write; use core::ops::{Deref, DerefMut}; use heapless::{FnvIndexMap, String}; use serde::Deserialize; #[test] fn ser_u8() { let output: Vec = to_vec(&0x05u8).unwrap(); assert_eq!(&[5], output.deref()); assert!(output.len() == serialized_size(&0x05u8).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u16() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0xA5C7u16).unwrap(); assert_eq!(&[0xC7, 0xCB, 0x02], output.deref()); assert!(output.len() == serialized_size(&0xA5C7u16).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u32() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0xCDAB3412u32).unwrap(); assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref()); assert!(output.len() == serialized_size(&0xCDAB3412u32).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u64() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0x1234_5678_90AB_CDEFu64).unwrap(); assert_eq!( &[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12], output.deref() ); assert!(output.len() == serialized_size(&0x1234_5678_90AB_CDEFu64).unwrap()); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[test] fn ser_u128() { const SZ: usize = varint_max::(); let output: Vec = to_vec(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap(); assert_eq!( &[ 0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x92, 0xDE, 0xB7, 0xDE, 0x8A, 0x92, 0x9E, 0xAB, 0xB4, 0x24, ], output.deref() ); assert!( output.len() == serialized_size(&0x1234_5678_90AB_CDEF_1234_5678_90AB_CDEFu128).unwrap() ); assert!(output.len() <= Vec::::POSTCARD_MAX_SIZE); } #[derive(Serialize)] struct BasicU8S { st: u16, ei: u8, ote: u128, sf: u64, tt: u32, } impl MaxSize for BasicU8S { const POSTCARD_MAX_SIZE: usize = { u16::POSTCARD_MAX_SIZE + u8::POSTCARD_MAX_SIZE + u128::POSTCARD_MAX_SIZE + u64::POSTCARD_MAX_SIZE + u32::POSTCARD_MAX_SIZE }; } #[test] fn ser_struct_unsigned() { const SZ: usize = BasicU8S::POSTCARD_MAX_SIZE; let input = BasicU8S { st: 0xABCD, ei: 0xFE, ote: 0x1234_4321_ABCD_DCBA_1234_4321_ABCD_DCBA, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }; let output: Vec = to_vec(&input).unwrap(); assert_eq!( &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x92, 0xF4, 0xF2, 0xEE, 0xBC, 0xB5, 0xC8, 0xA1, 0xB4, 0x24, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A ], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); assert!(output.len() <= BasicU8S::POSTCARD_MAX_SIZE); } #[test] fn ser_byte_slice() { let input: &[u8] = &[1u8, 2, 3, 4, 5, 6, 7, 8]; let output: Vec = to_vec(input).unwrap(); assert_eq!( &[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); let mut input: Vec = Vec::new(); for i in 0..1024 { input.push((i & 0xFF) as u8).unwrap(); } let output: Vec = to_vec(input.deref()).unwrap(); assert_eq!(&[0x80, 0x08], &output.deref()[..2]); assert_eq!(output.len(), 1026); for (i, val) in output.deref()[2..].iter().enumerate() { assert_eq!((i & 0xFF) as u8, *val); } } #[test] fn ser_str() { let input: &str = "hello, postcard!"; let output: Vec = to_vec(input).unwrap(); assert_eq!(0x10, output.deref()[0]); assert_eq!(input.as_bytes(), &output.deref()[1..]); assert!(output.len() == serialized_size(&input).unwrap()); let mut input: String<1024> = String::new(); for _ in 0..256 { write!(&mut input, "abcd").unwrap(); } let output: Vec = to_vec(input.deref()).unwrap(); assert_eq!(&[0x80, 0x08], &output.deref()[..2]); assert!(String::<1024>::POSTCARD_MAX_SIZE <= output.len()); assert_eq!(output.len(), 1026); for ch in output.deref()[2..].chunks(4) { assert_eq!("abcd", core::str::from_utf8(ch).unwrap()); } } #[test] fn usize_varint_encode() { let mut buf = [0; varint_max::()]; let res = varint_usize(1, &mut buf); assert_eq!(&[1], res); let res = varint_usize(usize::MAX, &mut buf); // if varint_max::() == varint_max::() { assert_eq!(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F], res); } else if varint_max::() == varint_max::() { assert_eq!( &[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01], res ); } else { panic!("Update this test for 16/128 bit targets!"); } } #[allow(dead_code)] #[derive(Serialize)] enum BasicEnum { Bib, Bim, Bap, } #[derive(Serialize)] struct EnumStruct { eight: u8, sixt: u16, } #[derive(Serialize)] enum DataEnum { Bib(u16), Bim(u64), Bap(u8), Kim(EnumStruct), Chi { a: u8, b: u32 }, Sho(u16, u8), } #[test] fn enums() { let input = BasicEnum::Bim; let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x01], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Bim(u64::MAX); let output: Vec() }> = to_vec(&input).unwrap(); assert_eq!( &[0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Bib(u16::MAX); let output: Vec() }> = to_vec(&input).unwrap(); assert_eq!(&[0x00, 0xFF, 0xFF, 0x03], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Bap(u8::MAX); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x02, 0xFF], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Kim(EnumStruct { eight: 0xF0, sixt: 0xACAC, }); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x03, 0xF0, 0xAC, 0xD9, 0x02], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Chi { a: 0x0F, b: 0xC7C7C7C7, }; let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = DataEnum::Sho(0x6969, 0x07); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x05, 0xE9, 0xD2, 0x01, 0x07], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); } #[test] fn tuples() { let input = (1u8, 10u32, "Hello!"); let output: Vec = to_vec(&input).unwrap(); assert_eq!( &[1u8, 0x0A, 0x06, b'H', b'e', b'l', b'l', b'o', b'!'], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); } #[test] fn bytes() { let x: &[u8; 32] = &[0u8; 32]; let output: Vec = to_vec(x).unwrap(); assert_eq!(output.len(), 32); assert!(output.len() == serialized_size(&x).unwrap()); assert!(<[u8; 32] as MaxSize>::POSTCARD_MAX_SIZE <= output.len()); let x: &[u8] = &[0u8; 32]; let output: Vec = to_vec(x).unwrap(); assert_eq!(output.len(), 33); assert!(output.len() == serialized_size(&x).unwrap()); } #[derive(Serialize)] pub struct NewTypeStruct(u32); #[derive(Serialize)] pub struct TupleStruct((u8, u16)); #[test] fn structs() { let input = NewTypeStruct(5); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x05], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let input = TupleStruct((0xA0, 0x1234)); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0xA0, 0xB4, 0x24], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); } #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } #[test] fn ref_struct() { let message = "hElLo"; let bytes = [0x01, 0x10, 0x02, 0x20]; let input = RefStruct { bytes: &bytes, str_s: message, }; let output: Vec = to_vec(&input).unwrap(); assert_eq!( &[0x04, 0x01, 0x10, 0x02, 0x20, 0x05, b'h', b'E', b'l', b'L', b'o',], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); } #[test] fn unit() { let output: Vec = to_vec(&()).unwrap(); assert_eq!(output.len(), 0); assert!(output.len() == serialized_size(&()).unwrap()); } #[test] fn heapless_data() { let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let mut input: String<8> = String::new(); write!(&mut input, "helLO!").unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!(&[0x06, b'h', b'e', b'l', b'L', b'O', b'!'], output.deref()); assert!(output.len() == serialized_size(&input).unwrap()); let mut input: FnvIndexMap = FnvIndexMap::new(); input.insert(0x01, 0x05).unwrap(); input.insert(0x02, 0x06).unwrap(); input.insert(0x03, 0x07).unwrap(); input.insert(0x04, 0x08).unwrap(); let output: Vec = to_vec(&input).unwrap(); assert_eq!( &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], output.deref() ); assert!(output.len() == serialized_size(&input).unwrap()); } #[test] fn cobs_test() { let message = "hElLo"; let bytes = [0x01, 0x00, 0x02, 0x20]; let input = RefStruct { bytes: &bytes, str_s: message, }; let mut output: Vec = to_vec_cobs(&input).unwrap(); let sz = cobs::decode_in_place(output.deref_mut()).unwrap(); let x = crate::from_bytes::>(&output.deref_mut()[..sz]).unwrap(); assert_eq!(input, x); } } postcard-1.1.3/src/ser/serializer.rs000064400000000000000000000346611046102023000155560ustar 00000000000000use serde::{ser, Serialize}; use crate::error::{Error, Result}; use crate::ser::flavors::Flavor; use crate::varint::*; /// A `serde` compatible serializer, generic over "Flavors" of serializing plugins. /// /// It should rarely be necessary to directly use this type unless you are implementing your /// own [`SerFlavor`]. /// /// See the docs for [`SerFlavor`] for more information about "flavors" of serialization /// /// [`SerFlavor`]: crate::ser_flavors::Flavor pub struct Serializer where F: Flavor, { /// This is the Flavor(s) that will be used to modify or store any bytes generated /// by serialization pub output: F, } impl Serializer { /// Attempt to push a variably encoded [usize] into the output data stream #[inline] pub(crate) fn try_push_varint_usize(&mut self, data: usize) -> Result<()> { let mut buf = [0u8; varint_max::()]; let used_buf = varint_usize(data, &mut buf); self.output.try_extend(used_buf) } /// Attempt to push a variably encoded [u128] into the output data stream #[inline] pub(crate) fn try_push_varint_u128(&mut self, data: u128) -> Result<()> { let mut buf = [0u8; varint_max::()]; let used_buf = varint_u128(data, &mut buf); self.output.try_extend(used_buf) } /// Attempt to push a variably encoded [u64] into the output data stream #[inline] pub(crate) fn try_push_varint_u64(&mut self, data: u64) -> Result<()> { let mut buf = [0u8; varint_max::()]; let used_buf = varint_u64(data, &mut buf); self.output.try_extend(used_buf) } /// Attempt to push a variably encoded [u32] into the output data stream #[inline] pub(crate) fn try_push_varint_u32(&mut self, data: u32) -> Result<()> { let mut buf = [0u8; varint_max::()]; let used_buf = varint_u32(data, &mut buf); self.output.try_extend(used_buf) } /// Attempt to push a variably encoded [u16] into the output data stream #[inline] pub(crate) fn try_push_varint_u16(&mut self, data: u16) -> Result<()> { let mut buf = [0u8; varint_max::()]; let used_buf = varint_u16(data, &mut buf); self.output.try_extend(used_buf) } } impl ser::Serializer for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; // Associated types for keeping track of additional state while serializing // compound data structures like sequences and maps. In this case no // additional state is required beyond what is already stored in the // Serializer struct. type SerializeSeq = Self; type SerializeTuple = Self; type SerializeTupleStruct = Self; type SerializeTupleVariant = Self; type SerializeMap = Self; type SerializeStruct = Self; type SerializeStructVariant = Self; #[inline] fn is_human_readable(&self) -> bool { false } #[inline] fn serialize_bool(self, v: bool) -> Result<()> { self.serialize_u8(if v { 1 } else { 0 }) } #[inline] fn serialize_i8(self, v: i8) -> Result<()> { self.serialize_u8(v.to_le_bytes()[0]) } #[inline] fn serialize_i16(self, v: i16) -> Result<()> { let zzv = zig_zag_i16(v); self.try_push_varint_u16(zzv) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_i32(self, v: i32) -> Result<()> { let zzv = zig_zag_i32(v); self.try_push_varint_u32(zzv) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_i64(self, v: i64) -> Result<()> { let zzv = zig_zag_i64(v); self.try_push_varint_u64(zzv) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_i128(self, v: i128) -> Result<()> { let zzv = zig_zag_i128(v); self.try_push_varint_u128(zzv) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_u8(self, v: u8) -> Result<()> { self.output .try_push(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_u16(self, v: u16) -> Result<()> { self.try_push_varint_u16(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_u32(self, v: u32) -> Result<()> { self.try_push_varint_u32(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_u64(self, v: u64) -> Result<()> { self.try_push_varint_u64(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_u128(self, v: u128) -> Result<()> { self.try_push_varint_u128(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_f32(self, v: f32) -> Result<()> { let buf = v.to_bits().to_le_bytes(); self.output .try_extend(&buf) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_f64(self, v: f64) -> Result<()> { let buf = v.to_bits().to_le_bytes(); self.output .try_extend(&buf) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_char(self, v: char) -> Result<()> { let mut buf = [0u8; 4]; let strsl = v.encode_utf8(&mut buf); strsl.serialize(self) } #[inline] fn serialize_str(self, v: &str) -> Result<()> { self.try_push_varint_usize(v.len()) .map_err(|_| Error::SerializeBufferFull)?; self.output .try_extend(v.as_bytes()) .map_err(|_| Error::SerializeBufferFull)?; Ok(()) } #[inline] fn serialize_bytes(self, v: &[u8]) -> Result<()> { self.try_push_varint_usize(v.len()) .map_err(|_| Error::SerializeBufferFull)?; self.output .try_extend(v) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_none(self) -> Result<()> { self.serialize_u8(0) } #[inline] fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { self.serialize_u8(1)?; value.serialize(self) } #[inline] fn serialize_unit(self) -> Result<()> { Ok(()) } #[inline] fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { Ok(()) } #[inline] fn serialize_unit_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, ) -> Result<()> { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull) } #[inline] fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(self) } #[inline] fn serialize_newtype_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull)?; value.serialize(self) } #[inline] fn serialize_seq(self, len: Option) -> Result { self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn serialize_tuple(self, _len: usize) -> Result { Ok(self) } #[inline] fn serialize_tuple_struct( self, _name: &'static str, _len: usize, ) -> Result { Ok(self) } #[inline] fn serialize_tuple_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn serialize_map(self, len: Option) -> Result { self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { Ok(self) } #[inline] fn serialize_struct_variant( self, _name: &'static str, variant_index: u32, _variant: &'static str, _len: usize, ) -> Result { self.try_push_varint_u32(variant_index) .map_err(|_| Error::SerializeBufferFull)?; Ok(self) } #[inline] fn collect_str(self, value: &T) -> Result where T: core::fmt::Display + ?Sized, { use core::fmt::Write; // Unfortunately, we need to know the size of the serialized data before // we can place it into the output. In order to do this, we run the formatting // of the output data TWICE, the first time to determine the length, the // second time to actually format the data // // There are potentially other ways to do this, such as: // // * Reserving a fixed max size, such as 5 bytes, for the length field, and // leaving non-canonical trailing zeroes at the end. This would work up // to some reasonable length, but might have some portability vs max size // tradeoffs, e.g. 64KiB if we pick 3 bytes, or 4GiB if we pick 5 bytes // * Expose some kind of "memmove" capability to flavors, to allow us to // format into the buffer, then "scoot over" that many times. // // Despite the current approaches downside in speed, it is likely flexible // enough for the rare-ish case where formatting a Debug impl is necessary. // This is better than the previous panicking behavior, and can be improved // in the future. struct CountWriter { ct: usize, } impl Write for CountWriter { fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { self.ct += s.len(); Ok(()) } } let mut ctr = CountWriter { ct: 0 }; // This is the first pass through, where we just count the length of the // data that we are given write!(&mut ctr, "{value}").map_err(|_| Error::CollectStrError)?; let len = ctr.ct; self.try_push_varint_usize(len) .map_err(|_| Error::SerializeBufferFull)?; struct FmtWriter<'a, IF> where IF: Flavor, { output: &'a mut IF, } impl Write for FmtWriter<'_, IF> where IF: Flavor, { fn write_str(&mut self, s: &str) -> core::result::Result<(), core::fmt::Error> { self.output .try_extend(s.as_bytes()) .map_err(|_| core::fmt::Error) } } // This second pass actually inserts the data. let mut fw = FmtWriter { output: &mut self.output, }; write!(&mut fw, "{value}").map_err(|_| Error::CollectStrError)?; Ok(()) } } impl ser::SerializeSeq for &mut Serializer where F: Flavor, { // Must match the `Ok` type of the serializer. type Ok = (); // Must match the `Error` type of the serializer. type Error = Error; // Serialize a single element of the sequence. #[inline] fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } // Close the sequence. #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeTuple for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_element(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeTupleStruct for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeTupleVariant for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeMap for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_key(&mut self, key: &T) -> Result<()> where T: ?Sized + Serialize, { key.serialize(&mut **self) } #[inline] fn serialize_value(&mut self, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeStruct for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } impl ser::SerializeStructVariant for &mut Serializer where F: Flavor, { type Ok = (); type Error = Error; #[inline] fn serialize_field(&mut self, _key: &'static str, value: &T) -> Result<()> where T: ?Sized + Serialize, { value.serialize(&mut **self) } #[inline] fn end(self) -> Result<()> { Ok(()) } } fn zig_zag_i16(n: i16) -> u16 { ((n << 1) ^ (n >> 15)) as u16 } fn zig_zag_i32(n: i32) -> u32 { ((n << 1) ^ (n >> 31)) as u32 } fn zig_zag_i64(n: i64) -> u64 { ((n << 1) ^ (n >> 63)) as u64 } fn zig_zag_i128(n: i128) -> u128 { ((n << 1) ^ (n >> 127)) as u128 } postcard-1.1.3/src/varint.rs000064400000000000000000000052441046102023000141120ustar 00000000000000/// Returns the maximum number of bytes required to encode T. pub const fn varint_max() -> usize { const BITS_PER_BYTE: usize = 8; const BITS_PER_VARINT_BYTE: usize = 7; // How many data bits do we need for this type? let bits = core::mem::size_of::() * BITS_PER_BYTE; // We add (BITS_PER_VARINT_BYTE - 1), to ensure any integer divisions // with a remainder will always add exactly one full byte, but // an evenly divided number of bits will be the same let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1); // Apply division, using normal "round down" integer division roundup_bits / BITS_PER_VARINT_BYTE } /// Returns the maximum value stored in the last encoded byte. pub const fn max_of_last_byte() -> u8 { let max_bits = core::mem::size_of::() * 8; let extra_bits = max_bits % 7; (1 << extra_bits) - 1 } #[inline] pub fn varint_usize(n: usize, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { out[i] = value.to_le_bytes()[0]; if value < 128 { return &mut out[..=i]; } out[i] |= 0x80; value >>= 7; } debug_assert_eq!(value, 0); &mut out[..] } #[inline] pub fn varint_u16(n: u16, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { out[i] = value.to_le_bytes()[0]; if value < 128 { return &mut out[..=i]; } out[i] |= 0x80; value >>= 7; } debug_assert_eq!(value, 0); &mut out[..] } #[inline] pub fn varint_u32(n: u32, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { out[i] = value.to_le_bytes()[0]; if value < 128 { return &mut out[..=i]; } out[i] |= 0x80; value >>= 7; } debug_assert_eq!(value, 0); &mut out[..] } #[inline] pub fn varint_u64(n: u64, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { out[i] = value.to_le_bytes()[0]; if value < 128 { return &mut out[..=i]; } out[i] |= 0x80; value >>= 7; } debug_assert_eq!(value, 0); &mut out[..] } #[inline] pub fn varint_u128(n: u128, out: &mut [u8; varint_max::()]) -> &mut [u8] { let mut value = n; for i in 0..varint_max::() { out[i] = value.to_le_bytes()[0]; if value < 128 { return &mut out[..=i]; } out[i] |= 0x80; value >>= 7; } debug_assert_eq!(value, 0); &mut out[..] } postcard-1.1.3/tests/accumulator.rs000064400000000000000000000032261046102023000154770ustar 00000000000000use postcard::accumulator::{CobsAccumulator, FeedResult}; use serde::{Deserialize, Serialize}; use std::io::Read; // Read a "huge" serialized struct in 32 byte chunks into a 256 byte buffer and deserialize it. #[test] fn reader() { let mut raw_buf = [0u8; 32]; let mut input_buf = [0u8; 256]; let mut cobs_buf: CobsAccumulator<256> = CobsAccumulator::new(); #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] struct Huge { a: u32, b: [u32; 32], d: u64, } let expected = Huge { a: 0xabcdef00, b: [0x01234567; 32], d: u64::MAX, }; let input = postcard::to_slice_cobs(&expected, &mut input_buf).unwrap(); // TODO(https://github.com/rust-lang/rust-clippy/issues/12751): Remove once fixed. #[allow(clippy::redundant_slicing)] let mut input = &input[..]; // Magic number from serializing struct and printing length assert_eq!(input.len(), 145); let mut output = None; while let Ok(ct) = input.read(&mut raw_buf) { // Finished reading input if ct == 0 { break; } let buf = &raw_buf[..ct]; let mut window = buf; 'cobs: while !window.is_empty() { window = match cobs_buf.feed::(window) { FeedResult::Consumed => break 'cobs, FeedResult::OverFull(new_wind) => new_wind, FeedResult::DeserError(new_wind) => new_wind, FeedResult::Success { data, remaining } => { output = Some(data); remaining } }; } } assert_eq!(output.unwrap(), expected); } postcard-1.1.3/tests/crc.rs000064400000000000000000000050031046102023000137220ustar 00000000000000#[test] #[cfg(feature = "use-crc")] fn test_crc() { use crc::{Crc, CRC_32_ISCSI}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_32_ISCSI); let digest = crc.digest(); let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 0x8E, 0xC8, 0x1A, 0x37]); let digest = crc.digest(); let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest).unwrap(); let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; let remaining_bytes = []; assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); } #[test] #[cfg(feature = "use-crc")] fn test_crc_8() { use crc::{Crc, CRC_8_SMBUS}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_8_SMBUS); let digest = crc.digest(); let res = postcard::ser_flavors::crc::to_slice_u8(data, buffer, digest).unwrap(); assert_eq!(res, &[0x04, 0x01, 0x00, 0x20, 0x30, 167]); let digest = crc.digest(); let res = postcard::de_flavors::crc::take_from_bytes_u8::<[u8; 5]>(res, digest).unwrap(); let expected_bytes = [0x04, 0x01, 0x00, 0x20, 0x30]; let remaining_bytes = []; assert_eq!(res, (expected_bytes, remaining_bytes.as_slice())); } #[test] #[cfg(feature = "use-crc")] fn test_crc_error() { use crc::{Crc, CRC_32_ISCSI}; let data: &[u8] = &[0x01, 0x00, 0x20, 0x30]; let buffer = &mut [0u8; 32]; let crc = Crc::::new(&CRC_32_ISCSI); let digest = crc.digest(); let res = postcard::to_slice_crc32(data, buffer, digest).unwrap(); // intentionally corrupt the crc let last = res.len() - 1; res[last] = 0; let digest = crc.digest(); let res = postcard::take_from_bytes_crc32::<[u8; 5]>(res, digest); assert_eq!(res, Err(postcard::Error::DeserializeBadCrc)); } #[test] #[cfg(feature = "use-crc")] fn test_crc_in_method() { use crc::{Crc, CRC_32_ISCSI}; use postcard::{to_slice_crc32, Result}; use serde::Serialize; #[derive(Debug, Serialize)] pub struct Thing { value: u32, } impl Thing { pub fn to_bytes<'a>(&self, buf: &'a mut [u8]) -> Result<&'a mut [u8]> { let crc = Crc::::new(&CRC_32_ISCSI); to_slice_crc32(self, buf, crc.digest()) } } let buffer = &mut [0u8; 5]; let thing = Thing { value: 42 }; let slice = thing.to_bytes(buffer).unwrap(); assert_eq!(slice, &[0x2A, 0xB7, 0xF5, 0x22, 0x19]); } postcard-1.1.3/tests/loopback.rs000064400000000000000000000151741046102023000147570ustar 00000000000000use core::fmt::Debug; use core::fmt::Write; use core::ops::Deref; #[cfg(feature = "heapless")] use heapless::{FnvIndexMap, String, Vec}; #[cfg(feature = "heapless")] use postcard::to_vec; use postcard::from_bytes; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] struct BasicU8S { st: u16, ei: u8, sf: u64, tt: u32, } #[allow(dead_code)] #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] enum BasicEnum { Bib, Bim, Bap, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct EnumStruct { eight: u8, sixt: u16, } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] enum DataEnum { Bib(u16), Bim(u64), Bap(u8), Kim(EnumStruct), Chi { a: u8, b: u32 }, Sho(u16, u8), } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct NewTypeStruct(u32); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct TupleStruct((u8, u16)); #[derive(Serialize, Deserialize, Debug, Eq, PartialEq)] struct RefStruct<'a> { bytes: &'a [u8], str_s: &'a str, } #[cfg(feature = "heapless")] #[test] fn loopback() { // Basic types test_one((), &[]); test_one(false, &[0x00]); test_one(true, &[0x01]); test_one(5u8, &[0x05]); test_one(0xA5C7u16, &[0xC7, 0xCB, 0x02]); test_one(0xCDAB3412u32, &[0x92, 0xE8, 0xAC, 0xED, 0x0C]); test_one( 0x1234_5678_90AB_CDEFu64, &[0xEF, 0x9B, 0xAF, 0x85, 0x89, 0xCF, 0x95, 0x9A, 0x12], ); // https://github.com/jamesmunns/postcard/pull/83 test_one(32767i16, &[0xFE, 0xFF, 0x03]); test_one(-32768i16, &[0xFF, 0xFF, 0x03]); // chars test_one('z', &[0x01, 0x7a]); test_one('¢', &[0x02, 0xc2, 0xa2]); test_one('𐍈', &[0x04, 0xF0, 0x90, 0x8D, 0x88]); test_one('🥺', &[0x04, 0xF0, 0x9F, 0xA5, 0xBA]); // Structs test_one( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); // Enums! test_one(BasicEnum::Bim, &[0x01]); test_one( DataEnum::Bim(u64::MAX), &[ 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, ], ); test_one(DataEnum::Bib(u16::MAX), &[0x00, 0xFF, 0xFF, 0x03]); test_one(DataEnum::Bap(u8::MAX), &[0x02, 0xFF]); test_one( DataEnum::Kim(EnumStruct { eight: 0xF0, sixt: 0xACAC, }), &[0x03, 0xF0, 0xAC, 0xD9, 0x02], ); test_one( DataEnum::Chi { a: 0x0F, b: 0xC7C7C7C7, }, &[0x04, 0x0F, 0xC7, 0x8F, 0x9F, 0xBE, 0x0C], ); test_one(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); // Tuples! test_one((0x12u8, 0xC7A5u16), &[0x12, 0xA5, 0x8F, 0x03]); // Structs! test_one(NewTypeStruct(5), &[0x05]); test_one(TupleStruct((0xA0, 0x1234)), &[0xA0, 0xB4, 0x24]); let mut input: Vec = Vec::new(); input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap(); test_one(input, &[0x04, 0x01, 0x02, 0x03, 0x04]); let mut input: String<8> = String::new(); write!(&mut input, "helLO!").unwrap(); test_one(input, &[0x06, b'h', b'e', b'l', b'L', b'O', b'!']); let mut input: FnvIndexMap = FnvIndexMap::new(); input.insert(0x01, 0x05).unwrap(); input.insert(0x02, 0x06).unwrap(); input.insert(0x03, 0x07).unwrap(); input.insert(0x04, 0x08).unwrap(); test_one( input, &[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08], ); // `CString` (uses `serialize_bytes`/`deserialize_byte_buf`) #[cfg(feature = "use-std")] test_one( std::ffi::CString::new("heLlo").unwrap(), &[0x05, b'h', b'e', b'L', b'l', b'o'], ); } #[cfg(feature = "heapless")] #[track_caller] fn test_one(data: T, ser_rep: &[u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: Vec = to_vec(&data).unwrap(); assert_eq!(serialized.len(), ser_rep.len()); let mut x: ::std::vec::Vec = vec![]; x.extend(serialized.deref().iter().cloned()); // let bysl: &'de [u8] = serialized.deref(); assert_eq!(x, ser_rep); { // let deserialized: T = from_bytes(serialized.deref()).unwrap(); let deserialized: T = from_bytes(&x).unwrap(); assert_eq!(data, deserialized); } } #[cfg(feature = "use-std")] #[test] fn std_io_loopback() { use postcard::from_io; use postcard::to_io; fn test_io(data: T, ser_rep: &[u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: ::std::vec::Vec = vec![]; let ser = to_io(&data, serialized).unwrap(); assert_eq!(ser.len(), ser_rep.len()); assert_eq!(ser, ser_rep); { let mut buff = [0; 2048]; let x = ser.clone(); let deserialized: T = from_io((x.as_slice(), &mut buff)).unwrap().0; assert_eq!(data, deserialized); } } test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); test_io( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); } #[cfg(all( any(feature = "embedded-io-04", feature = "embedded-io-06"), feature = "alloc" ))] #[test] fn std_eio_loopback() { use postcard::from_eio; use postcard::to_eio; fn test_io(data: T, ser_rep: &[u8]) where T: Serialize + DeserializeOwned + Eq + PartialEq + Debug, { let serialized: ::std::vec::Vec = vec![]; let ser = to_eio(&data, serialized).unwrap(); assert_eq!(ser.len(), ser_rep.len()); assert_eq!(ser, ser_rep); { let mut buff = [0; 2048]; let x = ser.clone(); let deserialized: T = from_eio((x.as_slice(), &mut buff)).unwrap().0; assert_eq!(data, deserialized); } } test_io(DataEnum::Sho(0x6969, 0x07), &[0x05, 0xE9, 0xD2, 0x01, 0x07]); test_io( BasicU8S { st: 0xABCD, ei: 0xFE, sf: 0x1234_4321_ABCD_DCBA, tt: 0xACAC_ACAC, }, &[ 0xCD, 0xD7, 0x02, 0xFE, 0xBA, 0xB9, 0xB7, 0xDE, 0x9A, 0xE4, 0x90, 0x9A, 0x12, 0xAC, 0xD9, 0xB2, 0xE5, 0x0A, ], ); } postcard-1.1.3/tests/max_size.rs000064400000000000000000000045311046102023000147770ustar 00000000000000#![allow(unused_imports)] #[cfg(feature = "experimental-derive")] mod tests { use postcard::experimental::max_size::MaxSize; use postcard::to_slice; use serde::Serialize; #[test] fn test_struct_max_size() { #[derive(MaxSize)] struct Foo { _a: u16, _b: Option, } assert_eq!(Foo::POSTCARD_MAX_SIZE, 5); } #[test] fn test_enum_max_size() { #[allow(dead_code)] #[derive(MaxSize, Serialize)] enum Bar { A(u16), B(u8), } assert_eq!(Bar::POSTCARD_MAX_SIZE, 4); let mut buf = [0u8; 128]; let used = to_slice(&Bar::A(0xFFFF), &mut buf).unwrap(); assert!( used.len() <= Bar::POSTCARD_MAX_SIZE, "FAIL {} > {}", used.len(), Bar::POSTCARD_MAX_SIZE ); #[derive(MaxSize)] enum Baz {} assert_eq!(Baz::POSTCARD_MAX_SIZE, 0); } #[test] fn test_ref() { #[allow(dead_code)] #[derive(MaxSize)] struct Foo { a: &'static u32, } } #[cfg(feature = "heapless")] #[test] fn test_vec_edge_cases() { #[track_caller] fn test_equals(buf: &mut [u8]) { let mut v = heapless::Vec::::new(); for _ in 0..N { v.push(0).unwrap(); } let serialized = postcard::to_slice(&v, buf).unwrap(); assert_eq!(heapless::Vec::::POSTCARD_MAX_SIZE, serialized.len()); } let mut buf = [0; 16400]; test_equals::<1>(&mut buf); test_equals::<2>(&mut buf); test_equals::<127>(&mut buf); test_equals::<128>(&mut buf); test_equals::<129>(&mut buf); test_equals::<16383>(&mut buf); test_equals::<16384>(&mut buf); test_equals::<16385>(&mut buf); } // #[cfg(feature = "experimental-derive")] // #[test] // fn test_union_max_size() { // #[derive(postcard::MaxSize)] // union Foo { // a: u16, // b: Option, // } // } // #[cfg(feature = "experimental-derive")] // #[test] // fn test_not_implemented() { // #[derive(postcard::MaxSize)] // struct Foo { // a: &'static str, // } // } }