feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"fa36d6389b358d23d8f055be96aa8f5711754c3b7f65777320f6bf85573a8235","Cargo.lock":"f6273fea06ff1a8da2b633cdca234846abbbc4f08ad689f990d647e7bfe7a6e6","Cargo.toml":"8f1de19506db838be4fc72b5c14c7a414073e9ef8222b7b7e18fa2e005c1e05e","Cargo.toml.orig":"ec846871cb12f3e868a9950e7ef1d7ff7710dba1d5073beb1d4293b179adaaff","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"177540cad091a40e8071db310bc3b6115c4e329a92a234609b60c154b008a888","README.md":"f74dbb06448938d8926d28997d603ef6fc6c1265aebbd831cdc26d87a44436f4","src/accumulator.rs":"0c4e4d8ec302878793c0193c2141f5c934341c7a8d9ee046fc8f2d27ad5cd6a2","src/de/deserializer.rs":"20f5ff69404628e4d765f58ceb91b6ac8879ce998aef6dfe49a2fdb2c5f20ac2","src/de/flavors.rs":"558b4daadaf3c6892e914a5ef3533ba3b1a696bd093a1ed471154cc11f9b4b91","src/de/mod.rs":"0672b81c7bb1e0e1cc9f812661631e2b43dcd2ddba8dbb89d8d2cb72375f35a7","src/eio.rs":"0b99abc945654b0e3ecd1d16fa2b908e0c61a74b615d3e16fc22b3028e15970e","src/error.rs":"07f66178e86c46ae53f00df5ef6e778ab3f2d075e08084086e67cca132539379","src/fixint.rs":"41a8734a6ff99bb129d944be9228fbd9dfbcc7a514acf6305098299f5726f7ee","src/lib.rs":"026c0feb3d0b44b9208024b86d47d185087611b16b500627459ace28b86be1f9","src/max_size.rs":"d08926697cff90893fbd0c02dd43ba6be611335d6b540c1000e03d30d0ed20ec","src/ser/flavors.rs":"9be0b2ae5f9012b1183ad4102df8369a29864b1c615d62a3d6666a6b2884432d","src/ser/mod.rs":"2ef1490316948536890dfbb6d19f5051b464a0c4e28efb50a72dbb8306653cdc","src/ser/serializer.rs":"e8ac1136c617d622e8897a0469295605239d1000f772cafa0d64f18e1a52e5bc","src/varint.rs":"d40e63d0e13752c562f5116da2cbc786f608815a97076f94bf56b701b39120d4","tests/accumulator.rs":"1424dd6af14a2d54b7b234ce2f41b7e618fd92d42e4ecb63636fd3e85d28faf4","tests/crc.rs":"d255be56c9b877bc18076720b93d9d287dd1024ba642cd36bfa26ba6232cce38","tests/loopback.rs":"a6bf82b01f144d138771b594d08d6b3aea6447fc35b4e5c67c68ae12742009f1","tests/max_size.rs":"074487dc54725a0b54066b61b663bad75d9e18e0921ec1743ce434563adeb9fd"},"package":"6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "718aa6a6850456017c19eeff67303c633f875736"
|
||||
},
|
||||
"path_in_vcs": "source/postcard"
|
||||
}
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
# 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"
|
||||
Vendored
+136
@@ -0,0 +1,136 @@
|
||||
# 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 <james@onevariable.com>"]
|
||||
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
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
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.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (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.
|
||||
Vendored
+146
@@ -0,0 +1,146 @@
|
||||
# Postcard
|
||||
|
||||
[](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 <https://postcard.jamesmunns.com>.
|
||||
|
||||
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<u8>`:
|
||||
|
||||
```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<u8, 11> = 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<u8>`:
|
||||
|
||||
```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<u8> = 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
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>)
|
||||
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
||||
|
||||
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.
|
||||
Vendored
+376
@@ -0,0 +1,376 @@
|
||||
//! 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::<MyData>(&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<const N: usize> {
|
||||
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<const N: usize> Default for CobsAccumulator<N> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> CobsAccumulator<N> {
|
||||
/// 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::<T>(&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::<Demo>(&data[..]),
|
||||
FeedResult::OverFull(_)
|
||||
));
|
||||
|
||||
// Accumulator is juuuuust right
|
||||
let mut acc: CobsAccumulator<12> = CobsAccumulator::new();
|
||||
assert!(matches!(
|
||||
acc.feed::<Demo>(&data[..]),
|
||||
FeedResult::Success { .. }
|
||||
));
|
||||
}
|
||||
}
|
||||
+602
@@ -0,0 +1,602 @@
|
||||
use 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<F::Remainder> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
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<usize> {
|
||||
self.try_take_varint_u64().map(|u| u as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_take_varint_u16(&mut self) -> Result<u16> {
|
||||
let mut out = 0;
|
||||
for i in 0..varint_max::<u16>() {
|
||||
let val = self.flavor.pop()?;
|
||||
let carry = (val & 0x7F) as u16;
|
||||
out |= carry << (7 * i);
|
||||
|
||||
if (val & 0x80) == 0 {
|
||||
if i == varint_max::<u16>() - 1 && val > max_of_last_byte::<u16>() {
|
||||
return Err(Error::DeserializeBadVarint);
|
||||
} else {
|
||||
return Ok(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::DeserializeBadVarint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_take_varint_u32(&mut self) -> Result<u32> {
|
||||
let mut out = 0;
|
||||
for i in 0..varint_max::<u32>() {
|
||||
let val = self.flavor.pop()?;
|
||||
let carry = (val & 0x7F) as u32;
|
||||
out |= carry << (7 * i);
|
||||
|
||||
if (val & 0x80) == 0 {
|
||||
if i == varint_max::<u32>() - 1 && val > max_of_last_byte::<u32>() {
|
||||
return Err(Error::DeserializeBadVarint);
|
||||
} else {
|
||||
return Ok(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::DeserializeBadVarint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_take_varint_u64(&mut self) -> Result<u64> {
|
||||
let mut out = 0;
|
||||
for i in 0..varint_max::<u64>() {
|
||||
let val = self.flavor.pop()?;
|
||||
let carry = (val & 0x7F) as u64;
|
||||
out |= carry << (7 * i);
|
||||
|
||||
if (val & 0x80) == 0 {
|
||||
if i == varint_max::<u64>() - 1 && val > max_of_last_byte::<u64>() {
|
||||
return Err(Error::DeserializeBadVarint);
|
||||
} else {
|
||||
return Ok(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(Error::DeserializeBadVarint)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn try_take_varint_u128(&mut self) -> Result<u128> {
|
||||
let mut out = 0;
|
||||
for i in 0..varint_max::<u128>() {
|
||||
let val = self.flavor.pop()?;
|
||||
let carry = (val & 0x7F) as u128;
|
||||
out |= carry << (7 * i);
|
||||
|
||||
if (val & 0x80) == 0 {
|
||||
if i == varint_max::<u128>() - 1 && val > max_of_last_byte::<u128>() {
|
||||
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<V: DeserializeSeed<'b>>(&mut self, seed: V) -> Result<Option<V::Value>> {
|
||||
if self.len > 0 {
|
||||
self.len -= 1;
|
||||
Ok(Some(DeserializeSeed::deserialize(
|
||||
seed,
|
||||
&mut *self.deserializer,
|
||||
)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
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<K: DeserializeSeed<'b>>(&mut self, seed: K) -> Result<Option<K::Value>> {
|
||||
if self.len > 0 {
|
||||
self.len -= 1;
|
||||
Ok(Some(DeserializeSeed::deserialize(
|
||||
seed,
|
||||
&mut *self.deserializer,
|
||||
)?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn next_value_seed<V: DeserializeSeed<'b>>(&mut self, seed: V) -> Result<V::Value> {
|
||||
DeserializeSeed::deserialize(seed, &mut *self.deserializer)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
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<V>(self, _visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
// We wont ever support this.
|
||||
Err(Error::WontImplement)
|
||||
}
|
||||
|
||||
// Take a boolean encoded as a u8
|
||||
#[inline]
|
||||
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_i8(self.flavor.pop()? as i8)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u16()?;
|
||||
visitor.visit_i16(de_zig_zag_i16(v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u32()?;
|
||||
visitor.visit_i32(de_zig_zag_i32(v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u64()?;
|
||||
visitor.visit_i64(de_zig_zag_i64(v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u128()?;
|
||||
visitor.visit_i128(de_zig_zag_i128(v))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_u8(self.flavor.pop()?)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u16()?;
|
||||
visitor.visit_u16(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u32()?;
|
||||
visitor.visit_u32(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u64()?;
|
||||
visitor.visit_u64(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_u128<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let v = self.try_take_varint_u128()?;
|
||||
visitor.visit_u128(v)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, visitor: V) -> Result<V::Value>
|
||||
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<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_unit(visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let len = self.try_take_varint_usize()?;
|
||||
|
||||
visitor.visit_seq(SeqAccess {
|
||||
deserializer: self,
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_seq(SeqAccess {
|
||||
deserializer: self,
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_tuple_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_tuple(len, visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
let len = self.try_take_varint_usize()?;
|
||||
|
||||
visitor.visit_map(MapAccess {
|
||||
deserializer: self,
|
||||
len,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_struct<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_tuple(fields.len(), visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_enum<V>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variants: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_enum(self)
|
||||
}
|
||||
|
||||
// As a binary format, Postcard does not encode identifiers
|
||||
#[inline]
|
||||
fn deserialize_identifier<V>(self, _visitor: V) -> Result<V::Value>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
// Will not support
|
||||
Err(Error::WontImplement)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
|
||||
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<V: DeserializeSeed<'de>>(self, seed: V) -> Result<V::Value> {
|
||||
DeserializeSeed::deserialize(seed, self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn tuple_variant<V: Visitor<'de>>(self, len: usize, visitor: V) -> Result<V::Value> {
|
||||
serde::de::Deserializer::deserialize_tuple(self, len, visitor)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn struct_variant<V: Visitor<'de>>(
|
||||
self,
|
||||
fields: &'static [&'static str],
|
||||
visitor: V,
|
||||
) -> Result<V::Value> {
|
||||
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<V: DeserializeSeed<'de>>(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))
|
||||
}
|
||||
Vendored
+642
@@ -0,0 +1,642 @@
|
||||
//! # 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<u8>;
|
||||
|
||||
/// 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<usize> {
|
||||
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<Self::Remainder>;
|
||||
}
|
||||
|
||||
/// 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<u8> {
|
||||
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<usize> {
|
||||
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<u8> {
|
||||
let mut val = [0; 1];
|
||||
self.reader
|
||||
.read_exact(&mut val)
|
||||
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
|
||||
Ok(val[0])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
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<u8> {
|
||||
let mut val = [0; 1];
|
||||
self.reader
|
||||
.read_exact(&mut val)
|
||||
.map_err(|_| Error::DeserializeUnexpectedEnd)?;
|
||||
Ok(val[0])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
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: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
|
||||
#[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<u8> {
|
||||
match self.flav.pop() {
|
||||
Ok(byte) => {
|
||||
self.digest.update(&[byte]);
|
||||
Ok(byte)
|
||||
}
|
||||
e @ Err(_) => e,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> Option<usize> {
|
||||
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<Self::Remainder> {
|
||||
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<T>
|
||||
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);
|
||||
}
|
||||
Vendored
+612
@@ -0,0 +1,612 @@
|
||||
use 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<T>
|
||||
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<T>
|
||||
where
|
||||
T: Deserialize<'a>,
|
||||
{
|
||||
let sz = decode_in_place(s).map_err(|_| Error::DeserializeBadEncoding)?;
|
||||
from_bytes::<T>(&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::<T>(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<T>
|
||||
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<u8, 1> = 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<u8, { varint_max::<u16>() }> = 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<u8, { varint_max::<u32>() }> = 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<u8, { varint_max::<u64>() }> = 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<u8, { varint_max::<u128>() }> =
|
||||
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::<u16>()
|
||||
+ 1
|
||||
+ varint_max::<u128>()
|
||||
+ varint_max::<u64>()
|
||||
+ varint_max::<u32>();
|
||||
|
||||
let output: Vec<u8, SZ> = 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<u8, 9> = to_vec(input).unwrap();
|
||||
assert_eq!(
|
||||
&[0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
|
||||
output.deref()
|
||||
);
|
||||
|
||||
let out: Vec<u8, 128> = from_bytes(output.deref()).unwrap();
|
||||
assert_eq!(input, out.deref());
|
||||
|
||||
let mut input: Vec<u8, 1024> = Vec::new();
|
||||
for i in 0..1024 {
|
||||
input.push((i & 0xFF) as u8).unwrap();
|
||||
}
|
||||
let output: Vec<u8, 2048> = 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<u8, 1024> = from_bytes(output.deref()).unwrap();
|
||||
assert_eq!(input.deref(), de.deref());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn de_str() {
|
||||
let input: &str = "hello, postcard!";
|
||||
let output: Vec<u8, 17> = 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<u8, 2048> = 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<u8, 1> = 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<u8, { 1 + varint_max::<u64>() }> =
|
||||
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<u8, { 1 + varint_max::<u16>() }> =
|
||||
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<u8, 2> = 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<u8, 8> = 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<u8, 8> = 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<u8, 8> = 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<u8, 128> = 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<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
|
||||
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<D>(deserializer: D) -> core::result::Result<Self, D::Error>
|
||||
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<u8, 128> = 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<u8, 128> = 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<u8, 128> = 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<u8, 5> = 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<u8, 4> = 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<u8, 3> = 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<u8, 3> = 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<u8, 11> = 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<u8, 1> = to_vec(&()).unwrap();
|
||||
assert_eq!(output.len(), 0);
|
||||
let _: () = from_bytes(output.deref()).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heapless_data() {
|
||||
let mut input: Vec<u8, 4> = Vec::new();
|
||||
input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap();
|
||||
let output: Vec<u8, 5> = to_vec(&input).unwrap();
|
||||
assert_eq!(&[0x04, 0x01, 0x02, 0x03, 0x04], output.deref());
|
||||
let out: Vec<u8, 4> = from_bytes(output.deref()).unwrap();
|
||||
assert_eq!(out, input);
|
||||
|
||||
let mut input: String<8> = String::new();
|
||||
write!(&mut input, "helLO!").unwrap();
|
||||
let output: Vec<u8, 7> = 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<u8, u8, 4> = 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<u8, 100> = to_vec(&input).unwrap();
|
||||
assert_eq!(
|
||||
&[0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07, 0x04, 0x08],
|
||||
output.deref()
|
||||
);
|
||||
let out: FnvIndexMap<u8, u8, 4> = 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<u8, 11> = to_vec(&input).unwrap();
|
||||
|
||||
let mut encode_buf = [0u8; 32];
|
||||
let sz = cobs::encode(output.deref(), &mut encode_buf);
|
||||
let out = from_bytes_cobs::<RefStruct<'_>>(&mut encode_buf[..sz]).unwrap();
|
||||
|
||||
assert_eq!(input, out);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_from_includes_terminator() {
|
||||
// With the null terminator
|
||||
let mut output: Vec<u8, 32> = 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<u8, 32> = 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::<Vec<u8>>(&[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::<Vec<u8>>(&[(1 << 7) | 8, 255, 255, 255, 0, 0, 0, 0, 0]),
|
||||
Err(Error::DeserializeUnexpectedEnd)
|
||||
);
|
||||
}
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
// 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};
|
||||
Vendored
+96
@@ -0,0 +1,96 @@
|
||||
use 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<T> = ::core::result::Result<T, Error>;
|
||||
|
||||
impl serde::ser::Error for Error {
|
||||
fn custom<T>(_msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
Error::SerdeSerCustom
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::de::Error for Error {
|
||||
fn custom<T>(_msg: T) -> Self
|
||||
where
|
||||
T: Display,
|
||||
{
|
||||
Error::SerdeDeCustom
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::StdError for Error {}
|
||||
Vendored
+191
@@ -0,0 +1,191 @@
|
||||
//! # 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<S, T>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: Copy,
|
||||
LE<T>: 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<T, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
LE<T>: Deserialize<'de>,
|
||||
{
|
||||
LE::<T>::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<S, T>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: Copy,
|
||||
BE<T>: 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<T, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
BE<T>: Deserialize<'de>,
|
||||
{
|
||||
BE::<T>::deserialize(deserializer).map(|x| x.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct LE<T>(T);
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct BE<T>(T);
|
||||
|
||||
macro_rules! impl_fixint {
|
||||
($( $int:ty ),*) => {
|
||||
$(
|
||||
impl Serialize for LE<$int> {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.to_le_bytes().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for LE<$int> {
|
||||
#[inline]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
<_ as Deserialize>::deserialize(deserializer)
|
||||
.map(<$int>::from_le_bytes)
|
||||
.map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for BE<$int> {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.0.to_be_bytes().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for BE<$int> {
|
||||
#[inline]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
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);
|
||||
}
|
||||
}
|
||||
Vendored
+125
@@ -0,0 +1,125 @@
|
||||
#![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<u32, crate::Error> = 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);
|
||||
}
|
||||
}
|
||||
Vendored
+354
@@ -0,0 +1,354 @@
|
||||
#[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::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for i32 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for i64 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for i128 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for isize {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for u8 {
|
||||
const POSTCARD_MAX_SIZE: usize = 1;
|
||||
}
|
||||
|
||||
impl MaxSize for u16 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for u32 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for u64 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for u128 {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
impl MaxSize for usize {
|
||||
const POSTCARD_MAX_SIZE: usize = varint_max::<Self>();
|
||||
}
|
||||
|
||||
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<T: MaxSize> MaxSize for Option<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE + 1;
|
||||
}
|
||||
|
||||
impl<T: MaxSize, E: MaxSize> MaxSize for Result<T, E> {
|
||||
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<T: MaxSize, const N: usize> MaxSize for [T; N] {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * N;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> MaxSize for &'_ T {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> 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<T> MaxSize for PhantomData<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = 0;
|
||||
}
|
||||
|
||||
impl<A: MaxSize> MaxSize for (A,) {
|
||||
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<A: MaxSize, B: MaxSize> MaxSize for (A, B) {
|
||||
const POSTCARD_MAX_SIZE: usize = A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<A: MaxSize, B: MaxSize, C: MaxSize> MaxSize for (A, B, C) {
|
||||
const POSTCARD_MAX_SIZE: usize =
|
||||
A::POSTCARD_MAX_SIZE + B::POSTCARD_MAX_SIZE + C::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize> 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<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize, E: MaxSize> 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<A: MaxSize, B: MaxSize, C: MaxSize, D: MaxSize, E: MaxSize, F: MaxSize> 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<T: MaxSize> MaxSize for Range<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> MaxSize for RangeInclusive<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE * 2;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> MaxSize for RangeFrom<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> MaxSize for RangeTo<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
impl<T: MaxSize> MaxSize for core::num::Wrapping<T> {
|
||||
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<T: MaxSize> MaxSize for core::num::Saturating<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
|
||||
impl<T: MaxSize> MaxSize for Box<T> {
|
||||
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<T: MaxSize> MaxSize for Arc<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
|
||||
impl<T: MaxSize> MaxSize for Rc<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = T::POSTCARD_MAX_SIZE;
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapless")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
|
||||
impl<T: MaxSize, const N: usize> MaxSize for heapless::Vec<T, N> {
|
||||
const POSTCARD_MAX_SIZE: usize = <[T; N]>::POSTCARD_MAX_SIZE + varint_size(N);
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapless")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "heapless")))]
|
||||
impl<const N: usize> MaxSize for heapless::String<N> {
|
||||
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<T, const R: usize, const C: usize> MaxSize
|
||||
for nalgebra_v0_33::Matrix<
|
||||
T,
|
||||
nalgebra_v0_33::Const<R>,
|
||||
nalgebra_v0_33::Const<C>,
|
||||
nalgebra_v0_33::ArrayStorage<T, R, C>,
|
||||
>
|
||||
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<T: MaxSize> MaxSize for nalgebra_v0_33::Unit<T> {
|
||||
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<T: MaxSize + nalgebra_v0_33::Scalar> MaxSize for nalgebra_v0_33::Quaternion<T> {
|
||||
const POSTCARD_MAX_SIZE: usize = nalgebra_v0_33::Vector4::<T>::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::<usize>() * 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::<u8>::POSTCARD_MAX_SIZE, 1);
|
||||
assert_eq!(Box::<u32>::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::<u8>::POSTCARD_MAX_SIZE, 1);
|
||||
assert_eq!(Arc::<u32>::POSTCARD_MAX_SIZE, 5);
|
||||
assert_eq!(Arc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rc_max_size() {
|
||||
assert_eq!(Rc::<u8>::POSTCARD_MAX_SIZE, 1);
|
||||
assert_eq!(Rc::<u32>::POSTCARD_MAX_SIZE, 5);
|
||||
assert_eq!(Rc::<(u128, [u8; 8])>::POSTCARD_MAX_SIZE, 27);
|
||||
}
|
||||
}
|
||||
Vendored
+729
@@ -0,0 +1,729 @@
|
||||
//! # 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<Slice>, &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<Self::Output>;
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// 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<Self::Output> {
|
||||
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<usize> 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<usize> 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<u8>`] that implements the flavor trait
|
||||
pub struct ExtendFlavor<T> {
|
||||
iter: T,
|
||||
}
|
||||
|
||||
impl<T> ExtendFlavor<T>
|
||||
where
|
||||
T: core::iter::Extend<u8>,
|
||||
{
|
||||
/// Create a new [`Self`] flavor from a given [`core::iter::Extend<u8>`]
|
||||
pub fn new(iter: T) -> Self {
|
||||
Self { iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Flavor for ExtendFlavor<T>
|
||||
where
|
||||
T: core::iter::Extend<u8>,
|
||||
{
|
||||
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<Self::Output> {
|
||||
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<T> {
|
||||
writer: T,
|
||||
}
|
||||
|
||||
impl<T> WriteFlavor<T>
|
||||
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<T> Flavor for WriteFlavor<T>
|
||||
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::Output> {
|
||||
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<T> {
|
||||
writer: T,
|
||||
}
|
||||
|
||||
impl<T> WriteFlavor<T>
|
||||
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<T> Flavor for WriteFlavor<T>
|
||||
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::Output> {
|
||||
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<const B: usize> {
|
||||
/// the contained data buffer
|
||||
vec: Vec<u8, B>,
|
||||
}
|
||||
|
||||
impl<const B: usize> HVec<B> {
|
||||
/// Create a new, currently empty, [`heapless::Vec`] to be used for storing serialized
|
||||
/// output data.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const B: usize> Flavor for HVec<B> {
|
||||
type Output = Vec<u8, B>;
|
||||
|
||||
#[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<Vec<u8, B>> {
|
||||
Ok(self.vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const B: usize> Index<usize> for HVec<B> {
|
||||
type Output = u8;
|
||||
|
||||
fn index(&self, idx: usize) -> &u8 {
|
||||
&self.vec[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const B: usize> IndexMut<usize> for HVec<B> {
|
||||
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<u8>,
|
||||
}
|
||||
|
||||
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<u8>;
|
||||
|
||||
#[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<Self::Output> {
|
||||
Ok(self.vec)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for AllocVec {
|
||||
type Output = u8;
|
||||
|
||||
#[inline]
|
||||
fn index(&self, idx: usize) -> &u8 {
|
||||
&self.vec[idx]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<usize> 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<B>
|
||||
where
|
||||
B: Flavor + IndexMut<usize, Output = u8>,
|
||||
{
|
||||
flav: B,
|
||||
cobs: EncoderState,
|
||||
}
|
||||
|
||||
impl<B> Cobs<B>
|
||||
where
|
||||
B: Flavor + IndexMut<usize, Output = u8>,
|
||||
{
|
||||
/// 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<Self> {
|
||||
bee.try_push(0).map_err(|_| Error::SerializeBufferFull)?;
|
||||
Ok(Self {
|
||||
flav: bee,
|
||||
cobs: EncoderState::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Flavor for Cobs<B>
|
||||
where
|
||||
B: Flavor + IndexMut<usize, Output = u8>,
|
||||
{
|
||||
type Output = <B as Flavor>::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<Self::Output> {
|
||||
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: <https://en.wikipedia.org/wiki/Cyclic_redundancy_check>.
|
||||
#[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 = <B as Flavor>::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<Self::Output> {
|
||||
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<u8>`, 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<T, const B: usize>(
|
||||
value: &T,
|
||||
digest: Digest<'_, $int>,
|
||||
) -> Result<heapless::Vec<u8, B>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
use super::HVec;
|
||||
serialize_with_flavor(value, CrcModifier::new(HVec::default(), digest))
|
||||
}
|
||||
|
||||
/// Serialize a `T` to a `heapless::Vec<u8>`, 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<T>(
|
||||
value: &T,
|
||||
digest: Digest<'_, $int>,
|
||||
) -> Result<alloc::vec::Vec<u8>>
|
||||
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<Self::Output> {
|
||||
Ok(self.size)
|
||||
}
|
||||
}
|
||||
Vendored
+863
@@ -0,0 +1,863 @@
|
||||
use 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::<T, Cobs<Slice<'a>>, &'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::<T, Slice<'a>, &'a mut [u8]>(value, Slice::new(buf))
|
||||
}
|
||||
|
||||
/// Serialize a `T` to a `heapless::Vec<u8>`, 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<u8, 32> = to_vec_cobs(&false).unwrap();
|
||||
/// assert_eq!(ser.deref(), &[0x01, 0x01, 0x00]);
|
||||
///
|
||||
/// let ser: Vec<u8, 32> = 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<u8, 32> = 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<u8, 32> = 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<T, const B: usize>(value: &T) -> Result<Vec<u8, B>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
serialize_with_flavor::<T, Cobs<HVec<B>>, Vec<u8, B>>(value, Cobs::try_new(HVec::default())?)
|
||||
}
|
||||
|
||||
/// Serialize a `T` to a `heapless::Vec<u8>`, 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<u8, 32> = to_vec(&true).unwrap();
|
||||
/// assert_eq!(ser.deref(), &[0x01]);
|
||||
///
|
||||
/// let ser: Vec<u8, 32> = 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<u8, 32> = to_vec(data).unwrap();
|
||||
/// assert_eq!(ser.deref(), &[0x04, 0x01, 0x00, 0x20, 0x30]);
|
||||
///
|
||||
/// let data: &[u8; 4] = &[0x01u8, 0x00, 0x20, 0x30];
|
||||
/// let ser: Vec<u8, 32> = 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<T, const B: usize>(value: &T) -> Result<Vec<u8, B>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
serialize_with_flavor::<T, HVec<B>, Vec<u8, B>>(value, HVec::default())
|
||||
}
|
||||
|
||||
/// Serialize a `T` to a `std::vec::Vec<u8>`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use postcard::to_stdvec;
|
||||
///
|
||||
/// let ser: Vec<u8> = to_stdvec(&true).unwrap();
|
||||
/// assert_eq!(ser.as_slice(), &[0x01]);
|
||||
///
|
||||
/// let ser: Vec<u8> = 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<T>(value: &T) -> Result<std::vec::Vec<u8>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
to_allocvec(value)
|
||||
}
|
||||
|
||||
/// Serialize and COBS encode a `T` to a `std::vec::Vec<u8>`.
|
||||
///
|
||||
/// The terminating sentinel `0x00` byte is included in the output.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use postcard::to_stdvec_cobs;
|
||||
///
|
||||
/// let ser: Vec<u8> = to_stdvec_cobs(&true).unwrap();
|
||||
/// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]);
|
||||
///
|
||||
/// let ser: Vec<u8> = 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<T>(value: &T) -> Result<std::vec::Vec<u8>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
to_allocvec_cobs(value)
|
||||
}
|
||||
|
||||
/// Serialize a `T` to an `alloc::vec::Vec<u8>`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use postcard::to_allocvec;
|
||||
///
|
||||
/// let ser: Vec<u8> = to_allocvec(&true).unwrap();
|
||||
/// assert_eq!(ser.as_slice(), &[0x01]);
|
||||
///
|
||||
/// let ser: Vec<u8> = 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<T>(value: &T) -> Result<alloc::vec::Vec<u8>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
serialize_with_flavor::<T, AllocVec, alloc::vec::Vec<u8>>(value, AllocVec::new())
|
||||
}
|
||||
|
||||
/// Serialize and COBS encode a `T` to an `alloc::vec::Vec<u8>`.
|
||||
///
|
||||
/// The terminating sentinel `0x00` byte is included in the output.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use postcard::to_allocvec_cobs;
|
||||
///
|
||||
/// let ser: Vec<u8> = to_allocvec_cobs(&true).unwrap();
|
||||
/// assert_eq!(ser.as_slice(), &[0x02, 0x01, 0x00]);
|
||||
///
|
||||
/// let ser: Vec<u8> = 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<T>(value: &T) -> Result<alloc::vec::Vec<u8>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
serialize_with_flavor::<T, Cobs<AllocVec>, alloc::vec::Vec<u8>>(
|
||||
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<T, W>(value: &T, writer: W) -> Result<W>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
W: core::iter::Extend<u8>,
|
||||
{
|
||||
serialize_with_flavor::<T, _, _>(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<T, W>(value: &T, writer: W) -> Result<W>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
W: crate::eio::Write,
|
||||
{
|
||||
serialize_with_flavor::<T, _, _>(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<T, W>(value: &T, writer: W) -> Result<W>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
W: std::io::Write,
|
||||
{
|
||||
serialize_with_flavor::<T, _, _>(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::<u32>::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<u8>`, 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::<u32>::new(&CRC_32_ISCSI);
|
||||
/// let ser: Vec<u8, 32> = 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<u8, 32> = 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<T, const B: usize>(
|
||||
value: &T,
|
||||
digest: crc::Digest<'_, u32>,
|
||||
) -> Result<heapless::Vec<u8, B>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
flavors::crc::to_vec_u32(value, digest)
|
||||
}
|
||||
|
||||
/// Conveniently serialize a `T` to a `heapless::Vec<u8>`, 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::<u32>::new(&CRC_32_ISCSI);
|
||||
/// let ser: Vec<u8> = 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<u8> = 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<T>(value: &T, digest: crc::Digest<'_, u32>) -> Result<std::vec::Vec<u8>>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
flavors::crc::to_allocvec_u32(value, digest)
|
||||
}
|
||||
|
||||
/// Conveniently serialize a `T` to a `heapless::Vec<u8>`, 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::<u32>::new(&CRC_32_ISCSI);
|
||||
/// let ser: Vec<u8> = 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<u8> = 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<T>(value: &T, digest: crc::Digest<'_, u32>) -> Result<alloc::vec::Vec<u8>>
|
||||
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<Slice>, &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<T, S, O>(value: &T, storage: S) -> Result<O>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
S: Flavor<Output = O>,
|
||||
{
|
||||
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<T>(value: &T) -> Result<usize>
|
||||
where
|
||||
T: Serialize + ?Sized,
|
||||
{
|
||||
serialize_with_flavor::<T, flavors::Size, usize>(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<u8, 1> = to_vec(&0x05u8).unwrap();
|
||||
assert_eq!(&[5], output.deref());
|
||||
assert!(output.len() == serialized_size(&0x05u8).unwrap());
|
||||
assert!(output.len() <= Vec::<u8, 1>::POSTCARD_MAX_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_u16() {
|
||||
const SZ: usize = varint_max::<u16>();
|
||||
let output: Vec<u8, SZ> = to_vec(&0xA5C7u16).unwrap();
|
||||
assert_eq!(&[0xC7, 0xCB, 0x02], output.deref());
|
||||
assert!(output.len() == serialized_size(&0xA5C7u16).unwrap());
|
||||
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_u32() {
|
||||
const SZ: usize = varint_max::<u32>();
|
||||
let output: Vec<u8, SZ> = to_vec(&0xCDAB3412u32).unwrap();
|
||||
assert_eq!(&[0x92, 0xE8, 0xAC, 0xED, 0x0C], output.deref());
|
||||
assert!(output.len() == serialized_size(&0xCDAB3412u32).unwrap());
|
||||
assert!(output.len() <= Vec::<u8, SZ>::POSTCARD_MAX_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_u64() {
|
||||
const SZ: usize = varint_max::<u64>();
|
||||
let output: Vec<u8, SZ> = 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::<u8, SZ>::POSTCARD_MAX_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ser_u128() {
|
||||
const SZ: usize = varint_max::<u128>();
|
||||
let output: Vec<u8, SZ> = 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::<u8, SZ>::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<u8, SZ> = 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<u8, 9> = 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<u8, 1024> = Vec::new();
|
||||
for i in 0..1024 {
|
||||
input.push((i & 0xFF) as u8).unwrap();
|
||||
}
|
||||
let output: Vec<u8, 2048> = 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<u8, 17> = 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<u8, 2048> = 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::<usize>()];
|
||||
let res = varint_usize(1, &mut buf);
|
||||
|
||||
assert_eq!(&[1], res);
|
||||
|
||||
let res = varint_usize(usize::MAX, &mut buf);
|
||||
|
||||
//
|
||||
if varint_max::<usize>() == varint_max::<u32>() {
|
||||
assert_eq!(&[0xFF, 0xFF, 0xFF, 0xFF, 0x0F], res);
|
||||
} else if varint_max::<usize>() == varint_max::<u64>() {
|
||||
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<u8, 1> = 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<u8, { 1 + varint_max::<u64>() }> = 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<u8, { 1 + varint_max::<u16>() }> = 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<u8, 2> = 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<u8, 8> = 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<u8, 8> = 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<u8, 8> = 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<u8, 128> = 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<u8, 128> = 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<u8, 128> = 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<u8, 1> = to_vec(&input).unwrap();
|
||||
assert_eq!(&[0x05], output.deref());
|
||||
assert!(output.len() == serialized_size(&input).unwrap());
|
||||
|
||||
let input = TupleStruct((0xA0, 0x1234));
|
||||
let output: Vec<u8, 3> = 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<u8, 11> = 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<u8, 1> = to_vec(&()).unwrap();
|
||||
assert_eq!(output.len(), 0);
|
||||
assert!(output.len() == serialized_size(&()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn heapless_data() {
|
||||
let mut input: Vec<u8, 4> = Vec::new();
|
||||
input.extend_from_slice(&[0x01, 0x02, 0x03, 0x04]).unwrap();
|
||||
let output: Vec<u8, 5> = 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<u8, 7> = 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<u8, u8, 4> = 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<u8, 100> = 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<u8, 13> = to_vec_cobs(&input).unwrap();
|
||||
|
||||
let sz = cobs::decode_in_place(output.deref_mut()).unwrap();
|
||||
|
||||
let x = crate::from_bytes::<RefStruct<'_>>(&output.deref_mut()[..sz]).unwrap();
|
||||
|
||||
assert_eq!(input, x);
|
||||
}
|
||||
}
|
||||
+564
@@ -0,0 +1,564 @@
|
||||
use 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<F>
|
||||
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<F: Flavor> Serializer<F> {
|
||||
/// 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::<usize>()];
|
||||
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::<u128>()];
|
||||
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::<u64>()];
|
||||
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::<u32>()];
|
||||
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::<u16>()];
|
||||
let used_buf = varint_u16(data, &mut buf);
|
||||
self.output.try_extend(used_buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::Serializer for &mut Serializer<F>
|
||||
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<T>(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<T>(self, _name: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_variant<T>(
|
||||
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<usize>) -> Result<Self::SerializeSeq> {
|
||||
self.try_push_varint_usize(len.ok_or(Error::SerializeSeqLengthUnknown)?)
|
||||
.map_err(|_| Error::SerializeBufferFull)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant> {
|
||||
self.try_push_varint_u32(variant_index)
|
||||
.map_err(|_| Error::SerializeBufferFull)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
|
||||
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<Self::SerializeStruct> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant> {
|
||||
self.try_push_varint_u32(variant_index)
|
||||
.map_err(|_| Error::SerializeBufferFull)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn collect_str<T>(self, value: &T) -> Result<Self::Ok>
|
||||
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<IF> 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<F> ser::SerializeSeq for &mut Serializer<F>
|
||||
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<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
// Close the sequence.
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeTuple for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_element<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeTupleStruct for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeTupleVariant for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeMap for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_key<T>(&mut self, key: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
key.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_value<T>(&mut self, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeStruct for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> Result<()>
|
||||
where
|
||||
T: ?Sized + Serialize,
|
||||
{
|
||||
value.serialize(&mut **self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn end(self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> ser::SerializeStructVariant for &mut Serializer<F>
|
||||
where
|
||||
F: Flavor,
|
||||
{
|
||||
type Ok = ();
|
||||
type Error = Error;
|
||||
|
||||
#[inline]
|
||||
fn serialize_field<T>(&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
|
||||
}
|
||||
Vendored
+103
@@ -0,0 +1,103 @@
|
||||
/// Returns the maximum number of bytes required to encode T.
|
||||
pub const fn varint_max<T: Sized>() -> 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::<T>() * 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<T: Sized>() -> u8 {
|
||||
let max_bits = core::mem::size_of::<T>() * 8;
|
||||
let extra_bits = max_bits % 7;
|
||||
(1 << extra_bits) - 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn varint_usize(n: usize, out: &mut [u8; varint_max::<usize>()]) -> &mut [u8] {
|
||||
let mut value = n;
|
||||
for i in 0..varint_max::<usize>() {
|
||||
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::<u16>()]) -> &mut [u8] {
|
||||
let mut value = n;
|
||||
for i in 0..varint_max::<u16>() {
|
||||
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::<u32>()]) -> &mut [u8] {
|
||||
let mut value = n;
|
||||
for i in 0..varint_max::<u32>() {
|
||||
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::<u64>()]) -> &mut [u8] {
|
||||
let mut value = n;
|
||||
for i in 0..varint_max::<u64>() {
|
||||
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::<u128>()]) -> &mut [u8] {
|
||||
let mut value = n;
|
||||
for i in 0..varint_max::<u128>() {
|
||||
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[..]
|
||||
}
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
use 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::<Huge>(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);
|
||||
}
|
||||
Vendored
+85
@@ -0,0 +1,85 @@
|
||||
#[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::<u32>::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::<u8>::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::<u32>::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::<u32>::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]);
|
||||
}
|
||||
Vendored
+251
@@ -0,0 +1,251 @@
|
||||
use 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<u8, 4> = 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<u8, u8, 4> = 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<T>(data: T, ser_rep: &[u8])
|
||||
where
|
||||
T: Serialize + DeserializeOwned + Eq + PartialEq + Debug,
|
||||
{
|
||||
let serialized: Vec<u8, 2048> = to_vec(&data).unwrap();
|
||||
assert_eq!(serialized.len(), ser_rep.len());
|
||||
let mut x: ::std::vec::Vec<u8> = 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<T>(data: T, ser_rep: &[u8])
|
||||
where
|
||||
T: Serialize + DeserializeOwned + Eq + PartialEq + Debug,
|
||||
{
|
||||
let serialized: ::std::vec::Vec<u8> = 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<T>(data: T, ser_rep: &[u8])
|
||||
where
|
||||
T: Serialize + DeserializeOwned + Eq + PartialEq + Debug,
|
||||
{
|
||||
let serialized: ::std::vec::Vec<u8> = 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,
|
||||
],
|
||||
);
|
||||
}
|
||||
Vendored
+101
@@ -0,0 +1,101 @@
|
||||
#![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<u8>,
|
||||
}
|
||||
|
||||
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<const N: usize>(buf: &mut [u8]) {
|
||||
let mut v = heapless::Vec::<u8, N>::new();
|
||||
for _ in 0..N {
|
||||
v.push(0).unwrap();
|
||||
}
|
||||
|
||||
let serialized = postcard::to_slice(&v, buf).unwrap();
|
||||
|
||||
assert_eq!(heapless::Vec::<u8, N>::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<u8>,
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "experimental-derive")]
|
||||
// #[test]
|
||||
// fn test_not_implemented() {
|
||||
// #[derive(postcard::MaxSize)]
|
||||
// struct Foo {
|
||||
// a: &'static str,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
Reference in new issue
Block a user