├── .github ├── FUNDING.yml └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE.md ├── Makefile.toml ├── README.md ├── docs ├── format-specification.md └── pgo.md ├── examples ├── bytes.rs └── simple.rs ├── rust-toolchain.toml ├── rustfmt.toml ├── src ├── buffer.rs ├── config.rs ├── de.rs ├── docs │ └── mod.rs ├── error.rs ├── format.rs ├── io.rs ├── lib.rs ├── ser.rs ├── tests │ ├── basic_types.rs │ ├── features.rs │ ├── mod.rs │ ├── serde_features.rs │ ├── special_handling.rs │ └── versioning.rs └── value │ ├── de.rs │ ├── mod.rs │ ├── ser.rs │ └── tests.rs └── tests ├── all ├── json_data.rs └── main.rs └── data └── json-edge-cases.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [FlixCoder] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | tags: ["*"] 7 | pull_request: 8 | branches: ["*"] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | formatting: 16 | name: Formatting 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | 22 | - name: Install/update Rust 23 | shell: bash 24 | run: rustup toolchain install nightly --component rustfmt 25 | 26 | - name: Formatting 27 | shell: bash 28 | run: cargo +nightly fmt -- --check 29 | 30 | checks: 31 | name: Tests and Lints 32 | runs-on: ubuntu-latest 33 | 34 | steps: 35 | - uses: actions/checkout@v4 36 | 37 | - name: Install/update Rust 38 | shell: bash 39 | run: | 40 | rustup update 41 | rustup show # Uses rust-toolchain.toml file to install the correct version and components. 42 | 43 | - uses: taiki-e/install-action@v2 44 | with: 45 | tool: cargo-make 46 | 47 | - name: Caching 48 | uses: Swatinem/rust-cache@v2 49 | 50 | - name: Run the CI checks 51 | shell: bash 52 | run: cargo make stable-ci 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /.vscode 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [0.1.1] - 2024-11-06 6 | 7 | ### Documentation 8 | 9 | - Fixed and improved documentation 10 | 11 | ## [0.1.0] - 2024-09-29 12 | 13 | ### Features 14 | 15 | - Initial implementation 16 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "byteorder" 16 | version = "1.5.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 19 | 20 | [[package]] 21 | name = "cfg-if" 22 | version = "1.0.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 25 | 26 | [[package]] 27 | name = "hash32" 28 | version = "0.3.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 31 | dependencies = [ 32 | "byteorder", 33 | ] 34 | 35 | [[package]] 36 | name = "heapless" 37 | version = "0.8.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 40 | dependencies = [ 41 | "hash32", 42 | "serde", 43 | "stable_deref_trait", 44 | ] 45 | 46 | [[package]] 47 | name = "itoa" 48 | version = "1.0.14" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" 51 | 52 | [[package]] 53 | name = "lazy_static" 54 | version = "1.5.0" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 57 | 58 | [[package]] 59 | name = "log" 60 | version = "0.4.25" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 63 | 64 | [[package]] 65 | name = "matchers" 66 | version = "0.1.0" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 69 | dependencies = [ 70 | "regex-automata 0.1.10", 71 | ] 72 | 73 | [[package]] 74 | name = "memchr" 75 | version = "2.7.4" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 78 | 79 | [[package]] 80 | name = "nu-ansi-term" 81 | version = "0.46.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 84 | dependencies = [ 85 | "overload", 86 | "winapi", 87 | ] 88 | 89 | [[package]] 90 | name = "once_cell" 91 | version = "1.20.3" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" 94 | 95 | [[package]] 96 | name = "overload" 97 | version = "0.1.1" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 100 | 101 | [[package]] 102 | name = "pin-project-lite" 103 | version = "0.2.16" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 106 | 107 | [[package]] 108 | name = "proc-macro2" 109 | version = "1.0.93" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" 112 | dependencies = [ 113 | "unicode-ident", 114 | ] 115 | 116 | [[package]] 117 | name = "quote" 118 | version = "1.0.38" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" 121 | dependencies = [ 122 | "proc-macro2", 123 | ] 124 | 125 | [[package]] 126 | name = "regex" 127 | version = "1.11.1" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 130 | dependencies = [ 131 | "aho-corasick", 132 | "memchr", 133 | "regex-automata 0.4.9", 134 | "regex-syntax 0.8.5", 135 | ] 136 | 137 | [[package]] 138 | name = "regex-automata" 139 | version = "0.1.10" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" 142 | dependencies = [ 143 | "regex-syntax 0.6.29", 144 | ] 145 | 146 | [[package]] 147 | name = "regex-automata" 148 | version = "0.4.9" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 151 | dependencies = [ 152 | "aho-corasick", 153 | "memchr", 154 | "regex-syntax 0.8.5", 155 | ] 156 | 157 | [[package]] 158 | name = "regex-syntax" 159 | version = "0.6.29" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 162 | 163 | [[package]] 164 | name = "regex-syntax" 165 | version = "0.8.5" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 168 | 169 | [[package]] 170 | name = "ryu" 171 | version = "1.0.19" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" 174 | 175 | [[package]] 176 | name = "serde" 177 | version = "1.0.218" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" 180 | dependencies = [ 181 | "serde_derive", 182 | ] 183 | 184 | [[package]] 185 | name = "serde-brief" 186 | version = "0.1.1" 187 | dependencies = [ 188 | "heapless", 189 | "serde", 190 | "serde_bytes", 191 | "serde_json", 192 | "tracing", 193 | "tracing-subscriber", 194 | ] 195 | 196 | [[package]] 197 | name = "serde_bytes" 198 | version = "0.11.15" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "387cc504cb06bb40a96c8e04e951fe01854cf6bc921053c954e4a606d9675c6a" 201 | dependencies = [ 202 | "serde", 203 | ] 204 | 205 | [[package]] 206 | name = "serde_derive" 207 | version = "1.0.218" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" 210 | dependencies = [ 211 | "proc-macro2", 212 | "quote", 213 | "syn", 214 | ] 215 | 216 | [[package]] 217 | name = "serde_json" 218 | version = "1.0.139" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "44f86c3acccc9c65b153fe1b85a3be07fe5515274ec9f0653b4a0875731c72a6" 221 | dependencies = [ 222 | "itoa", 223 | "memchr", 224 | "ryu", 225 | "serde", 226 | ] 227 | 228 | [[package]] 229 | name = "sharded-slab" 230 | version = "0.1.7" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 233 | dependencies = [ 234 | "lazy_static", 235 | ] 236 | 237 | [[package]] 238 | name = "smallvec" 239 | version = "1.14.0" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" 242 | 243 | [[package]] 244 | name = "stable_deref_trait" 245 | version = "1.2.0" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" 248 | 249 | [[package]] 250 | name = "syn" 251 | version = "2.0.98" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" 254 | dependencies = [ 255 | "proc-macro2", 256 | "quote", 257 | "unicode-ident", 258 | ] 259 | 260 | [[package]] 261 | name = "thread_local" 262 | version = "1.1.8" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 265 | dependencies = [ 266 | "cfg-if", 267 | "once_cell", 268 | ] 269 | 270 | [[package]] 271 | name = "tracing" 272 | version = "0.1.41" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" 275 | dependencies = [ 276 | "pin-project-lite", 277 | "tracing-attributes", 278 | "tracing-core", 279 | ] 280 | 281 | [[package]] 282 | name = "tracing-attributes" 283 | version = "0.1.28" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" 286 | dependencies = [ 287 | "proc-macro2", 288 | "quote", 289 | "syn", 290 | ] 291 | 292 | [[package]] 293 | name = "tracing-core" 294 | version = "0.1.33" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" 297 | dependencies = [ 298 | "once_cell", 299 | "valuable", 300 | ] 301 | 302 | [[package]] 303 | name = "tracing-log" 304 | version = "0.2.0" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 307 | dependencies = [ 308 | "log", 309 | "once_cell", 310 | "tracing-core", 311 | ] 312 | 313 | [[package]] 314 | name = "tracing-subscriber" 315 | version = "0.3.19" 316 | source = "registry+https://github.com/rust-lang/crates.io-index" 317 | checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" 318 | dependencies = [ 319 | "matchers", 320 | "nu-ansi-term", 321 | "once_cell", 322 | "regex", 323 | "sharded-slab", 324 | "smallvec", 325 | "thread_local", 326 | "tracing", 327 | "tracing-core", 328 | "tracing-log", 329 | ] 330 | 331 | [[package]] 332 | name = "unicode-ident" 333 | version = "1.0.17" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" 336 | 337 | [[package]] 338 | name = "valuable" 339 | version = "0.1.1" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 342 | 343 | [[package]] 344 | name = "winapi" 345 | version = "0.3.9" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 348 | dependencies = [ 349 | "winapi-i686-pc-windows-gnu", 350 | "winapi-x86_64-pc-windows-gnu", 351 | ] 352 | 353 | [[package]] 354 | name = "winapi-i686-pc-windows-gnu" 355 | version = "0.4.0" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 358 | 359 | [[package]] 360 | name = "winapi-x86_64-pc-windows-gnu" 361 | version = "0.4.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 364 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Flix "] 3 | categories = ["encoding", "parser-implementations", "no-std", "no-std::no-alloc", "embedded"] 4 | description = "A brief, self-descriptive, serde-compatible binary format." 5 | documentation = "https://docs.rs/serde-brief" 6 | edition = "2024" 7 | exclude = ["/tests/data", "/.github"] 8 | homepage = "https://github.com/FlixCoder/serde-brief" 9 | keywords = ["serde", "encoding", "binary", "data", "no-std"] 10 | license = "MIT" 11 | name = "serde-brief" 12 | readme = "README.md" 13 | repository = "https://github.com/FlixCoder/serde-brief" 14 | rust-version = "1.85" 15 | version = "0.1.1" 16 | 17 | [features] 18 | default = [] 19 | alloc = ["serde/alloc"] 20 | std = ["alloc", "serde/std", "tracing?/std"] 21 | tracing = ["dep:tracing"] 22 | heapless = ["dep:heapless"] 23 | 24 | [dependencies] 25 | heapless = { version = "0.8.0", optional = true, features = ["serde"] } 26 | serde = { version = "1.0.210", default-features = false } 27 | tracing = { version = "0.1.40", optional = true, default-features = false, features = ["attributes"] } 28 | 29 | [dev-dependencies] 30 | serde = { version = "1.0.210", features = ["derive"] } 31 | serde_bytes = "0.11.15" 32 | serde_json = "1.0.139" 33 | tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } 34 | 35 | 36 | # Also test the examples 37 | [[example]] 38 | name = "simple" 39 | path = "examples/simple.rs" 40 | test = true 41 | 42 | [[example]] 43 | name = "bytes" 44 | path = "examples/bytes.rs" 45 | test = true 46 | 47 | 48 | # Add more lints. 49 | [lints.rust] 50 | closure_returning_async_block = "warn" 51 | missing_debug_implementations = "warn" 52 | missing_docs = "warn" 53 | trivial_casts = "warn" 54 | trivial_numeric_casts = "warn" 55 | unused_extern_crates = "warn" 56 | 57 | [lints.clippy] 58 | tabs_in_doc_comments = "allow" # Rustfmt setting. 59 | unnecessary_lazy_evaluations = "allow" # Performance better, because `Drop`. 60 | allow_attributes_without_reason = "warn" 61 | assigning_clones = "warn" 62 | borrow_as_ptr = "warn" 63 | branches_sharing_code = "warn" 64 | cast_lossless = "warn" 65 | cast_possible_truncation = "warn" 66 | cast_possible_wrap = "warn" 67 | cast_precision_loss = "warn" 68 | cast_ptr_alignment = "warn" 69 | cast_sign_loss = "warn" 70 | checked_conversions = "warn" 71 | clear_with_drain = "warn" 72 | cloned_instead_of_copied = "warn" 73 | collection_is_never_read = "warn" 74 | copy_iterator = "warn" 75 | create_dir = "warn" 76 | dbg_macro = "warn" 77 | debug_assert_with_mut_call = "warn" 78 | decimal_literal_representation = "warn" 79 | default_trait_access = "warn" 80 | expect_used = "warn" 81 | expl_impl_clone_on_copy = "warn" 82 | fallible_impl_from = "warn" 83 | filetype_is_file = "warn" 84 | filter_map_next = "warn" 85 | flat_map_option = "warn" 86 | float_cmp = "warn" 87 | fn_params_excessive_bools = "warn" 88 | fn_to_numeric_cast_any = "warn" 89 | format_collect = "warn" 90 | format_push_string = "warn" 91 | future_not_send = "warn" 92 | if_then_some_else_none = "warn" 93 | ignored_unit_patterns = "warn" 94 | impl_trait_in_params = "warn" 95 | implicit_clone = "warn" 96 | implicit_hasher = "warn" 97 | imprecise_flops = "warn" 98 | indexing_slicing = "warn" 99 | inefficient_to_string = "warn" 100 | items_after_statements = "warn" 101 | iter_filter_is_some = "warn" 102 | iter_not_returning_iterator = "warn" 103 | iter_on_empty_collections = "warn" 104 | iter_on_single_items = "warn" 105 | large_digit_groups = "warn" 106 | large_futures = "warn" 107 | large_stack_arrays = "warn" 108 | large_stack_frames = "warn" 109 | large_types_passed_by_value = "warn" 110 | literal_string_with_formatting_args = "warn" 111 | lossy_float_literal = "warn" 112 | macro_use_imports = "warn" 113 | manual_assert = "warn" 114 | manual_instant_elapsed = "warn" 115 | manual_is_power_of_two = "warn" 116 | manual_is_variant_and = "warn" 117 | manual_let_else = "warn" 118 | manual_ok_or = "warn" 119 | many_single_char_names = "warn" 120 | map_unwrap_or = "warn" 121 | map_with_unused_argument_over_ranges = "warn" 122 | mismatching_type_param_order = "warn" 123 | missing_const_for_fn = "warn" 124 | missing_docs_in_private_items = "warn" 125 | missing_fields_in_debug = "warn" 126 | must_use_candidate = "warn" 127 | mut_mut = "warn" 128 | mutex_atomic = "warn" 129 | mutex_integer = "warn" 130 | needless_bitwise_bool = "warn" 131 | needless_collect = "warn" 132 | needless_pass_by_ref_mut = "warn" 133 | non_send_fields_in_send_ty = "warn" 134 | non_std_lazy_statics = "warn" 135 | option_option = "warn" 136 | or_fun_call = "warn" 137 | path_buf_push_overwrite = "warn" 138 | print_stderr = "warn" 139 | print_stdout = "warn" 140 | ptr_as_ptr = "warn" 141 | ptr_cast_constness = "warn" 142 | range_minus_one = "warn" 143 | rc_buffer = "warn" 144 | rc_mutex = "warn" 145 | read_zero_byte_vec = "warn" 146 | ref_binding_to_reference = "warn" 147 | ref_option_ref = "warn" 148 | return_self_not_must_use = "warn" 149 | same_functions_in_if_condition = "warn" 150 | same_name_method = "warn" 151 | set_contains_or_insert = "warn" 152 | stable_sort_primitive = "warn" 153 | str_to_string = "warn" 154 | string_lit_chars_any = "warn" 155 | string_to_string = "warn" 156 | suboptimal_flops = "warn" 157 | suspicious_operation_groupings = "warn" 158 | suspicious_xor_used_as_pow = "warn" 159 | tests_outside_test_module = "warn" 160 | too_many_lines = "warn" 161 | trait_duplication_in_bounds = "warn" 162 | trivially_copy_pass_by_ref = "warn" 163 | try_err = "warn" 164 | type_repetition_in_bounds = "warn" 165 | unchecked_duration_subtraction = "warn" 166 | undocumented_unsafe_blocks = "warn" 167 | unnecessary_box_returns = "warn" 168 | unnecessary_literal_bound = "warn" 169 | unnested_or_patterns = "warn" 170 | unreadable_literal = "warn" 171 | unsafe_derive_deserialize = "warn" 172 | unseparated_literal_suffix = "warn" 173 | unused_async = "warn" 174 | unused_self = "warn" 175 | unwrap_used = "warn" 176 | used_underscore_binding = "warn" 177 | used_underscore_items = "warn" 178 | useless_let_if_seq = "warn" 179 | verbose_file_reads = "warn" 180 | 181 | 182 | # Metadata for docs.rs to enable features and show everything. 183 | [package.metadata.docs.rs] 184 | all-features = true 185 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 FlixCoder 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.toml: -------------------------------------------------------------------------------- 1 | [config] 2 | skip_core_tasks = true 3 | default_to_workspace = false 4 | time_summary = true 5 | 6 | 7 | [env] 8 | CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true 9 | RUST_BACKTRACE = 1 10 | 11 | 12 | [tasks.install-rust-nightly-rustfmt] 13 | private = true 14 | description = "Installs nightly Rust with rustfmt (hopefully)." 15 | toolchain = "nightly" 16 | install_crate = { rustup_component_name = "rustfmt", binary = "rustfmt", test_arg = "--version" } 17 | 18 | [tasks.install-rust-toolchain] 19 | private = true 20 | description = "Installs the used Rust toolchain with specified components." 21 | command = "rustup" 22 | args = ["show"] 23 | 24 | 25 | [tasks.format] 26 | description = "Formats all Rust code." 27 | install_crate = false 28 | command = "cargo" 29 | args = ["+nightly", "fmt"] 30 | dependencies = ["install-rust-nightly-rustfmt"] 31 | 32 | [tasks.formatting] 33 | description = "Checks all Rust code formatting." 34 | install_crate = false 35 | command = "cargo" 36 | args = ["+nightly", "fmt", "--", "--check"] 37 | dependencies = ["install-rust-nightly-rustfmt"] 38 | 39 | 40 | [tasks.clippy-default] 41 | install_crate = false 42 | command = "cargo" 43 | args = ["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"] 44 | dependencies = ["install-rust-toolchain"] 45 | 46 | [tasks.clippy-none] 47 | install_crate = false 48 | command = "cargo" 49 | args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--", "-D", "warnings"] 50 | dependencies = ["install-rust-toolchain"] 51 | 52 | [tasks.clippy-alloc] 53 | install_crate = false 54 | command = "cargo" 55 | args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--features", "alloc", "--", "-D", "warnings"] 56 | dependencies = ["install-rust-toolchain"] 57 | 58 | [tasks.clippy-std] 59 | install_crate = false 60 | command = "cargo" 61 | args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--features", "std", "--", "-D", "warnings"] 62 | dependencies = ["install-rust-toolchain"] 63 | 64 | [tasks.clippy-heapless] 65 | install_crate = false 66 | command = "cargo" 67 | args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--features", "heapless", "--", "-D", "warnings"] 68 | dependencies = ["install-rust-toolchain"] 69 | 70 | [tasks.clippy-all] 71 | install_crate = false 72 | command = "cargo" 73 | args = ["clippy", "--workspace", "--all-targets", "--all-features", "--", "-D", "warnings"] 74 | dependencies = ["install-rust-toolchain"] 75 | 76 | [tasks.clippy] 77 | description = "Runs clippy with all various feature sets." 78 | dependencies = [ 79 | "clippy-default", 80 | "clippy-none", 81 | "clippy-alloc", 82 | "clippy-std", 83 | "clippy-heapless", 84 | "clippy-all", 85 | ] 86 | 87 | 88 | [tasks.test-all-features] 89 | description = "Runs all tests via cargo test with all features." 90 | install_crate = false 91 | command = "cargo" 92 | args = ["test", "--workspace", "--all-features"] 93 | dependencies = ["install-rust-toolchain"] 94 | 95 | [tasks.test-nextest] 96 | description = "Runs all tests via nextest (installs nextest if necessary)." 97 | install_crate = true 98 | command = "cargo" 99 | args = ["nextest", "run", "--workspace", "--all-features"] 100 | dependencies = ["install-rust-toolchain"] 101 | 102 | [tasks.test-docs] 103 | description = "Runs all doc-tests (since nextest does not)." 104 | install_crate = false 105 | command = "cargo" 106 | args = ["test", "--workspace", "--all-features", "--doc"] 107 | dependencies = ["install-rust-toolchain"] 108 | 109 | [tasks.nextest] 110 | description = "Runs all tests via cargo nextest." 111 | dependencies = ["test-nextest", "test-docs"] 112 | 113 | [tasks.test] 114 | description = "Runs all tests via cargo test." 115 | dependencies = ["test-all-features"] # Future: potentially some no-std test. 116 | 117 | 118 | [tasks.stable-ci] 119 | description = """ 120 | Runs all CI checks with stable Rust (all but formatting). 121 | """ 122 | dependencies = ["test", "clippy"] 123 | 124 | [tasks.ci] 125 | description = """ 126 | Runs all checks necessary for CI to pass. 127 | This includes formatting, clippy and tests currently. 128 | """ 129 | dependencies = ["test", "clippy", "formatting"] 130 | 131 | 132 | [tasks.default] 133 | alias = "ci" 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Serde-Brief 2 | 3 | [![crates.io page](https://img.shields.io/crates/v/serde-brief.svg)](https://crates.io/crates/serde-brief) 4 | [![docs.rs page](https://docs.rs/serde-brief/badge.svg)](https://docs.rs/serde-brief/) 5 | ![license: MIT](https://img.shields.io/crates/l/serde-brief.svg) 6 | 7 | Serde-Brief (German for letter) is a crate for encoding and decoding data into a binary format that is self-descriptive and [serde](https://docs.rs/serde/)-compatible. 8 | 9 | ## Design Goals 10 | 11 | Not necessarily in order of importance: 12 | 13 | - Convenient to use for developers: Integrates into the Rust ecosystem via `serde`, supporting all of its features in its derived implementations (e.g. renaming, flattening, ..). 14 | - Compatibility: Easy to add or re-order fields/variants without breakage. Detects wrong data types. 15 | - `#![no_std]` and std compatible. 16 | - Resource efficient: High performance, low memory usage. 17 | - Interoperability: Different architectures can communicate flawlessly. 18 | - Well-tested: Ensure safety (currently, there is no use of `unsafe`). 19 | 20 | ## Binary Format 21 | 22 | The format is new and therefore NOT YET STABLE. 23 | 24 | The format is specified [here](./docs/format-specification.md). 25 | 26 | ### Flavors / Modes 27 | 28 | By default, structs' field names and enums' variant names are encoded as strings. This can be configured to be encoded as unsigned integers of their indices instead. However, this has compatibility implications and some serde features do not work with the index representation. See the format specification for more info. 29 | 30 | ## Comparisons 31 | 32 | How does Serde-Brief compare to ..? 33 | 34 | ### [Postcard](https://docs.rs/postcard/) 35 | 36 | Postcard is NOT a self-describing format. It's encoding solely consists of the raw data and the deserializer needs to have the same information on the data schema. This makes it more difficult to change the data format, e.g. add new fields. 37 | 38 | Postcard is producing way smaller encoded data due to the missing schema information and field names. It is also faster. 39 | 40 | Serde-Brief supports decoding unknown data and parsing it into the requested structures regardless of additional fields or different orders. 41 | 42 | ### [Pot](https://docs.rs/pot/) 43 | 44 | Pot is a self-describing format as well. It's encoding is more space-efficient due to reducing repeated type/schema definitions. This comes at the cost of serialization/deserialization speed. 45 | 46 | It is also not no-std compatible. 47 | 48 | Serde-Brief is faster most of the times, but less space-efficient. 49 | 50 | ### [Serde_json](https://docs.rs/serde_json/) 51 | 52 | JSON is a self-describing format as well. However, it is text based and therefore requires string escaping. Bytes cannot be efficiently represented. However, JSON is widely adopted, as you already know :D 53 | 54 | In Serde-Brief, map keys can not only be strings. Unlike in JSON, keys can be nested data, so something like `HashMap` can be serialized and deserialized without issues. 55 | 56 | Serde-Brief is both more space-efficient and faster. 57 | 58 | ## Usage 59 | 60 | Add the library to your project with `cargo add serde-brief`. By default, no features are enabled (currently), so it is no-std by default. You can enable use of `Vec`s and such with features like `alloc` or `std`. 61 | 62 | ### Example Serialization/Deserialization 63 | 64 | The `heapless` feature was enabled for this example. It is similarly possible with `std`'s `Vec` or just slices. 65 | 66 | ```rust 67 | use heapless::Vec; 68 | use serde::{Serialize, Deserialize}; 69 | 70 | #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] 71 | struct MyBorrowedData<'a> { 72 | name: &'a str, 73 | age: u8, 74 | } 75 | 76 | let data = MyBorrowedData { name: "Holla", age: 21 }; 77 | let mut output: Vec = serde_brief::to_heapless_vec(&data).unwrap(); 78 | 79 | assert_eq!(output, [ 80 | 17, 81 | 11, 4, b'n', b'a', b'm', b'e', 11, 5, b'H', b'o', b'l', b'l', b'a', 82 | 11, 3, b'a', b'g', b'e', 3, 21, 83 | 18 84 | ]); 85 | 86 | let parsed: MyBorrowedData = serde_brief::from_slice(&output).unwrap(); 87 | assert_eq!(parsed, data); 88 | ``` 89 | 90 | ## Benchmarks 91 | 92 | For benchmark results, [see here](https://github.com/djkoloski/rust_serialization_benchmark). 93 | 94 | I expect there to be plenty room for performance improvements still. 95 | 96 | If you are interested in maximum performance, please try using profile-guided optimization. It measurably improves performance on this library. For further information, see the [PGO usage docs](./docs/pgo.md). 97 | 98 | ### Results 99 | 100 | The serialization/deserialization is reasonably fast. Between postcard and serde_json mostly. The data-size is also between postcard and JSON. 101 | 102 | I expect there is a lot improvements possible, we are still way slower than postcard sadly. 103 | 104 | ## Development & Testing 105 | 106 | 1. Install [cargo-make](https://github.com/sagiegurari/cargo-make) (and optionally [cargo-nextest](https://github.com/nextest-rs/nextest)): `cargo install cargo-make cargo-nextest`. 107 | 2. Optional, but recommended: Put `search_project_root = true` into cargo-make's user configuration, so that `cargo make` can be run from sub-folders. 108 | 3. From the project directory, you can run the following tasks: 109 | - **Format code**: `cargo make format` 110 | - **Check formatting**: `cargo make formatting` 111 | - **Run all tests via cargo test**: `cargo make test` 112 | - **Run all tests via cargo nextest**: `cargo make nextest` 113 | - **Run clippy for all feature sets, failing on any warnings**: `cargo make clippy` 114 | - **Do all checks that are done in CI**: `cargo make ci` 115 | 116 | ## Minimum supported Rust version 117 | 118 | Currently, I am always using the latest stable Rust version and do not put in effort to keep the MSRV. Please open an issue in case you need a different policy, I might consider changing the policy. 119 | 120 | ## License 121 | 122 | Licensed under the MIT license. All contributors agree to license under this license. 123 | -------------------------------------------------------------------------------- /docs/format-specification.md: -------------------------------------------------------------------------------- 1 | # Serde-Brief Binary Format 2 | 3 | The format is close to JSON, modified to be better, binary and fit to [serde's data model](https://serde.rs/data-model.html). 4 | 5 | ## Stability 6 | 7 | The format is not considered stable as of yet. 8 | 9 | ## Self-Describing Format 10 | 11 | The format includes information on the structure of the data. 12 | 13 | **Advantages** over non-self-describing formats: 14 | 15 | - There is no need for a schema to parse any given data. 16 | - Easy to provide backwards/forwards compatibility of data formats, as it is possible to add new fields. 17 | - Type compatibility can be checked. 18 | 19 | **Disadvantages** over non-self-describing formats: 20 | 21 | - Larger binary representation. 22 | - Additional parsing / overhead. 23 | 24 | ## Defined Types 25 | 26 | Every value in Serde-Brief is prepended with a byte detailing its type. 27 | The Serde-Brief format currently contains these types: 28 | 29 | | Type | Description | Byte value | 30 | | --- | --- | --- | 31 | | Null | No value. | 0 | 32 | | BooleanFalse | Boolean with value `false`. | 1 | 33 | | BooleanTrue | Boolean with value `true`. | 2 | 34 | | UnsignedInt | Unsigned integer. The following bytes are the value in "VarInt" encoding (see below). | 3 | 35 | | SignedInt | Signed integer. The following bytes are the value in "VarInt" encoding (see below). | 4 | 36 | | Float16 | Float with 16-bit precision (not yet used/supported). | 5 | 37 | | Float32 | Float with 32-bit precision. The next 4 bytes are the value (little-endian). | 6 | 38 | | Float64 | Float with 64-bit precision. The next 8 bytes are the value (little-endian). | 7 | 39 | | Float128 | Float with 128-bit precision (not yet used/supported). | 8 | 40 | | Bytes | Raw bytes. The following bytes are the length of the byte sequence (must fit into `usize`). After that come the raw bytes of the given length. | 10 | 41 | | String | UTF-8 string. The following bytes are the length of the byte sequence (must fit into `usize`). After that come the string's raw bytes of the given length. | 11 | 42 | | SeqStart | A sequence of any number of values of any type. There is no specified length. The following bytes are the sequence's values. The end of the sequence is recognized by the SeqEnd type. | 15 | 43 | | SeqEnd | The end of a sequence. | 16 | 44 | | MapStart | A map of any number of key-value pairs of any types. There is no specified length. The following bytes are the map's keys and values. The end of the sequence is recognized by the SeqEnd type. | 17 | 45 | | MapEnd | The end of a map. | 18 | 46 | 47 | ### Examples 48 | 49 | - `[0]`: null value 50 | - `[1]`: `false` 51 | - `[2]`: `true` 52 | - `[3, 0]`: `0` 53 | - `[4, 1]`: `-1` 54 | - `[10, 0]`: byte sequence of length 0 55 | - `[10, 1, 5]`: byte sequence of length 1 containing a byte with value `5` 56 | - `[15, 16]`: empty sequence 57 | - `[15, 0, 1, 16]`: sequence with 2 values: `null` and `false` 58 | - `[17, 18]`: empty map 59 | - `[17, 3, 0, 2, 18]`: map with 1 key-value pair: `0 -> true` 60 | 61 | ## VarInt Encoding 62 | 63 | All integers are encoded in this format. It allows to use the same format for all integer numbers, regardless of size. It also saves space for small integers. The format is identical to [postcard's VarInt encoding](https://postcard.jamesmunns.com/wire-format#varint-encoded-integers). Also see [Wikipedia's article on VLQ](https://en.wikipedia.org/wiki/Variable-length_quantity). 64 | 65 | For every byte, the most significant bit determines whether this is the last byte of the number. For example, `0x83`/`0b1000_0011` will result in another byte being read for the current number. `0x73`/`0b0111_0011` will be considered the last byte. 66 | Every byte's lower 7 bits are used to store the actual value. 67 | 68 | Unsigned integers are encoded least-significant-bits first. For example, `0x017F`/`0b0000_0001_0111_1111` will be encoded like this: `0xFF`/`0b1111_1111`, `0x02`/`0b0000_0010`. 69 | *Further explanation*: The least significant 7 bits are `111_1111`. Since we need another byte to store the number's rest of the bits, the 8th bit will be `1`, too. Therefore, out first bit is `0xFF`. The next 7 bytes of our number are `000_0010`. We don't need any more bytes after this one, as the value needs less than 14 bits, therefore the 8th bit is `0`. The encoded byte is `0x02`. 70 | 71 | Signed integers would blow up this encoding, since `-1` is `0xFFFF_FFFF_FFFF_FFFF` in two's-complement in a `u64`. Therefore, signed integers are [ZigZag](https://en.wikipedia.org/wiki/Variable-length_quantity)-encoded first. The sign ends up in the lowest bit in the first byte. `-1` would be encoded as `0b0000_0001`. `1` is encoded as `0b0000_0010`. 72 | 73 | ### Maximum Length 74 | 75 | There is no length limit on the number's encoding in the format itself. In practice however, `serde` supports up to 128 bits and the deserialization will fail on any numbers larger than the expected type. So reading a `u8` will fail when there is more than 2 bytes or more than 8 value-bits. A 128 bit value will never exceed 19 bytes. 76 | Other parsers would, in theory, be allowed to encode arbitrarily large numbers in any amount of bytes. 77 | 78 | ### Canonicalization 79 | 80 | The encoding allows values to pad numbers with any number of 0s, e.g. a chain of `0x80` bytes. The number `0` could be represented as `0x80`, `0x80`, `0x80`, `0x00`. Four bytes, despite being value `0`. The serializer will always output numbers with the lowest number of bytes. However, the deserializer will accept representations with additional padding up to the maximum number of bits of the expected type. 81 | 82 | ### Architecture-Specific Sizes 83 | 84 | The `isize` and `usize` types are as wide as pointers on the specific system. This means, the maximum/minimum number can differ across systems. The VarInt encoding works the same way, so different systems can communicate without any issues, as long as the value fits into the *smallest* of the system's architecture. Parsing will fail on the smaller architecture otherwise. 85 | 86 | ## Sequences and Maps 87 | 88 | Sequences/arrays and maps do not specify their length, so any number of values can follow. Their end is denoted by a value of a special end type. 89 | 90 | Values can have any type, so even maps can consist of arbitrarily complex keys and values. The key itself could be a structure 2 layers deep. The type of every value can differ. 91 | 92 | ## Mapping of Rust Types to Encoded Data 93 | 94 | The encoding/serialization and decoding/deserialization happens via `serde`, so it follows the [serde data model](https://serde.rs/data-model.html). Please familiarize yourself with its concept to fully understand the following. In any case, the following describes how Rust types are mapped to Serde-Brief format types. 95 | 96 | There are two modes of the format. The first and default encodes structs as maps with keys being strings of the fields' names. The second encodes structs as maps with keys being unsigned integers, where the value denotes the index/position in the struct. Similarly, the same happens for enums. Variants are encoded either as string or as unsigned integer denoting their index (NOT discriminant). 97 | 98 | Note that (at least currently) the deserializer can parse data regardless of which encoding was used, unless it relies on features that do not work with index representation mode (e.g. internally tagged enums). The serializer however needs to know which format it needs to serialize to. 99 | 100 | **Advantages of the default (string representation)**: 101 | 102 | - Compatibility and robustness: adding or re-ordering fields works without issues. 103 | - Support of `#[serde(rename)]`, internally tagged enums and any other serde feature. The index representation does NOT support renaming fields. It also cannot deserialize internally tagged enums. This is due to the way `serde` handles internally tagged enums. Externally or adjacently tagged enums DO work, as well as untagged enums. Please note however, that untagged enum variants can more easily be differentiated with named fields. 104 | - External parties can understand the data more easily with named fields. 105 | 106 | **Advantages of the index representation**: 107 | 108 | - Smaller footprint: strings need more space in the encoding. 109 | 110 | ### Serde Datatypes in Serde-Brief (String Representation) 111 | 112 | The list of serde's types can be found [here](https://serde.rs/data-model.html), along with how Rust types are mapped to serde's types. 113 | 114 | | Serde Type | Brief Type | Description | 115 | | --- | --- | --- | 116 | | bool | BooleanFalse or BooleanTrue | Value is saved within the type. No additional value. | 117 | | u8, u16, u32, u64, u128 | UnsignedInt | VarInt encoded. | 118 | | i8, i16, i32, i64, i128 | SignedInt | ZigZag encoded and then VarInt encoded. | 119 | | f32 | Float32 | 4 bytes containing the raw value (little-endian). | 120 | | f64 | Float64 | 8 bytes containing the raw value (little-endian). | 121 | | char | String | UTF-8 encoded and serialized as string. | 122 | | string | String | First, the length (bytes, not chars) in VarInt encoding is given (unsigned). Then the raw bytes follow. The bytes must be a UTF-8 encoded string. | 123 | | byte array | Bytes | First, the length in VarInt encoding is given (unsigned). Then the raw bytes follow. | 124 | | sequence | SeqStart .. SeqEnd | SeqStart is the type for starting a sequence. Any number of values follow. A SeqEnd at the correct position will end the sequence. | 125 | | map | MapStart .. MapEnd | MapStart is the type for starting a map. Any number of key-value pairs follow. The keys and values are not separated, they are differentiated by position. A MapEnd at the correct position will end the map. | 126 | | option | Null or any other type. | `None` becomes the `Null` type. Any other value is directly encoded as its type. Note that `Option<()>` will always be `Null` and decoded as `None`. | 127 | | tuple | SeqStart .. SeqEnd | Encoded as sequence. Information that the length is fixed is unused and not saved. | 128 | | unit | Null | Always `Null`. | 129 | | unit struct | Null | Struct names are not used. There is no value, similar to the unit type. | 130 | | newtype struct | Any | Structs names are not used. Newtype structs (only one field) are encoded as their inner value (transparent encoding). | 131 | | tuple struct | SeqStart .. SeqEnd | Struct names are not used. Therefore encoded just as a tuple (so as a sequence). | 132 | | struct | MapStart .. MapEnd | Struct names are not used. Encoded as a map with keys being the field names and values being their encoded values. | 133 | | unit variant | String | Enum names are not used. Variants without data are just the variant name as string. | 134 | | newtype variant | MapStart, String, Any, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant name as string. The value is the encoded value. | 135 | | tuple variant | MapStart, String, SeqStart .. SeqEnd, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant name as string. The value is a sequence of the encoded values. | 136 | | struct variant | MapStart, String, MapStart .. MapEnd, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant name as string. The value is a map of the field names to their values. | 137 | 138 | ### Serde Datatypes in Serde-Brief (Index Representation) 139 | 140 | The list of serde's types can be found [here](https://serde.rs/data-model.html), along with how Rust types are mapped to serde's types. 141 | 142 | The index representation does not work with internally tagged enums (`#[serde(tag = "t")]`). Externally or adjacently tagged enums do work (nothing or `#[serde(tag = "type", content = "c")]`). 143 | 144 | | Serde Type | Brief Type | Description | 145 | | --- | --- | --- | 146 | | bool | BooleanFalse or BooleanTrue | Value is saved within the type. No additional value. | 147 | | u8, u16, u32, u64, u128 | UnsignedInt | VarInt encoded. | 148 | | i8, i16, i32, i64, i128 | SignedInt | ZigZag encoded and then VarInt encoded. | 149 | | f32 | Float32 | 4 bytes containing the raw value (little-endian). | 150 | | f64 | Float64 | 8 bytes containing the raw value (little-endian). | 151 | | char | String | UTF-8 encoded and serialized as string. | 152 | | string | String | First, the length (bytes, not chars) in VarInt encoding is given (unsigned). Then the raw bytes follow. The bytes must be a UTF-8 encoded string. | 153 | | byte array | Bytes | First, the length in VarInt encoding is given (unsigned). Then the raw bytes follow. | 154 | | sequence | SeqStart .. SeqEnd | SeqStart is the type for starting a sequence. Any number of values follow. A SeqEnd at the correct position will end the sequence. | 155 | | map | MapStart .. MapEnd | MapStart is the type for starting a map. Any number of key-value pairs follow. The keys and values are not separated, they are differentiated by position. A MapEnd at the correct position will end the map. | 156 | | option | Null or any other type. | `None` becomes the `Null` type. Any other value is directly encoded as its type. Note that `Option<()>` will always be `Null` and decoded as `None`. | 157 | | tuple | SeqStart .. SeqEnd | Encoded as sequence. Information that the length is fixed is unused and not saved. | 158 | | unit | Null | Always `Null`. | 159 | | unit struct | Null | Struct names are not used. There is no value, similar to the unit type. | 160 | | newtype struct | Any | Structs names are not used. Newtype structs (only one field) are encoded as their inner value (transparent encoding). | 161 | | tuple struct | SeqStart .. SeqEnd | Struct names are not used. Therefore encoded just as a tuple (so as a sequence). | 162 | | struct | MapStart .. MapEnd | Struct names are not used. Encoded as a map with keys being the field indices (`u32`) and values being their encoded values. | 163 | | unit variant | UnsignedInt | Enum names are not used. Variants without data are just the variant index as unsigned integer (`u32`). | 164 | | newtype variant | MapStart, UnsignedInt, Any, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant index as unsigned integer (`u32`). The value is the encoded value. | 165 | | tuple variant | MapStart, UnsignedInt, SeqStart .. SeqEnd, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant index as unsigned integer (`u32`). The value is a sequence of the encoded values. | 166 | | struct variant | MapStart, UnsignedInt, MapStart .. MapEnd, MapEnd | Enum names are not used. Variants with values are a map with a single key-value pair. The key is the variant index as unsigned integer (`u32`). The value is a map of the field indices (`u32`) to their values. | 167 | -------------------------------------------------------------------------------- /docs/pgo.md: -------------------------------------------------------------------------------- 1 | # [Profile-Guided Optimization (PGO)](https://en.wikipedia.org/wiki/Profile-guided_optimization) 2 | 3 | PGO is a technique to automatically improve a program's runtime performance based on profiling. 4 | 5 | PGO should be applied to the final executable, it cannot be applied to the Rust crate, nor is a pre-compiled static library provided. If you want to benefit from PGO for maximum performance, you will need to follow the steps below. 6 | 7 | There is various ways to do this, but in general, it follows these steps: 8 | 9 | 1. Create a set of realistic data/examples/benchmarks. PGO needs to be primed with profiling, using unrealistic data can degrade performance in production, even if performance increases in the test cases. 10 | 2. Compile/build an instrumented executable. 11 | 3. Run instrumentation to collect profile data. 12 | 4. Compile/build the final executable given the profile data with PGO enabled optimizations. 13 | 14 | PGO can be time consuming and results vary. Always test the improvements on your use-case to see whether it helps and how much. 15 | 16 | ## Applying PGO 17 | 18 | [Rust provides a guide on the use of PGO](https://doc.rust-lang.org/rustc/profile-guided-optimization.html) and there is a nice tool, [cargo pgo](https://github.com/Kobzol/cargo-pgo), to simplify application of PGO. 19 | 20 | Using this tool, building a PGO-optimized executable is as simple as: 21 | 22 | ```bash 23 | # Build the instrumented executable. 24 | cargo pgo instrument build 25 | # Run the instrumented binary to produce profiles. 26 | cargo pgo instrument run 27 | # Build the optimized executable. 28 | cargo pgo optimize 29 | ``` 30 | 31 | Instrumentation can also be applied to tests and benchmarks. See the [documentation of cargo pgo](https://github.com/Kobzol/cargo-pgo) for further information. 32 | 33 | ## Expected Results 34 | 35 | [Results on benchmarks of this library](https://github.com/FlixCoder/serde-brief/issues/5) have shown very noticable improvements in performance. There is varying speed-ups, but realisticly I would guess it is about 30% improvements, currently (Version 0.1.0). 36 | 37 | ## Further Resources 38 | 39 | - [Various PGO results & resources](https://github.com/zamazan4ik/awesome-pgo) 40 | -------------------------------------------------------------------------------- /examples/bytes.rs: -------------------------------------------------------------------------------- 1 | //! Simple bytes serialization/deserialization example. 2 | #![allow( 3 | clippy::tests_outside_test_module, 4 | clippy::missing_docs_in_private_items, 5 | clippy::unwrap_used, 6 | reason = "Example" 7 | )] 8 | 9 | use serde::{Deserialize, Serialize}; 10 | use serde_bytes::{ByteBuf, Bytes}; 11 | 12 | #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] 13 | struct MyData<'a> { 14 | #[serde(borrow)] 15 | borrowed_bytes: &'a Bytes, 16 | owned_bytes: ByteBuf, 17 | #[serde(with = "serde_bytes")] 18 | byte_vec: Vec, 19 | } 20 | 21 | fn main() { 22 | let data = MyData { 23 | borrowed_bytes: Bytes::new(&[1, 2, 3]), 24 | owned_bytes: ByteBuf::from([1, 2, 3]), 25 | byte_vec: vec![1, 2, 3], 26 | }; 27 | 28 | let mut output = [0; 56]; 29 | let bytes = serde_brief::to_slice(&data, &mut output).unwrap(); 30 | let parsed: MyData = serde_brief::from_slice(bytes).unwrap(); 31 | assert_eq!(parsed, data); 32 | } 33 | 34 | #[test] 35 | fn run() { 36 | main(); 37 | } 38 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | //! Simple serialization/deserialization example. 2 | #![allow( 3 | clippy::tests_outside_test_module, 4 | clippy::missing_docs_in_private_items, 5 | clippy::unwrap_used, 6 | reason = "Example" 7 | )] 8 | 9 | use serde::{Deserialize, Serialize}; 10 | 11 | #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] 12 | struct MyBorrowedData<'a> { 13 | name: &'a str, 14 | age: u8, 15 | } 16 | 17 | fn main() { 18 | let data = MyBorrowedData { name: "Holla", age: 21 }; 19 | let mut output = [0; 22]; 20 | let bytes = serde_brief::to_slice(&data, &mut output).unwrap(); 21 | 22 | assert_eq!( 23 | bytes, 24 | [ 25 | 17, 11, 4, b'n', b'a', b'm', b'e', 11, 5, b'H', b'o', b'l', b'l', b'a', 11, 3, b'a', 26 | b'g', b'e', 3, 21, 18 27 | ] 28 | ); 29 | 30 | let parsed: MyBorrowedData = serde_brief::from_slice(bytes).unwrap(); 31 | assert_eq!(parsed, data); 32 | } 33 | 34 | #[test] 35 | fn run() { 36 | main(); 37 | } 38 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = ["rustfmt", "clippy"] 4 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2024" 2 | style_edition = "2024" 3 | hard_tabs = true 4 | group_imports = "StdExternalCrate" 5 | imports_granularity = "Crate" 6 | max_width = 100 7 | wrap_comments = true 8 | comment_width = 100 9 | doc_comment_code_block_width = 100 10 | format_code_in_doc_comments = true 11 | use_small_heuristics = "Max" 12 | blank_lines_upper_bound = 2 13 | spaces_around_ranges = true 14 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | //! Scratch/buffer implementation for different environments. 2 | #![cfg_attr( 3 | feature = "tracing", 4 | allow(clippy::used_underscore_binding, reason = "Only used in tracing::instrument") 5 | )] 6 | 7 | use crate::{Error, Result}; 8 | 9 | /// Scratch/buffer implementation for different environments. 10 | pub trait Buffer { 11 | /// Clear the buffer. 12 | fn clear(&mut self); 13 | /// Get the written buffer value as a slice. 14 | fn as_slice(&self) -> &[u8]; 15 | /// Push a byte to the buffer. 16 | fn push(&mut self, byte: u8) -> Result<()>; 17 | /// Extend the buffer with the given slice. 18 | fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<()>; 19 | /// Reserve space in the buffer and return a mutable slice to it to be written. 20 | fn reserve_slice(&mut self, len: usize) -> Result<&mut [u8]>; 21 | } 22 | 23 | /// Mostly as a type when no buffer is given, not a real buffer. 24 | impl Buffer for () { 25 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 26 | fn clear(&mut self) {} 27 | 28 | fn as_slice(&self) -> &[u8] { 29 | &[] 30 | } 31 | 32 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 33 | fn push(&mut self, _byte: u8) -> Result<()> { 34 | Err(Error::BufferTooSmall) 35 | } 36 | 37 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, bytes)))] 38 | fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<()> { 39 | if bytes.is_empty() { Ok(()) } else { Err(Error::BufferTooSmall) } 40 | } 41 | 42 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 43 | fn reserve_slice(&mut self, len: usize) -> Result<&mut [u8]> { 44 | if len == 0 { Ok(&mut []) } else { Err(Error::BufferTooSmall) } 45 | } 46 | } 47 | 48 | #[cfg(feature = "alloc")] 49 | impl Buffer for ::alloc::vec::Vec { 50 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 51 | fn clear(&mut self) { 52 | self.clear(); 53 | } 54 | 55 | fn as_slice(&self) -> &[u8] { 56 | self.as_slice() 57 | } 58 | 59 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 60 | fn push(&mut self, byte: u8) -> Result<()> { 61 | self.try_reserve(1).map_err(|_| Error::Allocation)?; 62 | self.push(byte); 63 | Ok(()) 64 | } 65 | 66 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, bytes)))] 67 | fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<()> { 68 | self.try_reserve(bytes.len()).map_err(|_| Error::Allocation)?; 69 | self.extend_from_slice(bytes); 70 | Ok(()) 71 | } 72 | 73 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 74 | fn reserve_slice(&mut self, len: usize) -> Result<&mut [u8]> { 75 | self.try_reserve(len).map_err(|_| Error::Allocation)?; 76 | let prev = self.len(); 77 | self.resize(prev.checked_add(len).ok_or_else(|| Error::UsizeOverflow)?, 0); 78 | Ok(self.as_mut_slice().split_at_mut(prev).1) 79 | } 80 | } 81 | 82 | #[cfg(feature = "heapless")] 83 | impl Buffer for ::heapless::Vec { 84 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 85 | fn clear(&mut self) { 86 | self.clear(); 87 | } 88 | 89 | fn as_slice(&self) -> &[u8] { 90 | self.as_slice() 91 | } 92 | 93 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 94 | fn push(&mut self, byte: u8) -> Result<()> { 95 | self.push(byte).map_err(|_| Error::BufferTooSmall) 96 | } 97 | 98 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, bytes)))] 99 | fn extend_from_slice(&mut self, bytes: &[u8]) -> Result<()> { 100 | self.extend_from_slice(bytes).map_err(|()| Error::BufferTooSmall) 101 | } 102 | 103 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 104 | fn reserve_slice(&mut self, len: usize) -> Result<&mut [u8]> { 105 | let prev = self.len(); 106 | self.resize(prev.checked_add(len).ok_or_else(|| Error::UsizeOverflow)?, 0) 107 | .map_err(|()| Error::BufferTooSmall)?; 108 | Ok(self.as_mut_slice().split_at_mut(prev).1) 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | #![allow( 115 | clippy::unwrap_used, 116 | clippy::expect_used, 117 | clippy::indexing_slicing, 118 | clippy::cast_possible_truncation, 119 | reason = "Tests" 120 | )] 121 | 122 | use super::*; 123 | 124 | fn does_not_panic(mut buffer: B) { 125 | buffer.clear(); 126 | _ = buffer.as_slice(); 127 | _ = buffer.push(0); 128 | _ = buffer.extend_from_slice(&[]); 129 | _ = buffer.extend_from_slice(&[5]); 130 | _ = buffer.extend_from_slice(&[1, 2, 3, 4, 5]); 131 | _ = buffer.reserve_slice(0); 132 | _ = buffer.reserve_slice(1); 133 | _ = buffer.reserve_slice(usize::MAX / 2); 134 | _ = buffer.reserve_slice(usize::MAX); 135 | } 136 | 137 | #[allow(dead_code, reason = "Different feature sets")] 138 | fn basics_work(mut buffer: B) { 139 | buffer.clear(); 140 | let expected: &[u8] = &[]; 141 | assert_eq!(buffer.as_slice(), expected); 142 | buffer.push(1).unwrap(); 143 | assert_eq!(buffer.as_slice(), &[1]); 144 | buffer.push(2).unwrap(); 145 | assert_eq!(buffer.as_slice(), &[1, 2]); 146 | buffer.extend_from_slice(&[3, 4]).unwrap(); 147 | assert_eq!(buffer.as_slice(), &[1, 2, 3, 4]); 148 | buffer.push(5).unwrap(); 149 | assert_eq!(buffer.as_slice(), &[1, 2, 3, 4, 5]); 150 | buffer.clear(); 151 | let expected: &[u8] = &[]; 152 | assert_eq!(buffer.as_slice(), expected); 153 | buffer.extend_from_slice(&[]).unwrap(); 154 | let expected: &[u8] = &[]; 155 | assert_eq!(buffer.as_slice(), expected); 156 | buffer.extend_from_slice(&[1]).unwrap(); 157 | assert_eq!(buffer.as_slice(), &[1]); 158 | buffer.extend_from_slice(&[2, 3, 4, 5]).unwrap(); 159 | assert_eq!(buffer.as_slice(), &[1, 2, 3, 4, 5]); 160 | } 161 | 162 | #[allow(dead_code, reason = "Different feature sets")] 163 | fn reserve_slice_works(mut buffer: B) { 164 | buffer.clear(); 165 | let slice = buffer.reserve_slice(0).unwrap(); 166 | let expected: &mut [u8] = &mut []; 167 | assert_eq!(slice, expected); 168 | let slice = buffer.reserve_slice(10).unwrap(); 169 | assert_eq!(slice.len(), 10); 170 | assert_eq!(slice, &mut [0; 10]); 171 | for (i, target) in slice.iter_mut().enumerate() { 172 | *target = i as u8; 173 | } 174 | assert_eq!(slice, &mut [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 175 | let slice = buffer.as_slice(); 176 | assert_eq!(slice, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); 177 | 178 | let slice = buffer.reserve_slice(1).unwrap(); 179 | slice[0] = 10; 180 | assert_eq!(slice, &[10]); 181 | let slice = buffer.as_slice(); 182 | assert_eq!(slice, &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); 183 | } 184 | 185 | #[test] 186 | fn unit_buffer_behaves() { 187 | does_not_panic(()); 188 | } 189 | 190 | #[cfg(feature = "alloc")] 191 | #[test] 192 | fn vec_buffer_behaves() { 193 | does_not_panic(::alloc::vec::Vec::new()); 194 | basics_work(::alloc::vec::Vec::new()); 195 | reserve_slice_works(::alloc::vec::Vec::new()); 196 | } 197 | 198 | #[cfg(feature = "heapless")] 199 | #[test] 200 | fn heapless_buffer_behaves() { 201 | does_not_panic(::heapless::Vec::<_, 100>::new()); 202 | basics_work(::heapless::Vec::<_, 100>::new()); 203 | reserve_slice_works(::heapless::Vec::<_, 100>::new()); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | //! Configuration for (de-)serialization. 2 | 3 | use ::core::num::NonZeroUsize; 4 | 5 | // TODO: 6 | // - Add max sequence/map size limit. 7 | // - Add max-depth limit. 8 | /// Configuration for (de-)serialization. 9 | #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] 10 | pub struct Config { 11 | /// Whether to use indices instead of strings as keys for struct-fields/enum-variants. 12 | pub use_indices: bool, 13 | /// Whether to return an error if there is excess data in the input. 14 | pub error_on_excess_data: bool, 15 | /// Maximum number of bytes to read or write, in any limit. 16 | pub max_size: Option, 17 | } 18 | 19 | impl Default for Config { 20 | fn default() -> Self { 21 | Self { use_indices: false, error_on_excess_data: true, max_size: None } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/docs/mod.rs: -------------------------------------------------------------------------------- 1 | //! Here lives more detailed documentation and explanations for this crate and its binary format. 2 | //! 3 | //! - [Format Specification](./format/index.html) 4 | //! - [Profile-Guided Optimization](./pgo/index.html) 5 | 6 | pub mod format { 7 | #![doc = include_str!("../../docs/format-specification.md")] 8 | } 9 | 10 | pub mod pgo { 11 | #![doc = include_str!("../../docs/pgo.md")] 12 | } 13 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Crate errors. 2 | 3 | use ::core::fmt::Display; 4 | 5 | use crate::format::Type; 6 | 7 | /// Error when (de-)serializing. 8 | #[derive(Debug)] 9 | pub enum Error { 10 | /// Expected more data but encountered the end of the input. 11 | UnexpectedEnd, 12 | /// Excess data appeared at the end of the input. 13 | ExcessData, 14 | /// Buffer was too small. 15 | BufferTooSmall, 16 | /// Allocation failure. 17 | Allocation, 18 | /// Usize overflow. 19 | UsizeOverflow, 20 | /// Configured size limit reached. 21 | LimitReached, 22 | 23 | /// Invalid data type designator encountered. 24 | InvalidType(u8), 25 | /// VarInt too large for the given expected type. 26 | VarIntTooLarge, 27 | /// Wrong data type encountered (found, expected). 28 | WrongType(Type, &'static [Type]), 29 | /// String is not exactly one character. 30 | NotOneChar, 31 | 32 | /// Formatting error. Happens serializing a `core::fmt::Display` value and could be due to an 33 | /// output writing failure. 34 | Format(::core::fmt::Error), 35 | /// Parsed string is not valid UTF-8. 36 | StringNotUtf8(::core::str::Utf8Error), 37 | /// IO error. 38 | #[cfg(feature = "std")] 39 | Io(::std::io::Error), 40 | 41 | /// **no-std + no-alloc**: Generic error message that can be created by data structures through 42 | /// the `ser::Error` and `de::Error` traits. 43 | Custom, 44 | /// **alloc**: Generic error message that can be created by data structures through the 45 | /// `ser::Error` and `de::Error` traits. 46 | #[cfg(feature = "alloc")] 47 | Message(::alloc::string::String), 48 | } 49 | 50 | impl Display for Error { 51 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 52 | match self { 53 | Error::UnexpectedEnd => { 54 | write!(f, "Expected more data but encountered the end of the input") 55 | } 56 | Error::ExcessData => write!(f, "Excess data appeared at the end of the input"), 57 | Error::BufferTooSmall => write!(f, "Output or scratch buffer was too small"), 58 | Error::Allocation => write!(f, "Allocator failed on allocating more space"), 59 | Error::UsizeOverflow => write!(f, "Tried using more bytes than usize allows for"), 60 | Error::LimitReached => write!(f, "Configured size limit reached"), 61 | 62 | Error::InvalidType(v) => { 63 | write!(f, "Invalid data type designator encountered: {v:#02X}") 64 | } 65 | Error::VarIntTooLarge => write!(f, "VarInt too large for the given expected type"), 66 | Error::WrongType(found, expected) => write!( 67 | f, 68 | "Wrong data type encountered. Found `{found:?}`, but expected one of `{expected:?}`" 69 | ), 70 | Error::NotOneChar => write!(f, "String is not exactly one character"), 71 | 72 | Error::Format(err) => write!(f, "Value formatting error: {err:#}"), 73 | Error::StringNotUtf8(err) => write!(f, "String is not valid UTF-8: {err:#}"), 74 | #[cfg(feature = "std")] 75 | Error::Io(err) => write!(f, "IO error: {err:#}"), 76 | 77 | Error::Custom => write!(f, "Unknown custom error"), 78 | #[cfg(feature = "alloc")] 79 | Error::Message(msg) => write!(f, "Custom error: {msg}"), 80 | } 81 | } 82 | } 83 | 84 | impl ::core::error::Error for Error { 85 | fn source(&self) -> Option<&(dyn ::core::error::Error + 'static)> { 86 | match self { 87 | Error::Format(err) => Some(err), 88 | Error::StringNotUtf8(err) => Some(err), 89 | #[cfg(feature = "std")] 90 | Error::Io(err) => Some(err), 91 | _ => None, 92 | } 93 | } 94 | } 95 | 96 | impl From<::core::fmt::Error> for Error { 97 | #[inline] 98 | fn from(err: ::core::fmt::Error) -> Self { 99 | Self::Format(err) 100 | } 101 | } 102 | 103 | impl From<::core::str::Utf8Error> for Error { 104 | #[inline] 105 | fn from(err: ::core::str::Utf8Error) -> Self { 106 | Self::StringNotUtf8(err) 107 | } 108 | } 109 | 110 | #[cfg(feature = "std")] 111 | impl From<::std::io::Error> for Error { 112 | #[inline] 113 | fn from(err: ::std::io::Error) -> Self { 114 | Self::Io(err) 115 | } 116 | } 117 | 118 | impl ::serde::ser::Error for Error { 119 | #[cfg(not(feature = "alloc"))] 120 | #[inline] 121 | fn custom(_msg: T) -> Self 122 | where 123 | T: Display, 124 | { 125 | Self::Custom 126 | } 127 | 128 | #[cfg(feature = "alloc")] 129 | #[inline] 130 | fn custom(msg: T) -> Self 131 | where 132 | T: Display, 133 | { 134 | Self::Message(::alloc::format!("{msg}")) 135 | } 136 | } 137 | 138 | impl ::serde::de::Error for Error { 139 | #[cfg(not(feature = "alloc"))] 140 | #[inline] 141 | fn custom(_msg: T) -> Self 142 | where 143 | T: Display, 144 | { 145 | Self::Custom 146 | } 147 | 148 | #[cfg(feature = "alloc")] 149 | #[inline] 150 | fn custom(msg: T) -> Self 151 | where 152 | T: Display, 153 | { 154 | Self::Message(::alloc::format!("{msg}")) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/format.rs: -------------------------------------------------------------------------------- 1 | //! Data format internals. 2 | 3 | use crate::{ 4 | Error, Result, 5 | io::{Input, Output}, 6 | }; 7 | 8 | /// The binary type identifier. 9 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 10 | #[repr(u8)] 11 | pub enum Type { 12 | /// The `null` or unit or none type. There is no additional byte value. 13 | Null = 0, 14 | /// The `boolean` type with value false. There is no additional byte value. 15 | BooleanFalse = 1, 16 | /// The `boolean` type with value true. There is no additional byte value. 17 | BooleanTrue = 2, 18 | /// The always-positive `integer` type of any length in variable length encoding. 19 | /// 20 | /// Format: Next bytes are the `VarInt` encoding of the unsigned number. The most-significant 21 | /// bit of each byte says whether there is a next byte. The bytes are in little-endian oder, so 22 | /// the first byte contains the least significant bits. 23 | UnsignedInt = 3, 24 | /// The signed `integer` type. 25 | /// 26 | /// Format: Next bytes are the `VarInt` encoding of the absolute number. The most-significant 27 | /// bit of each byte says whether there is a next byte. The bytes are in little-endian oder, so 28 | /// the first byte contains the least significant bits. The least-significant bit in the first 29 | /// byte determines whether the value is negative or positive (the sign, 1 = negative). 30 | SignedInt = 4, 31 | /// The `float16` type. The next 2 bytes are the value. 32 | Float16 = 5, 33 | /// The `float32` type. The next 4 bytes are the value. 34 | Float32 = 6, 35 | /// The `float64` type. The next 8 bytes are the value. 36 | Float64 = 7, 37 | /// The `float128` type. The next 16 bytes are the value. 38 | Float128 = 8, 39 | /// The `bytes` type of length N. 40 | /// 41 | /// Format: The first bytes are an `UnsignedInt` that encodes the length N. Then N bytes data 42 | /// follow. 43 | Bytes = 10, 44 | /// The `string` type. 45 | /// 46 | /// Format: Same as bytes, but all bytes must be valid UTF-8. 47 | String = 11, 48 | /// The `sequence` type consists of start, data and end. This is the start designator. 49 | /// 50 | /// Format: Any number of elements follow, then the end designator. 51 | SeqStart = 15, 52 | /// The end designator for the `sequence` type. 53 | SeqEnd = 16, 54 | /// The `map` type consists of start, data and end. This is the start designator. 55 | /// 56 | /// Format: Any number of elements follow, consisting of first key, then value. The end 57 | /// designator finishes up the map. 58 | MapStart = 17, 59 | /// The end designator for the `map` type. 60 | MapEnd = 18, 61 | } 62 | 63 | impl From for u8 { 64 | #[inline] 65 | fn from(value: Type) -> Self { 66 | value as u8 67 | } 68 | } 69 | 70 | impl TryFrom for Type { 71 | type Error = crate::Error; 72 | 73 | #[inline] 74 | fn try_from(value: u8) -> Result { 75 | match value { 76 | 0 => Ok(Self::Null), 77 | 1 => Ok(Self::BooleanFalse), 78 | 2 => Ok(Self::BooleanTrue), 79 | 3 => Ok(Self::UnsignedInt), 80 | 4 => Ok(Self::SignedInt), 81 | 5 => Ok(Self::Float16), 82 | 6 => Ok(Self::Float32), 83 | 7 => Ok(Self::Float64), 84 | 8 => Ok(Self::Float128), 85 | 10 => Ok(Self::Bytes), 86 | 11 => Ok(Self::String), 87 | 15 => Ok(Self::SeqStart), 88 | 16 => Ok(Self::SeqEnd), 89 | 17 => Ok(Self::MapStart), 90 | 18 => Ok(Self::MapEnd), 91 | _ => Err(crate::Error::InvalidType(value)), 92 | } 93 | } 94 | } 95 | 96 | /// The variable-length integer encoding implementation. 97 | pub trait VarInt: Sized { 98 | /// Encode the integer into bytes. 99 | fn encode(&self, output: &mut O) -> Result<()>; 100 | /// Decode the integer from bytes. 101 | fn decode<'de, I: Input<'de>>(input: &mut I) -> Result; 102 | 103 | /// The maximum number of bytes needed to represent to var int. 104 | const MAX_BYTES: usize = varint_max::(); 105 | } 106 | 107 | /// Implement [VarInt] encoding for unsigned integers. 108 | macro_rules! impl_var_int_unsigned { 109 | ($($t:ty),*) => { 110 | $( 111 | impl VarInt for $t { 112 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 113 | fn encode(&self, output: &mut O) -> Result<()> { 114 | let mut value = *self; 115 | for _ in 0..varint_max::<$t>() { 116 | let byte = value.to_le_bytes()[0]; 117 | 118 | if value < 0x80 { 119 | output.write_byte(byte)?; 120 | return Ok(()); 121 | } 122 | 123 | output.write_byte(byte | 0x80)?; 124 | value >>= 7; 125 | } 126 | panic!("VarInt needed more than maximum bytes"); 127 | } 128 | 129 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 130 | fn decode<'de, I: Input<'de>>(input: &mut I) -> Result { 131 | let mut value = 0; 132 | let mut bits = <$t>::BITS; 133 | for i in 0..varint_max::<$t>() { 134 | let byte = input.read_byte()?; 135 | 136 | if bits < 8 && ((byte & 0x7F) >> bits) != 0 { 137 | return Err(Error::VarIntTooLarge); 138 | } 139 | bits = bits.saturating_sub(7); 140 | 141 | value |= (<$t>::from(byte & 0x7F)) << (i * 7); 142 | if byte & 0x80 == 0 { 143 | return Ok(value); 144 | } 145 | } 146 | Err(Error::VarIntTooLarge) 147 | } 148 | } 149 | )* 150 | }; 151 | } 152 | impl_var_int_unsigned!(u8, u16, u32, u64, u128, usize); 153 | 154 | /// Implement [VarInt] encoding for signed integers. 155 | macro_rules! impl_var_int_signed { 156 | ($($u:ty => $t:ty),*) => { 157 | $( 158 | impl VarInt for $t { 159 | #[inline] 160 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 161 | #[allow(clippy::cast_sign_loss, reason = "We explicitly want this here")] 162 | fn encode(&self, output: &mut O) -> Result<()> { 163 | let value = if self.is_negative() { 164 | self.rotate_left(1).wrapping_neg() 165 | } else { 166 | self.rotate_left(1) 167 | } as $u; 168 | <$u>::encode(&value, output) 169 | } 170 | 171 | #[inline] 172 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 173 | fn decode<'de, I: Input<'de>>(input: &mut I) -> Result { 174 | #[allow(clippy::cast_possible_wrap, reason = "Wrapping is intended")] 175 | let value = <$u>::decode(input)? as $t; 176 | if (value & 1) != 0 { 177 | Ok(value.wrapping_neg().rotate_right(1)) 178 | } else { 179 | Ok(value.rotate_right(1)) 180 | } 181 | } 182 | } 183 | )* 184 | }; 185 | } 186 | impl_var_int_signed!(u8 => i8, u16 => i16, u32 => i32, u64 => i64, u128 => i128, usize => isize); 187 | 188 | /// Returns the maximum number of bytes required to encode T. 189 | pub const fn varint_max() -> usize { 190 | let bits = ::core::mem::size_of::() * 8; 191 | bits.div_ceil(7) 192 | } 193 | 194 | #[cfg(test)] 195 | mod tests { 196 | #![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, reason = "Tests")] 197 | 198 | use super::*; 199 | 200 | #[test] 201 | fn type_conversion_works() { 202 | let valid_types = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 15, 16, 17, 18]; 203 | for byte in 0 ..= u8::MAX { 204 | match Type::try_from(byte) { 205 | Ok(t) => { 206 | assert!( 207 | valid_types.contains(&byte), 208 | "Type {t:?} should should have been recognized from {byte} here" 209 | ); 210 | assert_eq!(u8::from(t), byte); 211 | } 212 | Err(_) => assert!( 213 | !valid_types.contains(&byte), 214 | "Type should have been recognized from {byte}" 215 | ), 216 | } 217 | } 218 | } 219 | 220 | 221 | #[test] 222 | fn unsigned_varint_encode_works() { 223 | let mut bytes = [0; 1]; 224 | let mut output = bytes.as_mut_slice(); 225 | 0_u8.encode(&mut output).unwrap(); 226 | assert_eq!(bytes, [0]); 227 | let mut output = bytes.as_mut_slice(); 228 | 0x7F_u8.encode(&mut output).unwrap(); 229 | assert_eq!(bytes, [0x7F]); 230 | let mut output = bytes.as_mut_slice(); 231 | let result = 0xFF_u8.encode(&mut output); 232 | assert!(matches!(result, Err(Error::BufferTooSmall))); 233 | 234 | let mut bytes = [0; 10]; 235 | let mut output = bytes.as_mut_slice(); 236 | 0xFF_u8.encode(&mut output).unwrap(); 237 | assert_eq!(&bytes[0 .. 2], &[0xFF, 0x01]); 238 | let mut output = bytes.as_mut_slice(); 239 | 0xFF_usize.encode(&mut output).unwrap(); 240 | assert_eq!(&bytes[0 .. 2], &[0xFF, 0x01]); 241 | 242 | let mut bytes = [0; u32::MAX_BYTES]; 243 | let mut output = bytes.as_mut_slice(); 244 | 64_u32.encode(&mut output).unwrap(); 245 | assert_eq!(&bytes[0 .. 1], &[0x40]); 246 | let mut output = bytes.as_mut_slice(); 247 | 0xFFFF_FFFF_u32.encode(&mut output).unwrap(); 248 | assert_eq!(&bytes, &[0xFF, 0xFF, 0xFF, 0xFF, 0x0F]); 249 | let mut output = bytes.as_mut_slice(); 250 | 0x0196_0713_u32.encode(&mut output).unwrap(); 251 | assert_eq!(&bytes[0 .. 4], &[0x93, 0x8E, 0xD8, 0x0C]); 252 | } 253 | 254 | #[test] 255 | fn unsigned_varint_decode_works() { 256 | let bytes = &[0x00, 0x00]; 257 | let mut input = bytes.as_slice(); 258 | let value = u16::decode(&mut input).unwrap(); 259 | assert_eq!(input.len(), 1); // Only one byte read. 260 | assert_eq!(value, 0); 261 | 262 | let bytes = &[0x80, 0x80, 0x00]; 263 | let value = u16::decode(&mut bytes.as_slice()).unwrap(); 264 | assert_eq!(value, 0); 265 | 266 | let bytes = &[0x80, 0x80, 0x80, 0x00]; 267 | let result = u16::decode(&mut bytes.as_slice()); 268 | assert!(matches!(result, Err(Error::VarIntTooLarge))); 269 | 270 | let bytes = &[0xFF, 0xFF, 0x03]; 271 | let value = u16::decode(&mut bytes.as_slice()).unwrap(); 272 | assert_eq!(value, 0xFFFF); 273 | 274 | let bytes = &[0xFF, 0xFF, 0x07]; 275 | let result = u16::decode(&mut bytes.as_slice()); 276 | assert!(matches!(result, Err(Error::VarIntTooLarge))); 277 | } 278 | 279 | #[test] 280 | fn signed_varint_encode_works() { 281 | let mut bytes = [0; 1]; 282 | let mut output = bytes.as_mut_slice(); 283 | 0_i8.encode(&mut output).unwrap(); 284 | assert_eq!(bytes, [0]); 285 | let mut output = bytes.as_mut_slice(); 286 | (-1_i8).encode(&mut output).unwrap(); 287 | assert_eq!(bytes, [0x01]); 288 | let mut output = bytes.as_mut_slice(); 289 | (1_i8).encode(&mut output).unwrap(); 290 | assert_eq!(bytes, [0x02]); 291 | let mut output = bytes.as_mut_slice(); 292 | let result = (64_i8).encode(&mut output); 293 | assert!(matches!(result, Err(Error::BufferTooSmall))); 294 | 295 | let mut bytes = [0; 10]; 296 | let mut output = bytes.as_mut_slice(); 297 | (64_i8).encode(&mut output).unwrap(); 298 | assert_eq!(&bytes[0 .. 2], &[0x80, 0x01]); 299 | let mut output = bytes.as_mut_slice(); 300 | (-65_i8).encode(&mut output).unwrap(); 301 | assert_eq!(&bytes[0 .. 2], &[0x81, 0x01]); 302 | let mut output = bytes.as_mut_slice(); 303 | (-65_isize).encode(&mut output).unwrap(); 304 | assert_eq!(&bytes[0 .. 2], &[0x81, 0x01]); 305 | 306 | let mut bytes = [0; i32::MAX_BYTES]; 307 | let mut output = bytes.as_mut_slice(); 308 | 0x7FFF_i32.encode(&mut output).unwrap(); 309 | assert_eq!(&bytes[0 .. 3], &[0xFE, 0xFF, 0x03]); 310 | let mut output = bytes.as_mut_slice(); 311 | (-0x8000_i32).encode(&mut output).unwrap(); 312 | assert_eq!(&bytes[0 .. 3], &[0xFF, 0xFF, 0x03]); 313 | } 314 | 315 | #[test] 316 | fn signed_varint_decode_works() { 317 | let bytes = &[0x00, 0x00]; 318 | let mut input = bytes.as_slice(); 319 | let value = i16::decode(&mut input).unwrap(); 320 | assert_eq!(input.len(), 1); // Only one byte read. 321 | assert_eq!(value, 0); 322 | 323 | let bytes = &[0x80, 0x80, 0x00]; 324 | let value = i16::decode(&mut bytes.as_slice()).unwrap(); 325 | assert_eq!(value, 0); 326 | 327 | let bytes = &[0x80, 0x80, 0x80, 0x00]; 328 | let result = i16::decode(&mut bytes.as_slice()); 329 | assert!(matches!(result, Err(Error::VarIntTooLarge))); 330 | 331 | let bytes = &[0x80, 0x01]; 332 | let value = i16::decode(&mut bytes.as_slice()).unwrap(); 333 | assert_eq!(value, 64); 334 | 335 | let bytes = &[0x81, 0x01]; 336 | let value = i16::decode(&mut bytes.as_slice()).unwrap(); 337 | assert_eq!(value, -65); 338 | 339 | let bytes = &[0xFE, 0xFF, 0x03]; 340 | let value = i16::decode(&mut bytes.as_slice()).unwrap(); 341 | assert_eq!(value, 0x7FFF); 342 | 343 | let bytes = &[0xFF, 0xFF, 0x03]; 344 | let value = i16::decode(&mut bytes.as_slice()).unwrap(); 345 | assert_eq!(value, -0x8000); 346 | 347 | let bytes = &[0xFF, 0xFF, 0x07]; 348 | let result = i16::decode(&mut bytes.as_slice()); 349 | assert!(matches!(result, Err(Error::VarIntTooLarge))); 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/io.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of input and output: reading and writing bytes. 2 | 3 | #[cfg(feature = "std")] 4 | use ::std::io::{Read, Write}; 5 | 6 | use crate::{Error, Result, buffer::Buffer}; 7 | 8 | /// Generic interface for reading bytes from somewhere. 9 | pub trait Input<'de> { 10 | /// Peek at the next byte without consuming it. 11 | fn peek_byte(&mut self) -> Result; 12 | /// Read a single byte. 13 | fn read_byte(&mut self) -> Result; 14 | /// Read exactly the required number of bytes to fill the given buffer. 15 | fn read_exact(&mut self, buffer: &mut [u8]) -> Result<()>; 16 | /// Read (exactly) the given number of bytes. When possible, return the borrowed slice of the 17 | /// input. If this is not possible, return `None` instead and write the output to the given 18 | /// buffer. If the buffer does not exist, we are out of luck and need to return an error. 19 | fn read_bytes(&mut self, len: usize, buffer: Option<&mut B>) -> Result> 20 | where 21 | B: Buffer; 22 | /// Skip the given number of bytes. 23 | fn skip_bytes(&mut self, len: usize) -> Result<()>; 24 | } 25 | 26 | impl<'de> Input<'de> for &'de [u8] { 27 | #[inline] 28 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 29 | fn peek_byte(&mut self) -> Result { 30 | self.first().copied().ok_or_else(|| Error::UnexpectedEnd) 31 | } 32 | 33 | #[inline] 34 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 35 | fn read_byte(&mut self) -> Result { 36 | let (byte, remaining) = self.split_first().ok_or_else(|| Error::UnexpectedEnd)?; 37 | *self = remaining; 38 | Ok(*byte) 39 | } 40 | 41 | #[inline] 42 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 43 | fn read_exact(&mut self, buffer: &mut [u8]) -> Result<()> { 44 | let (slice, remaining) = 45 | self.split_at_checked(buffer.len()).ok_or_else(|| Error::UnexpectedEnd)?; 46 | *self = remaining; 47 | buffer.copy_from_slice(slice); 48 | Ok(()) 49 | } 50 | 51 | #[inline] 52 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 53 | fn read_bytes(&mut self, len: usize, _buffer: Option<&mut B>) -> Result> { 54 | let (slice, remaining) = self.split_at_checked(len).ok_or_else(|| Error::UnexpectedEnd)?; 55 | *self = remaining; 56 | Ok(Some(slice)) 57 | } 58 | 59 | #[inline] 60 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 61 | fn skip_bytes(&mut self, len: usize) -> Result<()> { 62 | let (_slice, remaining) = self.split_at_checked(len).ok_or_else(|| Error::UnexpectedEnd)?; 63 | *self = remaining; 64 | Ok(()) 65 | } 66 | } 67 | 68 | #[cfg(feature = "std")] 69 | impl<'de, R> Input<'de> for IoReader 70 | where 71 | R: Read, 72 | { 73 | #[inline] 74 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 75 | fn peek_byte(&mut self) -> Result { 76 | let byte = Input::read_byte(self)?; 77 | self.next_byte = Some(byte); 78 | Ok(byte) 79 | } 80 | 81 | #[inline] 82 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 83 | fn read_byte(&mut self) -> Result { 84 | if let Some(byte) = self.next_byte.take() { 85 | Ok(byte) 86 | } else { 87 | let mut bytes = self.reader.by_ref().bytes(); 88 | let byte = bytes.next().ok_or_else(|| Error::UnexpectedEnd)??; 89 | Ok(byte) 90 | } 91 | } 92 | 93 | #[inline] 94 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 95 | fn read_exact(&mut self, mut buffer: &mut [u8]) -> Result<()> { 96 | if buffer.is_empty() { 97 | return Ok(()); 98 | } 99 | 100 | if let Some(byte) = self.next_byte.take() { 101 | let (first, remaining) = 102 | buffer.split_first_mut().ok_or_else(|| Error::BufferTooSmall)?; 103 | *first = byte; 104 | buffer = remaining; 105 | } 106 | 107 | match self.reader.read_exact(buffer) { 108 | Err(err) if err.kind() == ::std::io::ErrorKind::UnexpectedEof => { 109 | return Err(Error::UnexpectedEnd); 110 | } 111 | res => res?, 112 | } 113 | Ok(()) 114 | } 115 | 116 | #[inline] 117 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 118 | fn read_bytes(&mut self, mut len: usize, buffer: Option<&mut B>) -> Result> 119 | where 120 | B: Buffer, 121 | { 122 | if len == 0 { 123 | return Ok(Some(&[])); 124 | } 125 | 126 | let buffer = buffer.ok_or_else(|| Error::BufferTooSmall)?; 127 | if let Some(byte) = self.next_byte.take() { 128 | buffer.push(byte)?; 129 | len -= 1; 130 | } 131 | 132 | let write = buffer.reserve_slice(len)?; 133 | match self.reader.read_exact(write) { 134 | Err(err) if err.kind() == ::std::io::ErrorKind::UnexpectedEof => { 135 | return Err(Error::UnexpectedEnd); 136 | } 137 | res => res?, 138 | } 139 | Ok(None) 140 | } 141 | 142 | #[inline] 143 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 144 | fn skip_bytes(&mut self, mut len: usize) -> Result<()> { 145 | if len == 0 { 146 | return Ok(()); 147 | } 148 | 149 | if self.next_byte.take().is_some() { 150 | len -= 1; 151 | } 152 | 153 | #[expect(clippy::expect_used, reason = "Fundamental architecture assumption")] 154 | let to_write = u64::try_from(len).expect("usize is smaller or equal to u64"); 155 | let mut skip = self.reader.by_ref().take(to_write); 156 | let result = ::std::io::copy(&mut skip, &mut ::std::io::sink()); 157 | match result { 158 | Err(err) if err.kind() == ::std::io::ErrorKind::UnexpectedEof => { 159 | return Err(Error::UnexpectedEnd); 160 | } 161 | Ok(bytes) if bytes != to_write => return Err(Error::UnexpectedEnd), 162 | res => { 163 | res?; 164 | } 165 | } 166 | Ok(()) 167 | } 168 | } 169 | 170 | 171 | /// Generic interface for writing bytes to somewhere. 172 | pub trait Output { 173 | /// Write a single byte. 174 | fn write_byte(&mut self, byte: u8) -> Result<()>; 175 | /// Write all bytes from the buffer. 176 | fn write_all(&mut self, bytes: &[u8]) -> Result<()>; 177 | } 178 | 179 | impl Output for &mut [u8] { 180 | #[inline] 181 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(byte)))] 182 | fn write_byte(&mut self, byte: u8) -> Result<()> { 183 | // This somehow makes more optimized code gen. 184 | if self.is_empty() { 185 | return Err(Error::BufferTooSmall); 186 | } 187 | 188 | let (write, remaining) = 189 | ::core::mem::take(self).split_first_mut().ok_or_else(|| Error::BufferTooSmall)?; 190 | *write = byte; 191 | *self = remaining; 192 | 193 | Ok(()) 194 | } 195 | 196 | #[inline] 197 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 198 | fn write_all(&mut self, bytes: &[u8]) -> Result<()> { 199 | // This somehow makes more optimized code gen. 200 | if self.is_empty() { 201 | return Err(Error::BufferTooSmall); 202 | } 203 | 204 | let (write, remaining) = ::core::mem::take(self) 205 | .split_at_mut_checked(bytes.len()) 206 | .ok_or_else(|| Error::BufferTooSmall)?; 207 | write.copy_from_slice(bytes); 208 | *self = remaining; 209 | 210 | Ok(()) 211 | } 212 | } 213 | 214 | #[cfg(feature = "alloc")] 215 | impl Output for ::alloc::vec::Vec { 216 | #[inline] 217 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(byte)))] 218 | fn write_byte(&mut self, byte: u8) -> Result<()> { 219 | self.push(byte); 220 | Ok(()) 221 | } 222 | 223 | #[inline] 224 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 225 | fn write_all(&mut self, bytes: &[u8]) -> Result<()> { 226 | self.extend_from_slice(bytes); 227 | Ok(()) 228 | } 229 | } 230 | 231 | #[cfg(feature = "heapless")] 232 | impl Output for ::heapless::Vec { 233 | #[inline] 234 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(byte)))] 235 | fn write_byte(&mut self, byte: u8) -> Result<()> { 236 | self.push(byte).map_err(|_| Error::BufferTooSmall) 237 | } 238 | 239 | #[inline] 240 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 241 | fn write_all(&mut self, bytes: &[u8]) -> Result<()> { 242 | self.extend_from_slice(bytes).map_err(|()| Error::BufferTooSmall) 243 | } 244 | } 245 | 246 | // Note: this is also implementing for `std::vec::Vec`. 247 | #[cfg(feature = "std")] 248 | impl Output for IoWriter 249 | where 250 | W: Write, 251 | { 252 | #[inline] 253 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(byte)))] 254 | fn write_byte(&mut self, byte: u8) -> Result<()> { 255 | self.writer.write_all(&[byte])?; 256 | Ok(()) 257 | } 258 | 259 | #[inline] 260 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 261 | fn write_all(&mut self, bytes: &[u8]) -> Result<()> { 262 | self.writer.write_all(bytes)?; 263 | Ok(()) 264 | } 265 | } 266 | 267 | 268 | /// Wrapper for generic reader types as [Output]. 269 | #[allow(dead_code, reason = "Different feature sets")] 270 | #[derive(Debug)] 271 | pub struct IoReader { 272 | /// The inner reader. 273 | reader: R, 274 | /// Next peeked byte if available. 275 | next_byte: Option, 276 | } 277 | 278 | #[allow(dead_code, reason = "Different feature sets")] 279 | impl IoReader { 280 | /// Create a new reader from the given reader. 281 | #[must_use] 282 | pub const fn new(reader: R) -> Self { 283 | Self { reader, next_byte: None } 284 | } 285 | } 286 | 287 | /// Wrapper for generic writer types as [Output]. 288 | #[allow(dead_code, reason = "Different feature sets")] 289 | #[derive(Debug)] 290 | pub struct IoWriter { 291 | /// The inner writer. 292 | writer: W, 293 | } 294 | 295 | #[allow(dead_code, reason = "Different feature sets")] 296 | impl IoWriter { 297 | /// Create a new writer from the given writer. 298 | #[must_use] 299 | pub const fn new(writer: W) -> Self { 300 | Self { writer } 301 | } 302 | } 303 | 304 | /// [Input]/[Output] wrapper that limits the number of bytes being read/written. 305 | pub struct SizeLimit { 306 | /// The inner input/output. 307 | inner: IO, 308 | /// The remaining number of bytes that can be read/written. 309 | limit: usize, 310 | } 311 | 312 | impl SizeLimit { 313 | /// Create a new size limit from the given input/output and limit. 314 | #[must_use] 315 | pub const fn new(inner: IO, limit: usize) -> Self { 316 | Self { inner, limit } 317 | } 318 | 319 | /// Consume the size limit and return the inner input/output. 320 | #[must_use] 321 | pub fn into_inner(self) -> IO { 322 | self.inner 323 | } 324 | } 325 | 326 | impl<'de, I> Input<'de> for SizeLimit 327 | where 328 | I: Input<'de>, 329 | { 330 | #[inline] 331 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 332 | fn peek_byte(&mut self) -> Result { 333 | if self.limit == 0 { 334 | return Err(Error::LimitReached); 335 | } 336 | 337 | self.inner.peek_byte() 338 | } 339 | 340 | #[inline] 341 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 342 | fn read_byte(&mut self) -> Result { 343 | if self.limit == 0 { 344 | return Err(Error::LimitReached); 345 | } 346 | self.limit -= 1; 347 | 348 | self.inner.read_byte() 349 | } 350 | 351 | #[inline] 352 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 353 | fn read_exact(&mut self, buffer: &mut [u8]) -> Result<()> { 354 | if self.limit < buffer.len() { 355 | return Err(Error::LimitReached); 356 | } 357 | self.limit -= buffer.len(); 358 | 359 | self.inner.read_exact(buffer) 360 | } 361 | 362 | #[inline] 363 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 364 | fn read_bytes(&mut self, len: usize, buffer: Option<&mut B>) -> Result> 365 | where 366 | B: Buffer, 367 | { 368 | if self.limit < len { 369 | return Err(Error::LimitReached); 370 | } 371 | self.limit -= len; 372 | 373 | self.inner.read_bytes(len, buffer) 374 | } 375 | 376 | #[inline] 377 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(len)))] 378 | fn skip_bytes(&mut self, len: usize) -> Result<()> { 379 | if self.limit < len { 380 | return Err(Error::LimitReached); 381 | } 382 | self.limit -= len; 383 | 384 | self.inner.skip_bytes(len) 385 | } 386 | } 387 | 388 | impl Output for SizeLimit 389 | where 390 | O: Output, 391 | { 392 | #[inline] 393 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(byte)))] 394 | fn write_byte(&mut self, byte: u8) -> Result<()> { 395 | if self.limit == 0 { 396 | return Err(Error::LimitReached); 397 | } 398 | self.limit -= 1; 399 | 400 | self.inner.write_byte(byte) 401 | } 402 | 403 | #[inline] 404 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 405 | fn write_all(&mut self, bytes: &[u8]) -> Result<()> { 406 | if self.limit < bytes.len() { 407 | return Err(Error::LimitReached); 408 | } 409 | self.limit -= bytes.len(); 410 | 411 | self.inner.write_all(bytes) 412 | } 413 | } 414 | 415 | #[cfg(test)] 416 | mod tests { 417 | #![allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing, reason = "Tests")] 418 | 419 | use super::*; 420 | 421 | const PANIC_INPUT_DATA: &[u8] = &[0]; 422 | fn input_does_not_panic<'de, I: Input<'de>>(mut input: I) { 423 | _ = input.peek_byte(); 424 | _ = input.read_byte(); 425 | _ = input.read_exact(&mut [0, 1, 2, 3, 4]); 426 | _ = input.read_bytes::<()>(10, None); 427 | _ = input.read_bytes(10, Some(&mut ())); 428 | _ = input.read_bytes::<()>(usize::MAX / 2, None); 429 | _ = input.read_bytes::<()>(usize::MAX, None); 430 | _ = input.skip_bytes(10); 431 | _ = input.skip_bytes(usize::MAX / 2); 432 | _ = input.skip_bytes(usize::MAX); 433 | } 434 | 435 | const BASIC_INPUT_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 436 | fn basic_input_works<'de, I: Input<'de>>(mut input: I) { 437 | let byte = input.peek_byte().unwrap(); 438 | assert_eq!(byte, 0); 439 | let _byte = input.peek_byte().unwrap(); 440 | let byte = input.peek_byte().unwrap(); 441 | assert_eq!(byte, 0); 442 | 443 | let byte = input.read_byte().unwrap(); 444 | assert_eq!(byte, 0); 445 | let byte = input.peek_byte().unwrap(); 446 | assert_eq!(byte, 1); 447 | let _byte = input.read_byte().unwrap(); 448 | let byte = input.read_byte().unwrap(); 449 | assert_eq!(byte, 2); 450 | 451 | let mut target = [0; 0]; 452 | input.read_exact(&mut target).unwrap(); 453 | let byte = input.peek_byte().unwrap(); 454 | assert_eq!(byte, 3); 455 | let mut target = [0; 2]; 456 | input.read_exact(&mut target).unwrap(); 457 | assert_eq!(target, [3, 4]); 458 | let byte = input.peek_byte().unwrap(); 459 | assert_eq!(byte, 5); 460 | 461 | input.skip_bytes(0).unwrap(); 462 | let byte = input.peek_byte().unwrap(); 463 | assert_eq!(byte, 5); 464 | input.skip_bytes(1).unwrap(); 465 | input.skip_bytes(2).unwrap(); 466 | let byte = input.peek_byte().unwrap(); 467 | assert_eq!(byte, 8); 468 | 469 | input.skip_bytes(2).unwrap(); 470 | assert!(input.peek_byte().is_err()); 471 | assert!(input.read_byte().is_err()); 472 | assert!(input.read_exact(&mut [0]).is_err()); 473 | assert!(input.skip_bytes(1).is_err()); 474 | } 475 | 476 | const READ_BYTES_INPUT_DATA: &[u8] = &[5; 20]; 477 | fn read_bytes_works<'de, I: Input<'de>, B: Buffer>(mut input: I, mut buffer: Option) { 478 | if let Some(b) = buffer.as_mut() { 479 | b.clear(); 480 | } 481 | 482 | let borrowed = input.read_bytes(10, buffer.as_mut()).unwrap(); 483 | let slice = borrowed.unwrap_or_else(|| buffer.as_ref().map_or(&[], |b| b.as_slice())); 484 | assert_eq!(slice.len(), 10); 485 | assert_eq!(slice, [5; 10].as_slice()); 486 | 487 | if let Some(b) = buffer.as_mut() { 488 | b.clear(); 489 | } 490 | 491 | let borrowed = input.read_bytes(5, buffer.as_mut()).unwrap(); 492 | let slice = borrowed.unwrap_or_else(|| buffer.as_ref().map_or(&[], |b| b.as_slice())); 493 | assert_eq!(slice.len(), 5); 494 | assert_eq!(slice, [5; 5].as_slice()); 495 | 496 | if let Some(b) = buffer.as_mut() { 497 | b.clear(); 498 | } 499 | 500 | assert!(input.read_bytes(10, buffer.as_mut()).is_err()); 501 | } 502 | 503 | #[test] 504 | fn slice_input_behaves() { 505 | input_does_not_panic(PANIC_INPUT_DATA); 506 | basic_input_works(BASIC_INPUT_DATA); 507 | read_bytes_works(READ_BYTES_INPUT_DATA, None::<()>); 508 | 509 | // Read bytes returns borrowed data. 510 | let mut input = READ_BYTES_INPUT_DATA; 511 | let mut buffer = None::<()>; 512 | let borrowed = input.read_bytes(10, buffer.as_mut()).unwrap(); 513 | assert!(borrowed.is_some()); 514 | } 515 | 516 | #[cfg(feature = "std")] 517 | #[test] 518 | fn reader_input_behaves() { 519 | input_does_not_panic(IoReader::new(PANIC_INPUT_DATA)); 520 | basic_input_works(IoReader::new(BASIC_INPUT_DATA)); 521 | read_bytes_works(IoReader::new(READ_BYTES_INPUT_DATA), Some(Vec::new())); 522 | 523 | // Buffer behavior from IO reader. 524 | let mut input = IoReader::new(READ_BYTES_INPUT_DATA); 525 | let mut buffer = Some(Vec::new()); 526 | _ = input.read_bytes(10, buffer.as_mut()).unwrap(); 527 | _ = input.read_bytes(5, buffer.as_mut()).unwrap(); 528 | assert_eq!(buffer.unwrap().len(), 15); 529 | } 530 | 531 | 532 | fn output_does_not_panic(mut output: O) { 533 | _ = output.write_byte(0); 534 | _ = output.write_all(&[]); 535 | _ = output.write_all(&[1]); 536 | _ = output.write_all(&[1, 2, 3, 4, 5]); 537 | } 538 | 539 | const BASIC_OUTPUT_DATA: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 540 | fn basic_output_works(output: &mut O) { 541 | output.write_byte(0).unwrap(); 542 | output.write_byte(1).unwrap(); 543 | output.write_all(&[]).unwrap(); 544 | output.write_all(&[2, 3, 4, 5]).unwrap(); 545 | output.write_byte(6).unwrap(); 546 | output.write_all(&[7, 8, 9]).unwrap(); 547 | } 548 | 549 | #[test] 550 | fn slice_output_behaves() { 551 | output_does_not_panic([1, 2].as_mut_slice()); 552 | let mut buffer = [0; 10]; 553 | let mut output = buffer.as_mut_slice(); 554 | basic_output_works(&mut output); 555 | let expected: &mut [u8] = &mut []; 556 | assert_eq!(output, expected); 557 | assert_eq!(buffer, BASIC_OUTPUT_DATA); 558 | } 559 | 560 | #[cfg(feature = "alloc")] 561 | #[test] 562 | fn vec_output_behaves() { 563 | output_does_not_panic(::alloc::vec::Vec::new()); 564 | let mut output = ::alloc::vec::Vec::new(); 565 | basic_output_works(&mut output); 566 | assert_eq!(&output, BASIC_OUTPUT_DATA); 567 | } 568 | 569 | #[cfg(feature = "heapless")] 570 | #[test] 571 | fn heapless_output_behaves() { 572 | output_does_not_panic(::heapless::Vec::<_, 2>::new()); 573 | let mut output = ::heapless::Vec::<_, 10>::new(); 574 | basic_output_works(&mut output); 575 | assert_eq!(&output, BASIC_OUTPUT_DATA); 576 | } 577 | 578 | #[cfg(feature = "std")] 579 | #[test] 580 | fn writer_output_behaves() { 581 | output_does_not_panic(IoWriter::new(Vec::new())); 582 | let mut output = IoWriter::new(Vec::new()); 583 | basic_output_works(&mut output); 584 | assert_eq!(&output.writer, BASIC_OUTPUT_DATA); 585 | } 586 | } 587 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Serde-Brief 2 | //! 3 | //! Serde-Brief (German for letter) is a crate for encoding and decoding data into a binary format that is self-descriptive and [serde](https://docs.rs/serde/)-compatible. 4 | //! 5 | //! ## Design Goals 6 | //! 7 | //! Not necessarily in order of importance: 8 | //! 9 | //! - Convenient to use for developers: Integrates into the Rust ecosystem via `serde`, supporting 10 | //! all of its features in its derived implementations (e.g. renaming, flattening, ..). 11 | //! - Compatibility: Easy to add or re-order fields/variants without breakage. Detects wrong data 12 | //! types. 13 | //! - `#![no_std]` and std compatible. 14 | //! - Resource efficient: High performance, low memory usage. 15 | //! - Interoperability: Different architectures can communicate flawlessly. 16 | //! 17 | //! ## More Detailed Documentation 18 | //! 19 | //! See more detailed documentation in [the docs module](./docs/index.html). It also contains 20 | //! information on the binary representation format. 21 | //! 22 | //! ## Feature Flags 23 | //! 24 | //! This library is both no-std and std compatible. Additionally, there are some other features to 25 | //! enable additional functionality: 26 | //! 27 | //! | Feature Flag | Default | Description | 28 | //! | --- | --- | --- | 29 | //! | alloc | no | Enables the use of `alloc` types like serialization to a `Vec`. | 30 | //! | heapless | no | Enables serialization to a `heapless::Vec`. | 31 | //! | std | no | Enables the use of `std` types like serialization to a `Write`r and deserialization from a `Read`er. | 32 | //! | tracing | no | Enables tracing instrumentation. | 33 | //! 34 | //! ## Flavors / Modes 35 | //! 36 | //! By default, structs' field names and enums' variant names are encoded as strings. This can be 37 | //! configured to be encoded as unsigned integers of their indices instead. However, this has 38 | //! compatibility implications and some serde features do not work with the index representation. 39 | //! See the format specification for more info. 40 | //! 41 | //! ## Usage 42 | //! 43 | //! Add the library to your project with `cargo add serde-brief`. By default, no features are 44 | //! enabled (currently), so it is no-std by default. You can enable use of `Vec`s and such with 45 | //! features like `alloc` or `std`. 46 | //! 47 | //! ### Example Serialization/Deserialization 48 | //! 49 | //! The `heapless` feature was enabled for this example. It is similarly possible with `std`'s `Vec` 50 | //! or just slices. 51 | //! 52 | //! ```rust 53 | //! use heapless::Vec; 54 | //! use serde::{Deserialize, Serialize}; 55 | //! 56 | //! #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] 57 | //! struct MyBorrowedData<'a> { 58 | //! name: &'a str, 59 | //! age: u8, 60 | //! } 61 | //! 62 | //! let data = MyBorrowedData { name: "Holla", age: 21 }; 63 | //! let mut output: Vec = serde_brief::to_heapless_vec(&data).unwrap(); 64 | //! 65 | //! assert_eq!( 66 | //! output, 67 | //! [ 68 | //! 17, 11, 4, b'n', b'a', b'm', b'e', 11, 5, b'H', b'o', b'l', b'l', b'a', 11, 3, b'a', 69 | //! b'g', b'e', 3, 21, 18 70 | //! ] 71 | //! ); 72 | //! 73 | //! let parsed: MyBorrowedData = serde_brief::from_slice(&output).unwrap(); 74 | //! assert_eq!(parsed, data); 75 | //! ``` 76 | //! 77 | //! ### Bytes Serialization/Deserialization 78 | //! 79 | //! Serde serializes byte arrays, such as `[u8; N]` or `Vec`, as sequences by default (due to 80 | //! missing specialization support in Rust). To serialize these types as proper bytes, making the 81 | //! format way more efficient, you can use `serde_bytes` or your own serde-trait-implementations. 82 | //! 83 | //! Example using `serde_bytes`: 84 | //! 85 | //! ```rust 86 | //! use serde::{Deserialize, Serialize}; 87 | //! use serde_bytes::{ByteBuf, Bytes}; 88 | //! 89 | //! #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] 90 | //! struct MyData<'a> { 91 | //! owned_bytes: ByteBuf, 92 | //! #[serde(borrow)] 93 | //! borrowed_bytes: &'a Bytes, 94 | //! #[serde(with = "serde_bytes")] 95 | //! byte_vec: Vec, 96 | //! } 97 | //! ``` 98 | //! 99 | //! ## Performance 100 | //! 101 | //! If you are interested in maximum performance, please take a look at the [PGO usage 102 | //! documentation](./docs/pgo/index.html). 103 | #![cfg_attr(not(feature = "std"), no_std)] 104 | 105 | #[cfg(feature = "alloc")] 106 | extern crate alloc; 107 | 108 | mod buffer; 109 | mod config; 110 | pub mod de; 111 | pub mod docs; 112 | mod error; 113 | mod format; 114 | mod io; 115 | pub mod ser; 116 | #[cfg(feature = "alloc")] 117 | pub mod value; 118 | 119 | #[allow(unused_imports, reason = "Different feature sets")] 120 | use ::serde::{Deserialize, Serialize, de::DeserializeOwned}; 121 | #[cfg(feature = "std")] 122 | use ::std::io::{Read, Write}; 123 | 124 | #[cfg(feature = "alloc")] 125 | pub use self::value::{from_value, from_value_with_config, to_value, to_value_with_config}; 126 | pub use self::{config::Config, de::Deserializer, error::Error, ser::Serializer}; 127 | 128 | /// `Result` type that uses the `serde-brief` error. 129 | pub type Result = ::core::result::Result; 130 | 131 | /// Serialize a type into a slice of bytes using the given configuration. Returns the slice with the 132 | /// serialized data. 133 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 134 | pub fn to_slice_with_config<'buf, T>( 135 | value: &T, 136 | buffer: &'buf mut [u8], 137 | config: Config, 138 | ) -> Result<&'buf mut [u8]> 139 | where 140 | T: Serialize, 141 | { 142 | let remaining = if let Some(max) = config.max_size { 143 | let mut ser = Serializer::new(io::SizeLimit::new(&mut *buffer, max.into())) 144 | .use_indices(config.use_indices); 145 | value.serialize(&mut ser)?; 146 | ser.into_output().into_inner().len() 147 | } else { 148 | let mut ser = Serializer::new(&mut *buffer).use_indices(config.use_indices); 149 | value.serialize(&mut ser)?; 150 | ser.into_output().len() 151 | }; 152 | 153 | let used = buffer.len() - remaining; 154 | Ok(buffer.split_at_mut(used).0) 155 | } 156 | 157 | /// Serialize a type into a slice of bytes. Returns the slice with the serialized data. 158 | pub fn to_slice<'buf, T>(value: &T, buffer: &'buf mut [u8]) -> Result<&'buf mut [u8]> 159 | where 160 | T: Serialize, 161 | { 162 | to_slice_with_config(value, buffer, Config::default()) 163 | } 164 | 165 | /// Serialize a type into a [Vec] of bytes using the given configuration. 166 | #[cfg(feature = "alloc")] 167 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 168 | pub fn to_vec_with_config(value: &T, config: Config) -> Result<::alloc::vec::Vec> 169 | where 170 | T: Serialize, 171 | { 172 | if let Some(max) = config.max_size { 173 | let mut ser = Serializer::new(io::SizeLimit::new(::alloc::vec::Vec::new(), max.into())) 174 | .use_indices(config.use_indices); 175 | value.serialize(&mut ser)?; 176 | Ok(ser.into_output().into_inner()) 177 | } else { 178 | let mut ser = Serializer::new(::alloc::vec::Vec::new()).use_indices(config.use_indices); 179 | value.serialize(&mut ser)?; 180 | Ok(ser.into_output()) 181 | } 182 | } 183 | 184 | /// Serialize a type into a [Vec] of bytes. 185 | #[cfg(feature = "alloc")] 186 | pub fn to_vec(value: &T) -> Result<::alloc::vec::Vec> 187 | where 188 | T: Serialize, 189 | { 190 | to_vec_with_config(value, Config::default()) 191 | } 192 | 193 | /// Serialize a type into a [`heapless::Vec`] of bytes using the given configuration. 194 | #[cfg(feature = "heapless")] 195 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 196 | pub fn to_heapless_vec_with_config( 197 | value: &T, 198 | config: Config, 199 | ) -> Result<::heapless::Vec> 200 | where 201 | T: Serialize, 202 | { 203 | if let Some(max) = config.max_size { 204 | let mut ser = Serializer::new(io::SizeLimit::new(::heapless::Vec::new(), max.into())) 205 | .use_indices(config.use_indices); 206 | value.serialize(&mut ser)?; 207 | Ok(ser.into_output().into_inner()) 208 | } else { 209 | let mut ser = Serializer::new(::heapless::Vec::new()).use_indices(config.use_indices); 210 | value.serialize(&mut ser)?; 211 | Ok(ser.into_output()) 212 | } 213 | } 214 | 215 | /// Serialize a type into a [`heapless::Vec`] of bytes. 216 | #[cfg(feature = "heapless")] 217 | pub fn to_heapless_vec(value: &T) -> Result<::heapless::Vec> 218 | where 219 | T: Serialize, 220 | { 221 | to_heapless_vec_with_config(value, Config::default()) 222 | } 223 | 224 | /// Serialize a type into a [Write]r using the given configuration. 225 | #[cfg(feature = "std")] 226 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 227 | pub fn to_writer_with_config(value: &T, writer: W, config: Config) -> Result<()> 228 | where 229 | T: Serialize, 230 | W: Write, 231 | { 232 | if let Some(max) = config.max_size { 233 | let mut ser = Serializer::new(io::SizeLimit::new(io::IoWriter::new(writer), max.into())) 234 | .use_indices(config.use_indices); 235 | value.serialize(&mut ser)?; 236 | } else { 237 | let mut ser = Serializer::new(io::IoWriter::new(writer)).use_indices(config.use_indices); 238 | value.serialize(&mut ser)?; 239 | } 240 | Ok(()) 241 | } 242 | 243 | /// Serialize a type into a [Write]r. 244 | #[cfg(feature = "std")] 245 | pub fn to_writer(value: &T, writer: W) -> Result<()> 246 | where 247 | T: Serialize, 248 | W: Write, 249 | { 250 | to_writer_with_config(value, writer, Config::default()) 251 | } 252 | 253 | /// Deserialize a type from a slice of bytes using the given configuration. 254 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 255 | pub fn from_slice_with_config<'de, T>(bytes: &'de [u8], config: Config) -> Result 256 | where 257 | T: Deserialize<'de>, 258 | { 259 | let error_on_excess = config.error_on_excess_data; 260 | 261 | let (value, peek) = if let Some(max) = config.max_size { 262 | // The deserializer can parse both with and without `use_indices`.` 263 | let mut de = Deserializer::new(io::SizeLimit::new(bytes, max.into())); 264 | (T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input())) 265 | } else { 266 | // The deserializer can parse both with and without `use_indices`.` 267 | let mut de = Deserializer::new(bytes); 268 | (T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input())) 269 | }; 270 | 271 | if error_on_excess && peek.is_ok() { 272 | return Err(Error::ExcessData); 273 | } 274 | 275 | Ok(value) 276 | } 277 | 278 | /// Deserialize a type from a slice of bytes. 279 | pub fn from_slice<'de, T>(bytes: &'de [u8]) -> Result 280 | where 281 | T: Deserialize<'de>, 282 | { 283 | from_slice_with_config(bytes, Config::default()) 284 | } 285 | 286 | /// Deserialize a type from a [Read]er using the given configuration. 287 | #[cfg(feature = "std")] 288 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all, fields(config)))] 289 | pub fn from_reader_with_config(reader: R, config: Config) -> Result 290 | where 291 | R: Read, 292 | T: DeserializeOwned, 293 | { 294 | let error_on_excess = config.error_on_excess_data; 295 | 296 | let (value, peek) = if let Some(max) = config.max_size { 297 | // The deserializer can parse both with and without `use_indices`.` 298 | let mut de = Deserializer::new(io::SizeLimit::new(io::IoReader::new(reader), max.into())) 299 | .with_buffer(Vec::new()); 300 | (T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input())) 301 | } else { 302 | // The deserializer can parse both with and without `use_indices`.` 303 | let mut de = Deserializer::new(io::IoReader::new(reader)).with_buffer(Vec::new()); 304 | (T::deserialize(&mut de)?, io::Input::peek_byte(&mut de.into_input())) 305 | }; 306 | 307 | if error_on_excess && peek.is_ok() { 308 | return Err(Error::ExcessData); 309 | } 310 | 311 | Ok(value) 312 | } 313 | 314 | /// Deserialize a type from a [Read]er. 315 | #[cfg(feature = "std")] 316 | pub fn from_reader(reader: R) -> Result 317 | where 318 | R: Read, 319 | T: DeserializeOwned, 320 | { 321 | from_reader_with_config(reader, Config::default()) 322 | } 323 | 324 | #[cfg(test)] 325 | mod tests; 326 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | //! Serialization implementation. 2 | #![cfg_attr( 3 | feature = "tracing", 4 | allow(clippy::used_underscore_binding, reason = "Only used in tracing::instrument") 5 | )] 6 | 7 | use ::serde::Serialize; 8 | 9 | use crate::{ 10 | Config, Error, 11 | format::{Type, VarInt}, 12 | io::Output, 13 | }; 14 | 15 | /// The serializer for the binary format. 16 | #[derive(Debug, Clone, PartialEq, Eq)] 17 | pub struct Serializer { 18 | /// The output to write to. 19 | output: O, 20 | /// Serialize enum variants and struct fields by index instead of name-string. 21 | use_indices: bool, 22 | } 23 | 24 | impl Serializer { 25 | /// Create a new serializer from any [Output] compatible type. 26 | #[must_use] 27 | pub fn new(output: O) -> Self 28 | where 29 | // Same bounds as `serde::Serializer` impl. 30 | O: Output, 31 | { 32 | Self { output, use_indices: Config::default().use_indices } 33 | } 34 | 35 | /// Set whether to use indices instead of names for enum variants and struct fields. 36 | #[must_use] 37 | pub const fn use_indices(mut self, use_indices: bool) -> Self { 38 | self.use_indices = use_indices; 39 | self 40 | } 41 | 42 | /// Consume the serializer to get the output back. 43 | #[inline] 44 | pub fn into_output(self) -> O { 45 | self.output 46 | } 47 | } 48 | 49 | impl<'a, O> ::serde::Serializer for &'a mut Serializer 50 | where 51 | O: Output, 52 | { 53 | type Ok = (); 54 | type Error = Error; 55 | 56 | type SerializeSeq = Self; 57 | type SerializeTuple = Self; 58 | type SerializeTupleStruct = Self; 59 | type SerializeTupleVariant = Self; 60 | type SerializeMap = Self; 61 | type SerializeStruct = StructSerializer<'a, O>; 62 | type SerializeStructVariant = StructSerializer<'a, O>; 63 | 64 | #[inline] 65 | fn is_human_readable(&self) -> bool { 66 | false 67 | } 68 | 69 | #[inline] 70 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 71 | fn serialize_bool(self, v: bool) -> Result { 72 | if v { 73 | self.output.write_byte(Type::BooleanTrue.into())?; 74 | } else { 75 | self.output.write_byte(Type::BooleanFalse.into())?; 76 | } 77 | Ok(()) 78 | } 79 | 80 | #[inline] 81 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 82 | fn serialize_i8(self, v: i8) -> Result { 83 | self.output.write_byte(Type::SignedInt.into())?; 84 | v.encode(&mut self.output)?; 85 | Ok(()) 86 | } 87 | 88 | #[inline] 89 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 90 | fn serialize_i16(self, v: i16) -> Result { 91 | self.output.write_byte(Type::SignedInt.into())?; 92 | v.encode(&mut self.output)?; 93 | Ok(()) 94 | } 95 | 96 | #[inline] 97 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 98 | fn serialize_i32(self, v: i32) -> Result { 99 | self.output.write_byte(Type::SignedInt.into())?; 100 | v.encode(&mut self.output)?; 101 | Ok(()) 102 | } 103 | 104 | #[inline] 105 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 106 | fn serialize_i64(self, v: i64) -> Result { 107 | self.output.write_byte(Type::SignedInt.into())?; 108 | v.encode(&mut self.output)?; 109 | Ok(()) 110 | } 111 | 112 | #[inline] 113 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 114 | fn serialize_i128(self, v: i128) -> Result { 115 | self.output.write_byte(Type::SignedInt.into())?; 116 | v.encode(&mut self.output)?; 117 | Ok(()) 118 | } 119 | 120 | #[inline] 121 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 122 | fn serialize_u8(self, v: u8) -> Result { 123 | self.output.write_byte(Type::UnsignedInt.into())?; 124 | v.encode(&mut self.output)?; 125 | Ok(()) 126 | } 127 | 128 | #[inline] 129 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 130 | fn serialize_u16(self, v: u16) -> Result { 131 | self.output.write_byte(Type::UnsignedInt.into())?; 132 | v.encode(&mut self.output)?; 133 | Ok(()) 134 | } 135 | 136 | #[inline] 137 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 138 | fn serialize_u32(self, v: u32) -> Result { 139 | self.output.write_byte(Type::UnsignedInt.into())?; 140 | v.encode(&mut self.output)?; 141 | Ok(()) 142 | } 143 | 144 | #[inline] 145 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 146 | fn serialize_u64(self, v: u64) -> Result { 147 | self.output.write_byte(Type::UnsignedInt.into())?; 148 | v.encode(&mut self.output)?; 149 | Ok(()) 150 | } 151 | 152 | #[inline] 153 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 154 | fn serialize_u128(self, v: u128) -> Result { 155 | self.output.write_byte(Type::UnsignedInt.into())?; 156 | v.encode(&mut self.output)?; 157 | Ok(()) 158 | } 159 | 160 | #[inline] 161 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 162 | fn serialize_f32(self, v: f32) -> Result { 163 | self.output.write_byte(Type::Float32.into())?; 164 | self.output.write_all(&v.to_le_bytes())?; 165 | Ok(()) 166 | } 167 | 168 | #[inline] 169 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 170 | fn serialize_f64(self, v: f64) -> Result { 171 | self.output.write_byte(Type::Float64.into())?; 172 | self.output.write_all(&v.to_le_bytes())?; 173 | Ok(()) 174 | } 175 | 176 | #[inline] 177 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 178 | fn serialize_char(self, v: char) -> Result { 179 | let mut buffer = [0; 4]; 180 | let s = v.encode_utf8(&mut buffer); 181 | self.serialize_str(s) 182 | } 183 | 184 | #[inline] 185 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 186 | fn serialize_str(self, v: &str) -> Result { 187 | self.output.write_byte(Type::String.into())?; 188 | let bytes = v.as_bytes(); 189 | bytes.len().encode(&mut self.output)?; 190 | self.output.write_all(bytes)?; 191 | Ok(()) 192 | } 193 | 194 | #[inline] 195 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 196 | fn serialize_bytes(self, v: &[u8]) -> Result { 197 | self.output.write_byte(Type::Bytes.into())?; 198 | v.len().encode(&mut self.output)?; 199 | self.output.write_all(v)?; 200 | Ok(()) 201 | } 202 | 203 | #[inline] 204 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 205 | fn serialize_none(self) -> Result { 206 | self.output.write_byte(Type::Null.into())?; 207 | Ok(()) 208 | } 209 | 210 | #[inline] 211 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 212 | fn serialize_some(self, value: &T) -> Result 213 | where 214 | T: ?Sized + serde::Serialize, 215 | { 216 | value.serialize(self) 217 | } 218 | 219 | #[inline] 220 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 221 | fn serialize_unit(self) -> Result { 222 | self.output.write_byte(Type::Null.into())?; 223 | Ok(()) 224 | } 225 | 226 | #[inline] 227 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 228 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 229 | self.output.write_byte(Type::Null.into())?; 230 | Ok(()) 231 | } 232 | 233 | #[inline] 234 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 235 | fn serialize_unit_variant( 236 | self, 237 | _name: &'static str, 238 | variant_index: u32, 239 | variant: &'static str, 240 | ) -> Result { 241 | if self.use_indices { variant_index.serialize(self) } else { variant.serialize(self) } 242 | } 243 | 244 | #[inline] 245 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 246 | fn serialize_newtype_struct( 247 | self, 248 | _name: &'static str, 249 | value: &T, 250 | ) -> Result 251 | where 252 | T: ?Sized + serde::Serialize, 253 | { 254 | value.serialize(self) 255 | } 256 | 257 | #[inline] 258 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 259 | fn serialize_newtype_variant( 260 | self, 261 | _name: &'static str, 262 | variant_index: u32, 263 | variant: &'static str, 264 | value: &T, 265 | ) -> Result 266 | where 267 | T: ?Sized + serde::Serialize, 268 | { 269 | use ::serde::ser::SerializeMap; 270 | let use_indices = self.use_indices; 271 | let mut map = self.serialize_map(Some(1))?; 272 | if use_indices { 273 | map.serialize_entry(&variant_index, value)?; 274 | } else { 275 | map.serialize_entry(variant, value)?; 276 | } 277 | map.end()?; 278 | Ok(()) 279 | } 280 | 281 | #[inline] 282 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 283 | fn serialize_seq(self, _len: Option) -> Result { 284 | self.output.write_byte(Type::SeqStart.into())?; 285 | Ok(self) 286 | } 287 | 288 | #[inline] 289 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 290 | fn serialize_tuple(self, _len: usize) -> Result { 291 | self.output.write_byte(Type::SeqStart.into())?; 292 | Ok(self) 293 | } 294 | 295 | #[inline] 296 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 297 | fn serialize_tuple_struct( 298 | self, 299 | _name: &'static str, 300 | _len: usize, 301 | ) -> Result { 302 | self.output.write_byte(Type::SeqStart.into())?; 303 | Ok(self) 304 | } 305 | 306 | #[inline] 307 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 308 | fn serialize_tuple_variant( 309 | self, 310 | _name: &'static str, 311 | variant_index: u32, 312 | variant: &'static str, 313 | _len: usize, 314 | ) -> Result { 315 | self.output.write_byte(Type::MapStart.into())?; 316 | if self.use_indices { 317 | variant_index.serialize(&mut *self)?; 318 | } else { 319 | variant.serialize(&mut *self)?; 320 | } 321 | self.output.write_byte(Type::SeqStart.into())?; 322 | Ok(self) 323 | } 324 | 325 | #[inline] 326 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 327 | fn serialize_map(self, _len: Option) -> Result { 328 | self.output.write_byte(Type::MapStart.into())?; 329 | Ok(self) 330 | } 331 | 332 | #[inline] 333 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 334 | fn serialize_struct( 335 | self, 336 | _name: &'static str, 337 | _len: usize, 338 | ) -> Result { 339 | self.output.write_byte(Type::MapStart.into())?; 340 | Ok(StructSerializer::new(self)) 341 | } 342 | 343 | #[inline] 344 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 345 | fn serialize_struct_variant( 346 | self, 347 | _name: &'static str, 348 | variant_index: u32, 349 | variant: &'static str, 350 | _len: usize, 351 | ) -> Result { 352 | self.output.write_byte(Type::MapStart.into())?; 353 | if self.use_indices { 354 | variant_index.serialize(&mut *self)?; 355 | } else { 356 | variant.serialize(&mut *self)?; 357 | } 358 | self.output.write_byte(Type::MapStart.into())?; 359 | Ok(StructSerializer::new(self)) 360 | } 361 | 362 | #[cfg(feature = "alloc")] 363 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 364 | fn collect_str(self, value: &T) -> Result 365 | where 366 | T: ?Sized + core::fmt::Display, 367 | { 368 | let s = ::alloc::string::ToString::to_string(value); 369 | self.serialize_str(&s) 370 | } 371 | 372 | #[cfg(not(feature = "alloc"))] 373 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 374 | fn collect_str(self, value: &T) -> Result 375 | where 376 | T: ?Sized + core::fmt::Display, 377 | { 378 | use ::core::fmt::Write; 379 | 380 | /// A writer that counts the number of bytes written. 381 | struct CountWriter(usize); 382 | impl Write for CountWriter { 383 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 384 | self.0 += s.len(); 385 | Ok(()) 386 | } 387 | } 388 | 389 | /// A writer that writes the formatted string into the output. 390 | struct OutputWriter<'a, O>(&'a mut O); 391 | impl Write for OutputWriter<'_, O> { 392 | fn write_str(&mut self, s: &str) -> ::core::fmt::Result { 393 | self.0.write_all(s.as_bytes()).map_err(|_| ::core::fmt::Error) 394 | } 395 | } 396 | 397 | // Pass through once to get the string length. 398 | let mut counter = CountWriter(0); 399 | write!(&mut counter, "{value}")?; 400 | let len = counter.0; 401 | self.output.write_byte(Type::String.into())?; 402 | len.encode(&mut self.output)?; 403 | 404 | // Second pass to actually write the data. 405 | let mut writer = OutputWriter(&mut self.output); 406 | write!(&mut writer, "{value}")?; 407 | 408 | Ok(()) 409 | } 410 | } 411 | 412 | impl ::serde::ser::SerializeSeq for &mut Serializer 413 | where 414 | O: Output, 415 | { 416 | type Ok = (); 417 | type Error = Error; 418 | 419 | #[inline] 420 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 421 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 422 | where 423 | T: ?Sized + serde::Serialize, 424 | { 425 | value.serialize(&mut **self) 426 | } 427 | 428 | #[inline] 429 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 430 | fn end(self) -> Result { 431 | self.output.write_byte(Type::SeqEnd.into())?; 432 | Ok(()) 433 | } 434 | } 435 | 436 | impl ::serde::ser::SerializeTuple for &mut Serializer 437 | where 438 | O: Output, 439 | { 440 | type Ok = (); 441 | type Error = Error; 442 | 443 | #[inline] 444 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 445 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 446 | where 447 | T: ?Sized + serde::Serialize, 448 | { 449 | value.serialize(&mut **self) 450 | } 451 | 452 | #[inline] 453 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 454 | fn end(self) -> Result { 455 | self.output.write_byte(Type::SeqEnd.into())?; 456 | Ok(()) 457 | } 458 | } 459 | 460 | impl ::serde::ser::SerializeTupleStruct for &mut Serializer 461 | where 462 | O: Output, 463 | { 464 | type Ok = (); 465 | type Error = Error; 466 | 467 | #[inline] 468 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 469 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 470 | where 471 | T: ?Sized + serde::Serialize, 472 | { 473 | value.serialize(&mut **self) 474 | } 475 | 476 | #[inline] 477 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 478 | fn end(self) -> Result { 479 | self.output.write_byte(Type::SeqEnd.into())?; 480 | Ok(()) 481 | } 482 | } 483 | 484 | impl ::serde::ser::SerializeTupleVariant for &mut Serializer 485 | where 486 | O: Output, 487 | { 488 | type Ok = (); 489 | type Error = Error; 490 | 491 | #[inline] 492 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 493 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 494 | where 495 | T: ?Sized + serde::Serialize, 496 | { 497 | value.serialize(&mut **self) 498 | } 499 | 500 | #[inline] 501 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 502 | fn end(self) -> Result { 503 | self.output.write_byte(Type::SeqEnd.into())?; 504 | self.output.write_byte(Type::MapEnd.into())?; 505 | Ok(()) 506 | } 507 | } 508 | 509 | impl ::serde::ser::SerializeMap for &mut Serializer 510 | where 511 | O: Output, 512 | { 513 | type Ok = (); 514 | type Error = Error; 515 | 516 | #[inline] 517 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 518 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 519 | where 520 | T: ?Sized + serde::Serialize, 521 | { 522 | key.serialize(&mut **self) 523 | } 524 | 525 | #[inline] 526 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 527 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 528 | where 529 | T: ?Sized + serde::Serialize, 530 | { 531 | value.serialize(&mut **self) 532 | } 533 | 534 | #[inline] 535 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 536 | fn end(self) -> Result { 537 | self.output.write_byte(Type::MapEnd.into())?; 538 | Ok(()) 539 | } 540 | 541 | #[inline] 542 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 543 | fn serialize_entry(&mut self, key: &K, value: &V) -> Result<(), Self::Error> 544 | where 545 | K: ?Sized + serde::Serialize, 546 | V: ?Sized + serde::Serialize, 547 | { 548 | self.serialize_key(key)?; 549 | self.serialize_value(value) 550 | } 551 | } 552 | 553 | /// Struct serializer that keeps track of the field index. 554 | #[derive(Debug)] 555 | pub struct StructSerializer<'a, O> { 556 | /// The inner serializer. 557 | serializer: &'a mut Serializer, 558 | /// The current field index. 559 | field_index: u32, 560 | } 561 | 562 | impl<'a, O> StructSerializer<'a, O> { 563 | /// Create a new struct serializer. 564 | #[must_use] 565 | const fn new(serializer: &'a mut Serializer) -> Self { 566 | Self { serializer, field_index: 0 } 567 | } 568 | } 569 | 570 | impl ::serde::ser::SerializeStruct for StructSerializer<'_, O> 571 | where 572 | O: Output, 573 | { 574 | type Ok = (); 575 | type Error = Error; 576 | 577 | #[inline] 578 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 579 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 580 | where 581 | T: ?Sized + serde::Serialize, 582 | { 583 | if self.serializer.use_indices { 584 | self.field_index.serialize(&mut *self.serializer)?; 585 | } else { 586 | key.serialize(&mut *self.serializer)?; 587 | } 588 | self.field_index += 1; 589 | value.serialize(&mut *self.serializer) 590 | } 591 | 592 | #[inline] 593 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 594 | fn end(self) -> Result { 595 | self.serializer.output.write_byte(Type::MapEnd.into())?; 596 | Ok(()) 597 | } 598 | 599 | #[inline] 600 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 601 | fn skip_field(&mut self, _key: &'static str) -> Result<(), Self::Error> { 602 | self.field_index += 1; 603 | Ok(()) 604 | } 605 | } 606 | 607 | impl ::serde::ser::SerializeStructVariant for StructSerializer<'_, O> 608 | where 609 | O: Output, 610 | { 611 | type Ok = (); 612 | type Error = Error; 613 | 614 | #[inline] 615 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 616 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 617 | where 618 | T: ?Sized + Serialize, 619 | { 620 | if self.serializer.use_indices { 621 | self.field_index.serialize(&mut *self.serializer)?; 622 | } else { 623 | key.serialize(&mut *self.serializer)?; 624 | } 625 | self.field_index += 1; 626 | value.serialize(&mut *self.serializer) 627 | } 628 | 629 | #[inline] 630 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 631 | fn end(self) -> Result { 632 | self.serializer.output.write_byte(Type::MapEnd.into())?; 633 | self.serializer.output.write_byte(Type::MapEnd.into())?; 634 | Ok(()) 635 | } 636 | 637 | #[inline] 638 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 639 | fn skip_field(&mut self, _key: &'static str) -> Result<(), Self::Error> { 640 | self.field_index += 1; 641 | Ok(()) 642 | } 643 | } 644 | -------------------------------------------------------------------------------- /src/tests/features.rs: -------------------------------------------------------------------------------- 1 | //! Tests for crate specific format features, e.g. things are borrowed zero-copy. 2 | 3 | use ::core::num::NonZeroUsize; 4 | use ::serde_bytes::Bytes; 5 | 6 | use super::*; 7 | use crate::{Error, format::Type}; 8 | 9 | #[test] 10 | fn test_string_is_bytes() { 11 | init_tracing(); 12 | let mut buffer = [0; 1024]; 13 | let value = "Hello, my name is bumble bee, bumble bee!"; 14 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 15 | let parsed: &Bytes = crate::from_slice(bytes).unwrap(); 16 | assert_eq!(parsed, value.as_bytes()); 17 | } 18 | 19 | #[test] 20 | fn test_bytes_is_byte_sequence() { 21 | init_tracing(); 22 | let mut buffer = [0; 1024]; 23 | let value = Bytes::new(&[1, 2, 3]); 24 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 25 | let parsed: (u8, u8, u8) = crate::from_slice(bytes).unwrap(); 26 | assert_eq!(parsed, (1, 2, 3)); 27 | } 28 | 29 | #[test] 30 | fn test_string_is_char_sequence() { 31 | init_tracing(); 32 | let mut buffer = [0; 1024]; 33 | let value = "äü😻"; 34 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 35 | let parsed: (char, char, char) = crate::from_slice(bytes).unwrap(); 36 | assert_eq!(parsed, ('ä', 'ü', '😻')); 37 | } 38 | 39 | #[test] 40 | fn test_borrowing() { 41 | let data = [Type::String.into(), 3, b's', b'h', b'y']; 42 | let parsed = crate::from_slice::<&str>(&data).unwrap(); 43 | assert_eq!(parsed, "shy"); 44 | let data = [Type::Bytes.into(), 3, 1, 1, 1]; 45 | let parsed = crate::from_slice::<&[u8]>(&data).unwrap(); 46 | assert_eq!(parsed, &[1, 1, 1]); 47 | } 48 | 49 | #[test] 50 | fn test_deser_calls_borrowed() { 51 | struct Test; 52 | struct Visitor; 53 | impl<'de> ::serde::de::Visitor<'de> for &mut Visitor { 54 | type Value = Test; 55 | 56 | fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 57 | write!(formatter, "a borrowed value") 58 | } 59 | 60 | fn visit_borrowed_bytes(self, _v: &'de [u8]) -> Result 61 | where 62 | E: serde::de::Error, 63 | { 64 | Ok(Test) 65 | } 66 | 67 | fn visit_borrowed_str(self, _v: &'de str) -> Result 68 | where 69 | E: serde::de::Error, 70 | { 71 | Ok(Test) 72 | } 73 | 74 | // The other methods default to erroring. 75 | } 76 | impl<'de> ::serde::de::Deserialize<'de> for Test { 77 | fn deserialize(deserializer: D) -> Result 78 | where 79 | D: serde::Deserializer<'de>, 80 | { 81 | deserializer.deserialize_any(&mut Visitor) 82 | } 83 | } 84 | 85 | let data = [Type::String.into(), 3, b's', b'h', b'y']; 86 | crate::from_slice::(&data).unwrap(); 87 | 88 | let data = [Type::Bytes.into(), 3, b's', b'h', b'y']; 89 | crate::from_slice::(&data).unwrap(); 90 | } 91 | 92 | #[test] 93 | fn test_excess_data() { 94 | let config = Config { error_on_excess_data: true, ..Default::default() }; 95 | let data = [Type::String.into(), 1, b'a', 0]; 96 | let result = crate::from_slice_with_config::<&str>(&data, config); 97 | assert!(matches!(result, Err(Error::ExcessData))); 98 | } 99 | 100 | #[test] 101 | fn test_max_size() { 102 | let config = Config { max_size: Some(NonZeroUsize::new(5).unwrap()), ..Default::default() }; 103 | let data = [Type::Bytes.into(), 4, 1, 2, 3, 4]; 104 | let result = crate::from_slice_with_config::<&Bytes>(&data, config); 105 | assert!(matches!(result, Err(Error::LimitReached))); 106 | 107 | let config = Config { max_size: Some(NonZeroUsize::new(5).unwrap()), ..Default::default() }; 108 | let data = [Type::Bytes.into(), 3, 1, 2, 3]; 109 | let result = crate::from_slice_with_config::<&Bytes>(&data, config); 110 | assert!(result.is_ok()); 111 | 112 | let mut buffer = [0; 1024]; 113 | 114 | let config = Config { max_size: Some(NonZeroUsize::new(5).unwrap()), ..Default::default() }; 115 | let data = Bytes::new(&[1, 2, 3, 4]); 116 | let result = crate::to_slice_with_config(&data, &mut buffer, config); 117 | assert!(matches!(result, Err(Error::LimitReached))); 118 | 119 | let config = Config { max_size: Some(NonZeroUsize::new(5).unwrap()), ..Default::default() }; 120 | let data = Bytes::new(&[1, 2, 3]); 121 | let result = crate::to_slice_with_config(&data, &mut buffer, config); 122 | assert!(result.is_ok()); 123 | } 124 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | //! General, basic serialization and deserialization tests. 2 | #![allow(clippy::unwrap_used, clippy::expect_used, clippy::print_stdout, reason = "Tests")] 3 | 4 | mod basic_types; 5 | mod features; 6 | mod serde_features; 7 | mod special_handling; 8 | mod versioning; 9 | 10 | use ::core::fmt::Debug; 11 | use ::serde::{Deserialize, Serialize}; 12 | 13 | use crate::Config; 14 | 15 | #[cfg(all(feature = "std", feature = "tracing"))] 16 | pub fn init_tracing() { 17 | static INITIALIZED: ::std::sync::OnceLock<()> = ::std::sync::OnceLock::new(); 18 | 19 | INITIALIZED.get_or_init(|| { 20 | tracing_subscriber::fmt() 21 | .with_test_writer() 22 | .with_max_level(::tracing::Level::TRACE) 23 | .pretty() 24 | .with_span_events(::tracing_subscriber::fmt::format::FmtSpan::ACTIVE) 25 | .init(); 26 | }); 27 | } 28 | #[cfg(not(all(feature = "std", feature = "tracing")))] 29 | #[allow(clippy::missing_const_for_fn, reason = "Different feature sets")] 30 | pub fn init_tracing() { 31 | #[cfg(feature = "std")] 32 | println!("To see logs, run tests with the `std` and `tracing` feature enabled"); 33 | } 34 | 35 | 36 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(buffer)))] 37 | fn test_serde<'de, T>(value: &T, buffer: &'de mut [u8]) 38 | where 39 | T: Serialize + Deserialize<'de> + PartialEq + Debug, 40 | { 41 | let bytes = crate::to_slice(&value, buffer).unwrap(); 42 | #[cfg(feature = "tracing")] 43 | tracing::info!("Byte representation: {bytes:?}"); 44 | let deserialized: T = crate::from_slice(bytes).unwrap(); 45 | assert_eq!(deserialized, *value); 46 | } 47 | 48 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(buffer)))] 49 | fn test_serde_with_indices<'de, T>(value: &T, buffer: &'de mut [u8]) 50 | where 51 | T: Serialize + Deserialize<'de> + PartialEq + Debug, 52 | { 53 | let config = Config { use_indices: true, ..Default::default() }; 54 | let bytes = crate::to_slice_with_config(&value, buffer, config).unwrap(); 55 | #[cfg(feature = "tracing")] 56 | tracing::info!("Byte representation: {bytes:?}"); 57 | let deserialized: T = crate::from_slice_with_config(bytes, config).unwrap(); 58 | assert_eq!(deserialized, *value); 59 | } 60 | 61 | #[cfg_attr(feature = "tracing", ::tracing::instrument())] 62 | fn test_deser<'de, T>(bytes: &'de [u8]) 63 | where 64 | T: Deserialize<'de> + Serialize + Debug, 65 | { 66 | let deserialized: T = crate::from_slice(bytes).unwrap(); 67 | #[cfg(feature = "tracing")] 68 | tracing::info!("Deserialized value: {deserialized:?}"); 69 | let mut buffer = [0; 1024]; 70 | let serialized = crate::to_slice(&deserialized, &mut buffer).unwrap(); 71 | assert_eq!(serialized, bytes); 72 | } 73 | 74 | #[cfg_attr(feature = "tracing", ::tracing::instrument())] 75 | fn test_deser_with_indices<'de, T>(bytes: &'de [u8]) 76 | where 77 | T: Deserialize<'de> + Serialize + Debug, 78 | { 79 | let config = Config { use_indices: true, ..Default::default() }; 80 | let deserialized: T = crate::from_slice_with_config(bytes, config).unwrap(); 81 | #[cfg(feature = "tracing")] 82 | tracing::info!("Deserialized value: {deserialized:?}"); 83 | let mut buffer = [0; 1024]; 84 | let serialized = crate::to_slice_with_config(&deserialized, &mut buffer, config).unwrap(); 85 | assert_eq!(serialized, bytes); 86 | } 87 | -------------------------------------------------------------------------------- /src/tests/serde_features.rs: -------------------------------------------------------------------------------- 1 | //! Tests for the serde specifics and features, e.g. #[serde(rename = "X", alias = "Y")]. 2 | 3 | use ::serde_bytes::Bytes; 4 | 5 | use super::*; 6 | use crate::{Error, format::Type}; 7 | 8 | #[test] 9 | fn test_rename_struct() { 10 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 11 | #[serde(rename = "V1", rename_all = "PascalCase")] 12 | struct Struct { 13 | first_field: bool, 14 | #[serde(rename = "Extension")] 15 | second_field: bool, 16 | } 17 | 18 | init_tracing(); 19 | test_serde(&Struct { first_field: true, second_field: true }, &mut [0; 1024]); 20 | test_serde_with_indices(&Struct { first_field: true, second_field: true }, &mut [0; 1024]); 21 | test_deser::(&[ 22 | Type::MapStart.into(), 23 | Type::String.into(), 24 | 10, 25 | b'F', 26 | b'i', 27 | b'r', 28 | b's', 29 | b't', 30 | b'F', 31 | b'i', 32 | b'e', 33 | b'l', 34 | b'd', 35 | Type::BooleanTrue.into(), 36 | Type::String.into(), 37 | 9, 38 | b'E', 39 | b'x', 40 | b't', 41 | b'e', 42 | b'n', 43 | b's', 44 | b'i', 45 | b'o', 46 | b'n', 47 | Type::BooleanTrue.into(), 48 | Type::MapEnd.into(), 49 | ]); 50 | } 51 | 52 | #[test] 53 | fn test_rename_enum() { 54 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 55 | #[serde(rename = "V1", rename_all = "kebab-case", rename_all_fields = "PascalCase")] 56 | enum Enum { 57 | VarA { 58 | first_field: bool, 59 | #[serde(rename = "Extension")] 60 | second_field: bool, 61 | }, 62 | } 63 | 64 | init_tracing(); 65 | test_serde(&Enum::VarA { first_field: true, second_field: true }, &mut [0; 1024]); 66 | test_serde_with_indices(&Enum::VarA { first_field: true, second_field: true }, &mut [0; 1024]); 67 | test_deser::(&[ 68 | Type::MapStart.into(), 69 | Type::String.into(), 70 | 5, 71 | b'v', 72 | b'a', 73 | b'r', 74 | b'-', 75 | b'a', 76 | Type::MapStart.into(), 77 | Type::String.into(), 78 | 10, 79 | b'F', 80 | b'i', 81 | b'r', 82 | b's', 83 | b't', 84 | b'F', 85 | b'i', 86 | b'e', 87 | b'l', 88 | b'd', 89 | Type::BooleanTrue.into(), 90 | Type::String.into(), 91 | 9, 92 | b'E', 93 | b'x', 94 | b't', 95 | b'e', 96 | b'n', 97 | b's', 98 | b'i', 99 | b'o', 100 | b'n', 101 | Type::BooleanTrue.into(), 102 | Type::MapEnd.into(), 103 | Type::MapEnd.into(), 104 | ]); 105 | } 106 | 107 | #[test] 108 | fn test_alias() { 109 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 110 | struct Struct { 111 | #[serde(alias = "a")] 112 | first_field: bool, 113 | } 114 | 115 | init_tracing(); 116 | let parsed: Struct = crate::from_slice(&[ 117 | Type::MapStart.into(), 118 | Type::String.into(), 119 | 1, 120 | b'a', 121 | Type::BooleanTrue.into(), 122 | Type::MapEnd.into(), 123 | ]) 124 | .unwrap(); 125 | assert_eq!(parsed, Struct { first_field: true }); 126 | } 127 | 128 | #[test] 129 | fn test_deny_unknown_fields() { 130 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 131 | #[serde(deny_unknown_fields)] 132 | struct Deny { 133 | a: bool, 134 | } 135 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 136 | struct Accept { 137 | a: bool, 138 | } 139 | 140 | init_tracing(); 141 | let config = Config { use_indices: true, ..Default::default() }; 142 | 143 | let result = crate::from_slice::(&[ 144 | Type::MapStart.into(), 145 | Type::String.into(), 146 | 1, 147 | b'a', 148 | Type::BooleanTrue.into(), 149 | Type::String.into(), 150 | 1, 151 | b'b', 152 | Type::BooleanTrue.into(), 153 | Type::MapEnd.into(), 154 | ]); 155 | #[cfg(feature = "alloc")] 156 | assert!(matches!(result, Err(Error::Message(_)))); 157 | #[cfg(not(feature = "alloc"))] 158 | assert!(matches!(result, Err(Error::Custom))); 159 | 160 | let result = crate::from_slice_with_config::( 161 | &[ 162 | Type::MapStart.into(), 163 | Type::UnsignedInt.into(), 164 | 0, 165 | Type::BooleanTrue.into(), 166 | Type::UnsignedInt.into(), 167 | 1, 168 | Type::BooleanTrue.into(), 169 | Type::MapEnd.into(), 170 | ], 171 | config, 172 | ); 173 | #[cfg(feature = "alloc")] 174 | assert!(matches!(result, Err(Error::Message(_)))); 175 | #[cfg(not(feature = "alloc"))] 176 | assert!(matches!(result, Err(Error::Custom))); 177 | 178 | let parsed: Accept = crate::from_slice(&[ 179 | Type::MapStart.into(), 180 | Type::String.into(), 181 | 1, 182 | b'a', 183 | Type::BooleanTrue.into(), 184 | Type::String.into(), 185 | 1, 186 | b'b', 187 | Type::BooleanTrue.into(), 188 | Type::MapEnd.into(), 189 | ]) 190 | .unwrap(); 191 | assert_eq!(parsed, Accept { a: true }); 192 | 193 | let parsed: Accept = crate::from_slice_with_config( 194 | &[ 195 | Type::MapStart.into(), 196 | Type::UnsignedInt.into(), 197 | 0, 198 | Type::BooleanTrue.into(), 199 | Type::UnsignedInt.into(), 200 | 1, 201 | Type::BooleanTrue.into(), 202 | Type::MapEnd.into(), 203 | ], 204 | config, 205 | ) 206 | .unwrap(); 207 | assert_eq!(parsed, Accept { a: true }); 208 | } 209 | 210 | #[test] 211 | fn test_tagged_enum() { 212 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 213 | #[serde(tag = "type")] 214 | enum Enum { 215 | VarA { a: bool }, 216 | } 217 | 218 | init_tracing(); 219 | test_serde(&Enum::VarA { a: true }, &mut [0; 1024]); 220 | test_deser::(&[ 221 | Type::MapStart.into(), 222 | Type::String.into(), 223 | 4, 224 | b't', 225 | b'y', 226 | b'p', 227 | b'e', 228 | Type::String.into(), 229 | 4, 230 | b'V', 231 | b'a', 232 | b'r', 233 | b'A', 234 | Type::String.into(), 235 | 1, 236 | b'a', 237 | Type::BooleanTrue.into(), 238 | Type::MapEnd.into(), 239 | ]); 240 | 241 | // Using `use_indices` with internally-tagged enums is not supported, it calls serialize_struct, 242 | // but deserialize_any and does not recognize index keys. 243 | } 244 | 245 | #[test] 246 | fn test_tagged_enum_with_content() { 247 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 248 | #[serde(tag = "t", content = "c")] 249 | enum Enum { 250 | VarA { a: bool }, 251 | } 252 | 253 | init_tracing(); 254 | test_serde(&Enum::VarA { a: true }, &mut [0; 1024]); 255 | test_serde_with_indices(&Enum::VarA { a: true }, &mut [0; 1024]); 256 | test_deser::(&[ 257 | Type::MapStart.into(), 258 | Type::String.into(), 259 | 1, 260 | b't', 261 | Type::String.into(), 262 | 4, 263 | b'V', 264 | b'a', 265 | b'r', 266 | b'A', 267 | Type::String.into(), 268 | 1, 269 | b'c', 270 | Type::MapStart.into(), 271 | Type::String.into(), 272 | 1, 273 | b'a', 274 | Type::BooleanTrue.into(), 275 | Type::MapEnd.into(), 276 | Type::MapEnd.into(), 277 | ]); 278 | test_deser_with_indices::(&[ 279 | Type::MapStart.into(), 280 | Type::UnsignedInt.into(), 281 | 0, 282 | Type::UnsignedInt.into(), 283 | 0, 284 | Type::UnsignedInt.into(), 285 | 1, 286 | Type::MapStart.into(), 287 | Type::UnsignedInt.into(), 288 | 0, 289 | Type::BooleanTrue.into(), 290 | Type::MapEnd.into(), 291 | Type::MapEnd.into(), 292 | ]); 293 | } 294 | 295 | 296 | #[test] 297 | fn test_untagged_enum() { 298 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 299 | #[serde(untagged)] 300 | enum Enum { 301 | VarA { a: bool }, 302 | VarB { b: u8 }, 303 | VarC { a: u8 }, 304 | } 305 | 306 | init_tracing(); 307 | test_serde(&Enum::VarA { a: true }, &mut [0; 1024]); 308 | test_serde_with_indices(&Enum::VarA { a: true }, &mut [0; 1024]); 309 | test_serde(&Enum::VarB { b: 5 }, &mut [0; 1024]); 310 | test_serde_with_indices(&Enum::VarB { b: 5 }, &mut [0; 1024]); 311 | test_serde(&Enum::VarC { a: 5 }, &mut [0; 1024]); 312 | // With indices, it is impossible to recognize `VarC`. 313 | test_deser::(&[ 314 | Type::MapStart.into(), 315 | Type::String.into(), 316 | 1, 317 | b'b', 318 | Type::UnsignedInt.into(), 319 | 1, 320 | Type::MapEnd.into(), 321 | ]); 322 | test_deser_with_indices::(&[ 323 | Type::MapStart.into(), 324 | Type::UnsignedInt.into(), 325 | 0, 326 | Type::UnsignedInt.into(), 327 | 1, 328 | Type::MapEnd.into(), 329 | ]); 330 | } 331 | 332 | #[test] 333 | fn test_enum_single_untagged() { 334 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 335 | enum Enum { 336 | VarA { 337 | a: bool, 338 | }, 339 | #[serde(untagged)] 340 | VarB { 341 | b: u8, 342 | }, 343 | } 344 | 345 | init_tracing(); 346 | test_serde(&Enum::VarA { a: true }, &mut [0; 1024]); 347 | test_serde_with_indices(&Enum::VarA { a: true }, &mut [0; 1024]); 348 | test_serde(&Enum::VarB { b: 5 }, &mut [0; 1024]); 349 | test_serde_with_indices(&Enum::VarB { b: 5 }, &mut [0; 1024]); 350 | test_deser::(&[ 351 | Type::MapStart.into(), 352 | Type::String.into(), 353 | 1, 354 | b'b', 355 | Type::UnsignedInt.into(), 356 | 1, 357 | Type::MapEnd.into(), 358 | ]); 359 | test_deser_with_indices::(&[ 360 | Type::MapStart.into(), 361 | Type::UnsignedInt.into(), 362 | 0, 363 | Type::UnsignedInt.into(), 364 | 1, 365 | Type::MapEnd.into(), 366 | ]); 367 | test_deser::(&[ 368 | Type::MapStart.into(), 369 | Type::String.into(), 370 | 4, 371 | b'V', 372 | b'a', 373 | b'r', 374 | b'A', 375 | Type::MapStart.into(), 376 | Type::String.into(), 377 | 1, 378 | b'a', 379 | Type::BooleanFalse.into(), 380 | Type::MapEnd.into(), 381 | Type::MapEnd.into(), 382 | ]); 383 | test_deser_with_indices::(&[ 384 | Type::MapStart.into(), 385 | Type::UnsignedInt.into(), 386 | 0, 387 | Type::MapStart.into(), 388 | Type::UnsignedInt.into(), 389 | 0, 390 | Type::BooleanFalse.into(), 391 | Type::MapEnd.into(), 392 | Type::MapEnd.into(), 393 | ]); 394 | } 395 | 396 | #[test] 397 | fn test_struct_default() { 398 | #[derive(Debug, PartialEq, Default, Serialize, Deserialize)] 399 | #[serde(default)] 400 | struct OwnDefault { 401 | first_field: u8, 402 | second_field: u8, 403 | } 404 | #[derive(Debug, PartialEq, Default, Serialize, Deserialize)] 405 | #[serde(default = "my_default")] 406 | struct SeparateDefault { 407 | first_field: u8, 408 | second_field: u8, 409 | } 410 | const fn my_default() -> SeparateDefault { 411 | SeparateDefault { first_field: 5, second_field: 5 } 412 | } 413 | 414 | init_tracing(); 415 | let config = Config { use_indices: true, ..Default::default() }; 416 | 417 | let parsed = 418 | crate::from_slice::(&[Type::MapStart.into(), Type::MapEnd.into()]).unwrap(); 419 | assert_eq!(parsed.first_field, 0); 420 | assert_eq!(parsed.second_field, 0); 421 | let parsed = crate::from_slice_with_config::( 422 | &[Type::MapStart.into(), Type::MapEnd.into()], 423 | config, 424 | ) 425 | .unwrap(); 426 | assert_eq!(parsed.first_field, 0); 427 | assert_eq!(parsed.second_field, 0); 428 | 429 | let parsed = 430 | crate::from_slice::(&[Type::MapStart.into(), Type::MapEnd.into()]) 431 | .unwrap(); 432 | assert_eq!(parsed.first_field, 5); 433 | assert_eq!(parsed.second_field, 5); 434 | let parsed = crate::from_slice_with_config::( 435 | &[Type::MapStart.into(), Type::MapEnd.into()], 436 | config, 437 | ) 438 | .unwrap(); 439 | assert_eq!(parsed.first_field, 5); 440 | assert_eq!(parsed.second_field, 5); 441 | } 442 | 443 | #[test] 444 | fn test_transparent() { 445 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 446 | #[serde(transparent)] 447 | struct Struct { 448 | content: bool, 449 | } 450 | 451 | init_tracing(); 452 | test_serde(&Struct { content: true }, &mut [0; 1024]); 453 | test_serde_with_indices(&Struct { content: true }, &mut [0; 1024]); 454 | test_deser::(&[Type::BooleanTrue.into()]); 455 | test_deser_with_indices::(&[Type::BooleanTrue.into()]); 456 | } 457 | 458 | #[test] 459 | fn test_from_into() { 460 | #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] 461 | #[serde(from = "bool", into = "bool")] 462 | struct Struct { 463 | boolean: u8, 464 | } 465 | impl From for Struct { 466 | fn from(value: bool) -> Self { 467 | Struct { boolean: if value { 1 } else { 0 } } 468 | } 469 | } 470 | impl From for bool { 471 | fn from(value: Struct) -> Self { 472 | value.boolean != 0 473 | } 474 | } 475 | 476 | init_tracing(); 477 | test_serde(&Struct { boolean: 1 }, &mut [0; 1024]); 478 | test_serde_with_indices(&Struct { boolean: 1 }, &mut [0; 1024]); 479 | test_deser::(&[Type::BooleanFalse.into()]); 480 | test_deser_with_indices::(&[Type::BooleanFalse.into()]); 481 | } 482 | 483 | #[test] 484 | fn test_enum_other() { 485 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 486 | #[serde(tag = "t")] 487 | enum Enum { 488 | VarA, 489 | #[serde(other)] 490 | Default, 491 | } 492 | 493 | init_tracing(); 494 | 495 | test_serde(&Enum::VarA, &mut [0; 1024]); 496 | test_serde(&Enum::Default, &mut [0; 1024]); 497 | 498 | let parsed = crate::from_slice::(&[ 499 | Type::MapStart.into(), 500 | Type::String.into(), 501 | 1, 502 | b't', 503 | Type::String.into(), 504 | 1, 505 | 0, 506 | Type::MapEnd.into(), 507 | ]) 508 | .unwrap(); 509 | assert_eq!(parsed, Enum::Default); 510 | 511 | // Using `use_indices` with internally-tagged enums is not supported, it calls serialize_struct, 512 | // but deserialize_any and does not recognize index keys. 513 | } 514 | 515 | #[test] 516 | fn test_flatten() { 517 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 518 | struct Inner { 519 | b: bool, 520 | } 521 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 522 | struct Outer { 523 | a: bool, 524 | #[serde(flatten)] 525 | ext: Inner, 526 | } 527 | 528 | init_tracing(); 529 | test_serde(&Outer { a: true, ext: Inner { b: true } }, &mut [0; 1024]); 530 | test_serde_with_indices(&Outer { a: true, ext: Inner { b: true } }, &mut [0; 1024]); 531 | test_deser::(&[ 532 | Type::MapStart.into(), 533 | Type::String.into(), 534 | 1, 535 | b'a', 536 | Type::BooleanFalse.into(), 537 | Type::String.into(), 538 | 1, 539 | b'b', 540 | Type::BooleanTrue.into(), 541 | Type::MapEnd.into(), 542 | ]); 543 | // Flatten does not support indices, it is always a string map. 544 | test_deser_with_indices::(&[ 545 | Type::MapStart.into(), 546 | Type::String.into(), 547 | 1, 548 | b'a', 549 | Type::BooleanFalse.into(), 550 | Type::String.into(), 551 | 1, 552 | b'b', 553 | Type::BooleanTrue.into(), 554 | Type::MapEnd.into(), 555 | ]); 556 | } 557 | 558 | #[test] 559 | fn test_borrow() { 560 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 561 | struct Inner<'a> { 562 | s: &'a str, 563 | } 564 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 565 | struct Outer<'a> { 566 | #[serde(borrow)] 567 | i: Inner<'a>, 568 | b: &'a Bytes, 569 | } 570 | 571 | init_tracing(); 572 | test_serde(&Outer { b: Bytes::new(&[1, 2]), i: Inner { s: "hi" } }, &mut [0; 1024]); 573 | test_serde_with_indices( 574 | &Outer { b: Bytes::new(&[1, 2]), i: Inner { s: "hi" } }, 575 | &mut [0; 1024], 576 | ); 577 | test_deser::(&[ 578 | Type::MapStart.into(), 579 | Type::String.into(), 580 | 1, 581 | b'i', 582 | Type::MapStart.into(), 583 | Type::String.into(), 584 | 1, 585 | b's', 586 | Type::String.into(), 587 | 2, 588 | b'h', 589 | b'i', 590 | Type::MapEnd.into(), 591 | Type::String.into(), 592 | 1, 593 | b'b', 594 | Type::Bytes.into(), 595 | 2, 596 | 1, 597 | 2, 598 | Type::MapEnd.into(), 599 | ]); 600 | test_deser_with_indices::(&[ 601 | Type::MapStart.into(), 602 | Type::UnsignedInt.into(), 603 | 0, 604 | Type::MapStart.into(), 605 | Type::UnsignedInt.into(), 606 | 0, 607 | Type::String.into(), 608 | 2, 609 | b'h', 610 | b'i', 611 | Type::MapEnd.into(), 612 | Type::UnsignedInt.into(), 613 | 1, 614 | Type::Bytes.into(), 615 | 2, 616 | 1, 617 | 2, 618 | Type::MapEnd.into(), 619 | ]); 620 | } 621 | -------------------------------------------------------------------------------- /src/tests/special_handling.rs: -------------------------------------------------------------------------------- 1 | //! Tests for special cases in serialization and deserialization. 2 | 3 | use ::serde_bytes::Bytes; 4 | 5 | use super::*; 6 | use crate::{Error, format::Type}; 7 | 8 | #[test] 9 | fn test_type_mismatch() { 10 | init_tracing(); 11 | let mut buffer = [0; 1024]; 12 | 13 | let value = true; 14 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 15 | let result = crate::from_slice::<()>(bytes); 16 | assert!(matches!(result, Err(Error::WrongType(Type::BooleanTrue, &[Type::Null])))); 17 | 18 | let value = true; 19 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 20 | let result = crate::from_slice::(bytes); 21 | assert!(matches!(result, Err(Error::WrongType(Type::BooleanTrue, &[Type::SignedInt])))); 22 | 23 | let value = 0.0_f32; 24 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 25 | let result = crate::from_slice::(bytes); 26 | assert!(matches!(result, Err(Error::WrongType(Type::Float32, &[Type::SignedInt])))); 27 | 28 | let value = 0_usize; 29 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 30 | let result = crate::from_slice::(bytes); 31 | assert!(matches!(result, Err(Error::WrongType(Type::UnsignedInt, _)))); 32 | 33 | let value = 0_usize; 34 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 35 | let result = crate::from_slice::(bytes); 36 | assert!(matches!(result, Err(Error::WrongType(Type::UnsignedInt, &[Type::SignedInt])))); 37 | 38 | let value = Bytes::new(&[1, 2, 3, 4]); 39 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 40 | let result = crate::from_slice::<&str>(bytes); 41 | assert!(matches!(result, Err(Error::WrongType(Type::Bytes, &[Type::String])))); 42 | } 43 | 44 | #[test] 45 | fn test_unexpected_eof() { 46 | let data = [Type::String.into(), 2, b'a']; 47 | let result = crate::from_slice::<&str>(&data); 48 | assert!(matches!(result, Err(Error::UnexpectedEnd))); 49 | } 50 | 51 | #[test] 52 | fn test_output_too_small() { 53 | let mut buffer = [0; 5]; 54 | let result = crate::to_slice(&"hallo", &mut buffer); 55 | assert!(matches!(result, Err(Error::BufferTooSmall))); 56 | } 57 | -------------------------------------------------------------------------------- /src/tests/versioning.rs: -------------------------------------------------------------------------------- 1 | //! Test whether it is possible to make structs/enums forward/backward compatible. 2 | 3 | use super::*; 4 | 5 | #[test] 6 | fn test_struct_field_added() { 7 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 8 | struct V1 { 9 | a: bool, 10 | } 11 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 12 | struct V2 { 13 | #[serde(default)] 14 | b: bool, 15 | a: bool, 16 | } 17 | 18 | init_tracing(); 19 | let mut buffer = [0; 1024]; 20 | let value = V1 { a: true }; 21 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 22 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 23 | assert_eq!(parsed.a, value.a); 24 | } 25 | 26 | #[test] 27 | fn test_struct_field_added_with_indices() { 28 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 29 | struct V1 { 30 | a: bool, 31 | } 32 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 33 | struct V2 { 34 | a: bool, 35 | #[serde(default)] 36 | b: bool, 37 | } 38 | 39 | init_tracing(); 40 | let config = Config { use_indices: true, ..Default::default() }; 41 | let mut buffer = [0; 1024]; 42 | let value = V1 { a: true }; 43 | let bytes = crate::to_slice_with_config(&value, &mut buffer, config).unwrap(); 44 | let parsed: V2 = crate::from_slice_with_config(bytes, config).unwrap(); 45 | assert_eq!(parsed.a, value.a); 46 | } 47 | 48 | #[test] 49 | fn test_struct_fields_reordered() { 50 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 51 | struct V1 { 52 | a: bool, 53 | b: bool, 54 | c: bool, 55 | } 56 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 57 | struct V2 { 58 | b: bool, 59 | c: bool, 60 | a: bool, 61 | } 62 | 63 | init_tracing(); 64 | let mut buffer = [0; 1024]; 65 | let value = V1 { a: true, b: false, c: true }; 66 | 67 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 68 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 69 | assert_eq!(parsed.a, value.a); 70 | assert_eq!(parsed.b, value.b); 71 | assert_eq!(parsed.c, value.c); 72 | } 73 | 74 | #[test] 75 | fn test_enum_variant_added() { 76 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 77 | enum V1 { 78 | Some(bool), 79 | Multiple(bool, bool), 80 | } 81 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 82 | enum V2 { 83 | None, 84 | Some(bool), 85 | Multiple(bool, bool), 86 | } 87 | 88 | init_tracing(); 89 | let mut buffer = [0; 1024]; 90 | 91 | let value = V1::Some(true); 92 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 93 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 94 | assert_eq!(parsed, V2::Some(true)); 95 | 96 | let value = V1::Multiple(false, true); 97 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 98 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 99 | assert_eq!(parsed, V2::Multiple(false, true)); 100 | } 101 | 102 | #[test] 103 | fn test_enum_variant_added_with_indices() { 104 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 105 | enum V1 { 106 | None, 107 | Some(bool), 108 | } 109 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 110 | enum V2 { 111 | None, 112 | Some(bool), 113 | Multiple(bool, bool), 114 | } 115 | 116 | init_tracing(); 117 | let config = Config { use_indices: true, ..Default::default() }; 118 | let mut buffer = [0; 1024]; 119 | 120 | let value = V1::Some(true); 121 | let bytes = crate::to_slice_with_config(&value, &mut buffer, config).unwrap(); 122 | let parsed: V2 = crate::from_slice_with_config(bytes, config).unwrap(); 123 | assert_eq!(parsed, V2::Some(true)); 124 | 125 | let value = V1::None; 126 | let bytes = crate::to_slice_with_config(&value, &mut buffer, config).unwrap(); 127 | let parsed: V2 = crate::from_slice_with_config(bytes, config).unwrap(); 128 | assert_eq!(parsed, V2::None); 129 | } 130 | 131 | #[test] 132 | fn test_enum_variants_reordered() { 133 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 134 | enum V1 { 135 | X, 136 | Y, 137 | Z, 138 | } 139 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 140 | enum V2 { 141 | Z, 142 | X, 143 | Y, 144 | } 145 | 146 | init_tracing(); 147 | let mut buffer = [0; 1024]; 148 | 149 | let value = V1::X; 150 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 151 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 152 | assert_eq!(parsed, V2::X); 153 | 154 | let value = V1::Y; 155 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 156 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 157 | assert_eq!(parsed, V2::Y); 158 | 159 | let value = V1::Z; 160 | let bytes = crate::to_slice(&value, &mut buffer).unwrap(); 161 | let parsed: V2 = crate::from_slice(bytes).unwrap(); 162 | assert_eq!(parsed, V2::Z); 163 | } 164 | -------------------------------------------------------------------------------- /src/value/de.rs: -------------------------------------------------------------------------------- 1 | //! Deserialization from [Value] to any type. 2 | #![cfg_attr( 3 | feature = "tracing", 4 | allow(clippy::used_underscore_binding, reason = "Only used in tracing::instrument") 5 | )] 6 | 7 | use ::serde::de::{Error, IntoDeserializer, Unexpected}; 8 | 9 | use super::*; 10 | 11 | /// Deserializer to deserialize a [Value] into any type. 12 | #[derive(Debug)] 13 | pub struct ValueDeserializer<'de>(Value<'de>); 14 | 15 | impl<'de> ValueDeserializer<'de> { 16 | /// Create a new deserializer from the given value. 17 | #[must_use] 18 | pub const fn new(value: Value<'de>) -> Self { 19 | Self(value) 20 | } 21 | 22 | /// Deserialize the value. 23 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 24 | fn deserialize(self, visitor: V) -> Result 25 | where 26 | V: ::serde::de::Visitor<'de>, 27 | { 28 | match self.0 { 29 | Value::Null => visitor.visit_none(), 30 | Value::Bool(b) => visitor.visit_bool(b), 31 | Value::Integer(int) => visit_integer(int, visitor), 32 | Value::Float(Float::F32(float)) => visitor.visit_f32(float), 33 | Value::Float(Float::F64(float)) => visitor.visit_f64(float), 34 | Value::Bytes(Cow::Borrowed(bytes)) => visitor.visit_borrowed_bytes(bytes), 35 | Value::Bytes(Cow::Owned(bytes)) => visitor.visit_byte_buf(bytes), 36 | Value::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), 37 | Value::String(Cow::Owned(s)) => visitor.visit_string(s), 38 | Value::Array(arr) => visitor.visit_seq(ValueSeqDeserializer(arr)), 39 | Value::Map(map) => visitor.visit_map(ValueMapDeserializer(map)), 40 | } 41 | } 42 | } 43 | 44 | impl<'de> ::serde::de::Deserializer<'de> for ValueDeserializer<'de> { 45 | type Error = crate::Error; 46 | 47 | #[inline] 48 | fn is_human_readable(&self) -> bool { 49 | true 50 | } 51 | 52 | #[inline] 53 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 54 | fn deserialize_any(self, visitor: V) -> Result 55 | where 56 | V: serde::de::Visitor<'de>, 57 | { 58 | Self::deserialize(self, visitor) 59 | } 60 | 61 | #[inline] 62 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 63 | fn deserialize_bool(self, visitor: V) -> Result 64 | where 65 | V: serde::de::Visitor<'de>, 66 | { 67 | match self.0 { 68 | Value::Bool(value) => visitor.visit_bool(value), 69 | other => Err(Error::invalid_type(Unexpected::from(&other), &"bool")), 70 | } 71 | } 72 | 73 | #[inline] 74 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 75 | fn deserialize_i8(self, visitor: V) -> Result 76 | where 77 | V: serde::de::Visitor<'de>, 78 | { 79 | match self.0 { 80 | Value::Integer(int) => visit_integer(int, visitor), 81 | other => Err(Error::invalid_type(Unexpected::from(&other), &"i8")), 82 | } 83 | } 84 | 85 | #[inline] 86 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 87 | fn deserialize_i16(self, visitor: V) -> Result 88 | where 89 | V: serde::de::Visitor<'de>, 90 | { 91 | match self.0 { 92 | Value::Integer(int) => visit_integer(int, visitor), 93 | other => Err(Error::invalid_type(Unexpected::from(&other), &"i16")), 94 | } 95 | } 96 | 97 | #[inline] 98 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 99 | fn deserialize_i32(self, visitor: V) -> Result 100 | where 101 | V: serde::de::Visitor<'de>, 102 | { 103 | match self.0 { 104 | Value::Integer(int) => visit_integer(int, visitor), 105 | other => Err(Error::invalid_type(Unexpected::from(&other), &"i32")), 106 | } 107 | } 108 | 109 | #[inline] 110 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 111 | fn deserialize_i64(self, visitor: V) -> Result 112 | where 113 | V: serde::de::Visitor<'de>, 114 | { 115 | match self.0 { 116 | Value::Integer(int) => visit_integer(int, visitor), 117 | other => Err(Error::invalid_type(Unexpected::from(&other), &"i64")), 118 | } 119 | } 120 | 121 | #[inline] 122 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 123 | fn deserialize_u8(self, visitor: V) -> Result 124 | where 125 | V: serde::de::Visitor<'de>, 126 | { 127 | match self.0 { 128 | Value::Integer(int) => visit_integer(int, visitor), 129 | other => Err(Error::invalid_type(Unexpected::from(&other), &"u8")), 130 | } 131 | } 132 | 133 | #[inline] 134 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 135 | fn deserialize_u16(self, visitor: V) -> Result 136 | where 137 | V: serde::de::Visitor<'de>, 138 | { 139 | match self.0 { 140 | Value::Integer(int) => visit_integer(int, visitor), 141 | other => Err(Error::invalid_type(Unexpected::from(&other), &"u16")), 142 | } 143 | } 144 | 145 | #[inline] 146 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 147 | fn deserialize_u32(self, visitor: V) -> Result 148 | where 149 | V: serde::de::Visitor<'de>, 150 | { 151 | match self.0 { 152 | Value::Integer(int) => visit_integer(int, visitor), 153 | other => Err(Error::invalid_type(Unexpected::from(&other), &"u32")), 154 | } 155 | } 156 | 157 | #[inline] 158 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 159 | fn deserialize_u64(self, visitor: V) -> Result 160 | where 161 | V: serde::de::Visitor<'de>, 162 | { 163 | match self.0 { 164 | Value::Integer(int) => visit_integer(int, visitor), 165 | other => Err(Error::invalid_type(Unexpected::from(&other), &"u64")), 166 | } 167 | } 168 | 169 | #[inline] 170 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 171 | fn deserialize_i128(self, visitor: V) -> Result 172 | where 173 | V: serde::de::Visitor<'de>, 174 | { 175 | match self.0 { 176 | Value::Integer(int) => visit_integer(int, visitor), 177 | other => Err(Error::invalid_type(Unexpected::from(&other), &"i128")), 178 | } 179 | } 180 | 181 | #[inline] 182 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 183 | fn deserialize_u128(self, visitor: V) -> Result 184 | where 185 | V: serde::de::Visitor<'de>, 186 | { 187 | match self.0 { 188 | Value::Integer(int) => visit_integer(int, visitor), 189 | other => Err(Error::invalid_type(Unexpected::from(&other), &"u128")), 190 | } 191 | } 192 | 193 | #[inline] 194 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 195 | fn deserialize_f32(self, visitor: V) -> Result 196 | where 197 | V: serde::de::Visitor<'de>, 198 | { 199 | match self.0 { 200 | Value::Float(Float::F32(float)) => visitor.visit_f32(float), 201 | Value::Float(Float::F64(float)) => visitor.visit_f64(float), 202 | other => Err(Error::invalid_type(Unexpected::from(&other), &"float")), 203 | } 204 | } 205 | 206 | #[inline] 207 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 208 | fn deserialize_f64(self, visitor: V) -> Result 209 | where 210 | V: serde::de::Visitor<'de>, 211 | { 212 | match self.0 { 213 | Value::Float(Float::F32(float)) => visitor.visit_f32(float), 214 | Value::Float(Float::F64(float)) => visitor.visit_f64(float), 215 | other => Err(Error::invalid_type(Unexpected::from(&other), &"float")), 216 | } 217 | } 218 | 219 | #[inline] 220 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 221 | fn deserialize_char(self, visitor: V) -> Result 222 | where 223 | V: serde::de::Visitor<'de>, 224 | { 225 | match self.0 { 226 | Value::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), 227 | Value::String(Cow::Owned(s)) => visitor.visit_string(s), 228 | other => Err(Error::invalid_type(Unexpected::from(&other), &"char")), 229 | } 230 | } 231 | 232 | #[inline] 233 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 234 | fn deserialize_str(self, visitor: V) -> Result 235 | where 236 | V: serde::de::Visitor<'de>, 237 | { 238 | match self.0 { 239 | Value::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), 240 | Value::String(Cow::Owned(s)) => visitor.visit_string(s), 241 | other => Err(Error::invalid_type(Unexpected::from(&other), &"string")), 242 | } 243 | } 244 | 245 | #[inline] 246 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 247 | fn deserialize_string(self, visitor: V) -> Result 248 | where 249 | V: serde::de::Visitor<'de>, 250 | { 251 | match self.0 { 252 | Value::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), 253 | Value::String(Cow::Owned(s)) => visitor.visit_string(s), 254 | other => Err(Error::invalid_type(Unexpected::from(&other), &"string")), 255 | } 256 | } 257 | 258 | #[inline] 259 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 260 | fn deserialize_bytes(self, visitor: V) -> Result 261 | where 262 | V: serde::de::Visitor<'de>, 263 | { 264 | match self.0 { 265 | Value::Bytes(Cow::Borrowed(bytes)) => visitor.visit_borrowed_bytes(bytes), 266 | Value::Bytes(Cow::Owned(bytes)) => visitor.visit_byte_buf(bytes), 267 | other => Err(Error::invalid_type(Unexpected::from(&other), &"bytes")), 268 | } 269 | } 270 | 271 | #[inline] 272 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 273 | fn deserialize_byte_buf(self, visitor: V) -> Result 274 | where 275 | V: serde::de::Visitor<'de>, 276 | { 277 | match self.0 { 278 | Value::Bytes(Cow::Borrowed(bytes)) => visitor.visit_borrowed_bytes(bytes), 279 | Value::Bytes(Cow::Owned(bytes)) => visitor.visit_byte_buf(bytes), 280 | other => Err(Error::invalid_type(Unexpected::from(&other), &"bytes")), 281 | } 282 | } 283 | 284 | #[inline] 285 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 286 | fn deserialize_option(self, visitor: V) -> Result 287 | where 288 | V: serde::de::Visitor<'de>, 289 | { 290 | if matches!(&self.0, Value::Null) { visitor.visit_none() } else { visitor.visit_some(self) } 291 | } 292 | 293 | #[inline] 294 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 295 | fn deserialize_unit(self, visitor: V) -> Result 296 | where 297 | V: serde::de::Visitor<'de>, 298 | { 299 | match self.0 { 300 | Value::Null => visitor.visit_unit(), 301 | other => Err(Error::invalid_type(Unexpected::from(&other), &"unit")), 302 | } 303 | } 304 | 305 | #[inline] 306 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 307 | fn deserialize_unit_struct( 308 | self, 309 | _name: &'static str, 310 | visitor: V, 311 | ) -> Result 312 | where 313 | V: serde::de::Visitor<'de>, 314 | { 315 | match self.0 { 316 | Value::Null => visitor.visit_unit(), 317 | other => Err(Error::invalid_type(Unexpected::from(&other), &"unit")), 318 | } 319 | } 320 | 321 | #[inline] 322 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 323 | fn deserialize_newtype_struct( 324 | self, 325 | _name: &'static str, 326 | visitor: V, 327 | ) -> Result 328 | where 329 | V: serde::de::Visitor<'de>, 330 | { 331 | visitor.visit_newtype_struct(self) 332 | } 333 | 334 | #[inline] 335 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 336 | fn deserialize_seq(self, visitor: V) -> Result 337 | where 338 | V: serde::de::Visitor<'de>, 339 | { 340 | match self.0 { 341 | Value::Array(arr) => visitor.visit_seq(ValueSeqDeserializer(arr)), 342 | other => Err(Error::invalid_type(Unexpected::from(&other), &"sequence")), 343 | } 344 | } 345 | 346 | #[inline] 347 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 348 | fn deserialize_tuple(self, _len: usize, visitor: V) -> Result 349 | where 350 | V: serde::de::Visitor<'de>, 351 | { 352 | match self.0 { 353 | Value::Array(arr) => visitor.visit_seq(ValueSeqDeserializer(arr)), 354 | other => Err(Error::invalid_type(Unexpected::from(&other), &"tuple")), 355 | } 356 | } 357 | 358 | #[inline] 359 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 360 | fn deserialize_tuple_struct( 361 | self, 362 | _name: &'static str, 363 | _len: usize, 364 | visitor: V, 365 | ) -> Result 366 | where 367 | V: serde::de::Visitor<'de>, 368 | { 369 | match self.0 { 370 | Value::Array(arr) => visitor.visit_seq(ValueSeqDeserializer(arr)), 371 | other => Err(Error::invalid_type(Unexpected::from(&other), &"tuple struct")), 372 | } 373 | } 374 | 375 | #[inline] 376 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 377 | fn deserialize_map(self, visitor: V) -> Result 378 | where 379 | V: serde::de::Visitor<'de>, 380 | { 381 | match self.0 { 382 | Value::Map(map) => visitor.visit_map(ValueMapDeserializer(map)), 383 | other => Err(Error::invalid_type(Unexpected::from(&other), &"map")), 384 | } 385 | } 386 | 387 | #[inline] 388 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 389 | fn deserialize_struct( 390 | self, 391 | _name: &'static str, 392 | _fields: &'static [&'static str], 393 | visitor: V, 394 | ) -> Result 395 | where 396 | V: serde::de::Visitor<'de>, 397 | { 398 | match self.0 { 399 | Value::Map(map) => visitor.visit_map(ValueMapDeserializer(map)), 400 | other => Err(Error::invalid_type(Unexpected::from(&other), &"map")), 401 | } 402 | } 403 | 404 | #[inline] 405 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 406 | fn deserialize_enum( 407 | self, 408 | _name: &'static str, 409 | _variants: &'static [&'static str], 410 | visitor: V, 411 | ) -> Result 412 | where 413 | V: serde::de::Visitor<'de>, 414 | { 415 | match self.0 { 416 | Value::Integer(Integer::Unsigned(int)) => 417 | { 418 | #[expect(clippy::cast_possible_truncation, reason = "Enum discriminators are i32")] 419 | visitor.visit_enum((int as u32).into_deserializer()) 420 | } 421 | Value::String(s) => visitor.visit_enum(s.as_ref().into_deserializer()), 422 | Value::Map(map) => visitor.visit_enum(ValueEnumDeserializer(map)), 423 | other => Err(Error::invalid_type(Unexpected::from(&other), &"enum")), 424 | } 425 | } 426 | 427 | #[inline] 428 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 429 | fn deserialize_identifier(self, visitor: V) -> Result 430 | where 431 | V: serde::de::Visitor<'de>, 432 | { 433 | match self.0 { 434 | Value::Integer(Integer::Unsigned(_)) => self.deserialize_u32(visitor), 435 | Value::String(_) => self.deserialize_str(visitor), 436 | other => Err(Error::invalid_type(Unexpected::from(&other), &"identifier")), 437 | } 438 | } 439 | 440 | #[inline] 441 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 442 | fn deserialize_ignored_any(self, visitor: V) -> Result 443 | where 444 | V: serde::de::Visitor<'de>, 445 | { 446 | self.deserialize_any(visitor) 447 | } 448 | } 449 | 450 | /// Enum deserializer. 451 | #[derive(Debug)] 452 | struct ValueEnumDeserializer<'de>(VecDeque<(Value<'de>, Value<'de>)>); 453 | 454 | impl<'de> ::serde::de::EnumAccess<'de> for ValueEnumDeserializer<'de> { 455 | type Error = crate::Error; 456 | type Variant = ValueDeserializer<'de>; 457 | 458 | #[inline] 459 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 460 | fn variant_seed(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> 461 | where 462 | V: serde::de::DeserializeSeed<'de>, 463 | { 464 | if self.0.len() != 1 { 465 | return Err(Error::invalid_length(1, &"exactly one key-value-pair")); 466 | } 467 | 468 | #[expect(clippy::unwrap_used, reason = "Was just checked")] 469 | let (key, value) = self.0.pop_front().unwrap(); 470 | let res = seed.deserialize(ValueDeserializer(key))?; 471 | Ok((res, ValueDeserializer(value))) 472 | } 473 | } 474 | 475 | impl<'de> ::serde::de::VariantAccess<'de> for ValueDeserializer<'de> { 476 | type Error = crate::Error; 477 | 478 | #[inline] 479 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 480 | fn unit_variant(self) -> Result<(), Self::Error> { 481 | Err(Error::invalid_type(Unexpected::from(&self.0), &"unit variant")) 482 | } 483 | 484 | #[inline] 485 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 486 | fn newtype_variant_seed(self, seed: T) -> Result 487 | where 488 | T: serde::de::DeserializeSeed<'de>, 489 | { 490 | seed.deserialize(self) 491 | } 492 | 493 | #[inline] 494 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 495 | fn tuple_variant(self, _len: usize, visitor: V) -> Result 496 | where 497 | V: serde::de::Visitor<'de>, 498 | { 499 | ::serde::de::Deserializer::deserialize_seq(self, visitor) 500 | } 501 | 502 | #[inline] 503 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, visitor)))] 504 | fn struct_variant( 505 | self, 506 | _fields: &'static [&'static str], 507 | visitor: V, 508 | ) -> Result 509 | where 510 | V: serde::de::Visitor<'de>, 511 | { 512 | ::serde::de::Deserializer::deserialize_map(self, visitor) 513 | } 514 | } 515 | 516 | /// Sequence deserializer. 517 | #[derive(Debug)] 518 | struct ValueSeqDeserializer<'de>(VecDeque>); 519 | 520 | impl<'de> ::serde::de::SeqAccess<'de> for ValueSeqDeserializer<'de> { 521 | type Error = crate::Error; 522 | 523 | #[inline] 524 | fn size_hint(&self) -> Option { 525 | Some(self.0.len()) 526 | } 527 | 528 | #[inline] 529 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 530 | fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> 531 | where 532 | T: serde::de::DeserializeSeed<'de>, 533 | { 534 | if let Some(value) = self.0.pop_front() { 535 | seed.deserialize(ValueDeserializer(value)).map(Some) 536 | } else { 537 | Ok(None) 538 | } 539 | } 540 | } 541 | 542 | /// Map deserializer. 543 | #[derive(Debug)] 544 | struct ValueMapDeserializer<'de>(VecDeque<(Value<'de>, Value<'de>)>); 545 | 546 | impl<'de> ::serde::de::MapAccess<'de> for ValueMapDeserializer<'de> { 547 | type Error = crate::Error; 548 | 549 | #[inline] 550 | fn size_hint(&self) -> Option { 551 | Some(self.0.len()) 552 | } 553 | 554 | #[inline] 555 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 556 | fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> 557 | where 558 | K: serde::de::DeserializeSeed<'de>, 559 | { 560 | if let Some((key, _value)) = self.0.front_mut() { 561 | let value = ::core::mem::replace(key, Value::Null); 562 | Ok(Some(seed.deserialize(ValueDeserializer(value))?)) 563 | } else { 564 | Ok(None) 565 | } 566 | } 567 | 568 | #[inline] 569 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 570 | fn next_value_seed(&mut self, seed: V) -> Result 571 | where 572 | V: serde::de::DeserializeSeed<'de>, 573 | { 574 | if let Some((_key, value)) = self.0.pop_front() { 575 | Ok(seed.deserialize(ValueDeserializer(value))?) 576 | } else { 577 | Err(Error::custom("next_value_seed called without next_key_seed")) 578 | } 579 | } 580 | 581 | #[inline] 582 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 583 | #[allow(clippy::type_complexity, reason = "Tracing makes this trigger, also it's serde trait")] 584 | fn next_entry_seed( 585 | &mut self, 586 | kseed: K, 587 | vseed: V, 588 | ) -> Result, Self::Error> 589 | where 590 | K: serde::de::DeserializeSeed<'de>, 591 | V: serde::de::DeserializeSeed<'de>, 592 | { 593 | if let Some((key, value)) = self.0.pop_front() { 594 | let key = kseed.deserialize(ValueDeserializer(key))?; 595 | let value = vseed.deserialize(ValueDeserializer(value))?; 596 | Ok(Some((key, value))) 597 | } else { 598 | Ok(None) 599 | } 600 | } 601 | } 602 | 603 | /// Visit the integer, depending on its value / size. 604 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(visitor)))] 605 | fn visit_integer<'de, V>(int: Integer, visitor: V) -> Result 606 | where 607 | V: ::serde::de::Visitor<'de>, 608 | { 609 | #[allow(clippy::cast_lossless, reason = "We won't change it")] 610 | #[allow(clippy::cast_possible_truncation, reason = "Integer casting is necessary")] 611 | match int { 612 | Integer::Unsigned(int) if int <= u8::MAX as u128 => visitor.visit_u8(int as u8), 613 | Integer::Unsigned(int) if int <= u16::MAX as u128 => visitor.visit_u16(int as u16), 614 | Integer::Unsigned(int) if int <= u32::MAX as u128 => visitor.visit_u32(int as u32), 615 | Integer::Unsigned(int) if int <= u64::MAX as u128 => visitor.visit_u64(int as u64), 616 | Integer::Unsigned(int) => visitor.visit_u128(int), 617 | Integer::Signed(int) if (i8::MIN as i128 ..= i8::MAX as i128).contains(&int) => { 618 | visitor.visit_i8(int as i8) 619 | } 620 | Integer::Signed(int) if (i16::MIN as i128 ..= i16::MAX as i128).contains(&int) => { 621 | visitor.visit_i16(int as i16) 622 | } 623 | Integer::Signed(int) if (i32::MIN as i128 ..= i32::MAX as i128).contains(&int) => { 624 | visitor.visit_i32(int as i32) 625 | } 626 | Integer::Signed(int) if (i64::MIN as i128 ..= i64::MAX as i128).contains(&int) => { 627 | visitor.visit_i64(int as i64) 628 | } 629 | Integer::Signed(int) => visitor.visit_i128(int), 630 | } 631 | } 632 | 633 | impl<'a, 'de> From<&'a Value<'de>> for Unexpected<'a> { 634 | fn from(value: &'a Value<'de>) -> Self { 635 | #[allow(clippy::cast_possible_truncation, reason = "Integer casting is necessary")] 636 | match value { 637 | Value::Null => Unexpected::Unit, 638 | Value::Bool(b) => Unexpected::Bool(*b), 639 | Value::Integer(Integer::Unsigned(int)) => Unexpected::Unsigned(*int as u64), 640 | Value::Integer(Integer::Signed(int)) => Unexpected::Signed(*int as i64), 641 | Value::Float(Float::F32(float)) => Unexpected::Float(f64::from(*float)), 642 | Value::Float(Float::F64(float)) => Unexpected::Float(*float), 643 | Value::Bytes(bytes) => Unexpected::Bytes(bytes), 644 | Value::String(s) => Unexpected::Str(s), 645 | Value::Array(_arr) => Unexpected::Seq, 646 | Value::Map(_map) => Unexpected::Map, 647 | } 648 | } 649 | } 650 | -------------------------------------------------------------------------------- /src/value/mod.rs: -------------------------------------------------------------------------------- 1 | //! Generic value that can contain any value in our data format. 2 | #![cfg_attr( 3 | feature = "tracing", 4 | allow(clippy::used_underscore_binding, reason = "Only used in tracing::instrument") 5 | )] 6 | 7 | mod de; 8 | mod ser; 9 | 10 | use ::alloc::{ 11 | borrow::{Cow, ToOwned}, 12 | boxed::Box, 13 | collections::VecDeque, 14 | string::String, 15 | vec::Vec, 16 | }; 17 | use ::core::{ 18 | fmt::Write, 19 | marker::PhantomData, 20 | ops::{Deref, DerefMut}, 21 | }; 22 | use ::serde::{Deserialize, Serialize}; 23 | 24 | use crate::{Config, Result}; 25 | 26 | /// Serialize a type to the generic [Value] type using the given configuration. 27 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(value)))] 28 | pub fn to_value_with_config(value: &T, config: Config) -> Result> 29 | where 30 | T: Serialize, 31 | { 32 | let ser = ser::ValueSerializer::new(config.use_indices); 33 | value.serialize(ser) 34 | } 35 | 36 | /// Serialize a type to the generic [Value] type. 37 | pub fn to_value(value: &T) -> Result> 38 | where 39 | T: Serialize, 40 | { 41 | to_value_with_config(value, Config::default()) 42 | } 43 | 44 | /// Deserialize a type from a generic [Value] using the given configuration. 45 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(value)))] 46 | pub fn from_value_with_config<'de, T>(value: Value<'de>, _config: Config) -> Result 47 | where 48 | T: Deserialize<'de>, 49 | { 50 | let de = de::ValueDeserializer::new(value); 51 | T::deserialize(de) 52 | } 53 | 54 | /// Deserialize a type from a generic [Value]. 55 | pub fn from_value<'de, T>(value: Value<'de>) -> Result 56 | where 57 | T: Deserialize<'de>, 58 | { 59 | from_value_with_config(value, Config::default()) 60 | } 61 | 62 | /// Generic value that can contain any value in our data format. It can be deserialized and 63 | /// serialized to be exactly as when serializing or deserializing with the given type. 64 | /// 65 | /// Note: [Clone]ing this value will not borrow from owned values. For that, you need to call 66 | /// [Value::borrow_clone]. 67 | #[derive(Debug, Clone, Default)] 68 | pub enum Value<'a> { 69 | /// Null / None / Unit type. 70 | #[default] 71 | Null, 72 | /// Bool value. 73 | Bool(bool), 74 | /// Integer value. 75 | Integer(Integer), 76 | /// Float value. 77 | Float(Float), 78 | /// Bytes value. 79 | Bytes(Cow<'a, [u8]>), 80 | /// String value. 81 | String(Cow<'a, str>), 82 | /// Sequence value. 83 | Array(VecDeque), 84 | /// Map value (ordered). 85 | Map(VecDeque<(Self, Self)>), 86 | } 87 | 88 | /// Wrapper for an owned value, i.e. `Value<'static>`. 89 | #[derive(Debug, Clone, Default)] 90 | pub struct OwnedValue(Value<'static>); 91 | 92 | /// The unsigned/signed integer value. 93 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 94 | pub enum Integer { 95 | /// Unsigned integer. 96 | Unsigned(u128), 97 | /// Signed integer. 98 | Signed(i128), 99 | } 100 | 101 | /// The float value with any precision. 102 | #[derive(Debug, Clone, Copy, PartialEq)] 103 | pub enum Float { 104 | /// 32-bit float. 105 | F32(f32), 106 | /// 64-bit float. 107 | F64(f64), 108 | } 109 | 110 | impl<'a> Value<'a> { 111 | /// Clone this value, borrowing the owned data where possible. 112 | pub fn borrow_clone(&self) -> Value<'_> { 113 | match self { 114 | Value::Null => Value::Null, 115 | Value::Bool(b) => Value::Bool(*b), 116 | Value::Integer(int) => Value::Integer(*int), 117 | Value::Float(float) => Value::Float(*float), 118 | Value::Bytes(bytes) => Value::Bytes(Cow::Borrowed(bytes)), 119 | Value::String(s) => Value::String(Cow::Borrowed(s)), 120 | Value::Array(arr) => Value::Array(arr.iter().map(Self::borrow_clone).collect()), 121 | Value::Map(map) => Value::Map( 122 | map.iter().map(|(key, value)| (key.borrow_clone(), value.borrow_clone())).collect(), 123 | ), 124 | } 125 | } 126 | 127 | /// Make value `'static` by cloning all borrowed data. 128 | #[must_use] 129 | pub fn into_owned(self) -> OwnedValue { 130 | let value = match self { 131 | Value::Null => Value::Null, 132 | Value::Bool(b) => Value::Bool(b), 133 | Value::Integer(int) => Value::Integer(int), 134 | Value::Float(float) => Value::Float(float), 135 | Value::Bytes(bytes) => Value::Bytes(bytes.into_owned().into()), 136 | Value::String(s) => Value::String(s.into_owned().into()), 137 | Value::Array(arr) => Value::Array(arr.into_iter().map(|v| v.into_owned().0).collect()), 138 | Value::Map(map) => Value::Map( 139 | map.into_iter() 140 | .map(|(key, value)| (key.into_owned().0, value.into_owned().0)) 141 | .collect(), 142 | ), 143 | }; 144 | OwnedValue(value) 145 | } 146 | 147 | /// Use this generic [Value] to deserialize into the given concrete type. 148 | #[inline] 149 | pub fn deserialize_as<'de, T>(self) -> Result 150 | where 151 | 'a: 'de, 152 | T: Deserialize<'de>, 153 | { 154 | T::deserialize(de::ValueDeserializer::new(self)) 155 | } 156 | 157 | /// Return whether the value is empty. This is the case if: 158 | /// - The value is [Value::Null]. 159 | /// - The value is [Value::Bytes] or [Value::String] of length 0. 160 | /// - The value is [Value::Array] or [Value::Map] without items. 161 | #[must_use] 162 | #[inline] 163 | pub fn is_empty(&self) -> bool { 164 | match self { 165 | Value::Null => true, 166 | Value::Bool(_) | Value::Integer(_) | Value::Float(_) => false, 167 | Value::Bytes(bytes) => bytes.is_empty(), 168 | Value::String(s) => s.is_empty(), 169 | Value::Array(arr) => arr.is_empty(), 170 | Value::Map(map) => map.is_empty(), 171 | } 172 | } 173 | 174 | /// Return the inner bool if this is a [Value::Bool]. 175 | #[must_use] 176 | pub const fn as_bool(&self) -> Option { 177 | if let Value::Bool(v) = self { Some(*v) } else { None } 178 | } 179 | 180 | /// Return the inner int if this is a [Value::Integer]. 181 | #[must_use] 182 | pub const fn as_int(&self) -> Option { 183 | if let Value::Integer(v) = self { Some(*v) } else { None } 184 | } 185 | 186 | /// Return the inner float if this is a [Value::Float]. 187 | #[must_use] 188 | pub const fn as_float(&self) -> Option { 189 | if let Value::Float(v) = self { Some(*v) } else { None } 190 | } 191 | 192 | /// Return the inner bytes if this is a [Value::Bytes]. 193 | #[must_use] 194 | #[expect(clippy::missing_const_for_fn, reason = "False positive")] 195 | pub fn as_bytes(&self) -> Option<&[u8]> { 196 | if let Value::Bytes(v) = self { Some(v) } else { None } 197 | } 198 | 199 | /// Return the inner string if this is a [Value::String]. 200 | #[must_use] 201 | #[expect(clippy::missing_const_for_fn, reason = "False positive")] 202 | pub fn as_string(&self) -> Option<&str> { 203 | if let Value::String(v) = self { Some(v) } else { None } 204 | } 205 | 206 | /// Return the inner array if this is a [Value::Array]. 207 | #[must_use] 208 | pub const fn as_array(&self) -> Option<&VecDeque>> { 209 | if let Value::Array(v) = self { Some(v) } else { None } 210 | } 211 | 212 | /// Return the inner map if this is a [Value::Map]. 213 | #[must_use] 214 | pub const fn as_map(&self) -> Option<&VecDeque<(Value<'a>, Value<'a>)>> { 215 | if let Value::Map(v) = self { Some(v) } else { None } 216 | } 217 | 218 | /// Iterate over the inner values if this is a [Value::Array] or [Value::Map]. 219 | #[must_use] 220 | pub fn into_values(self) -> Iter> { 221 | match self.into_owned().0 { 222 | Value::Array(arr) => Iter::new(arr.into_iter()), 223 | Value::Map(map) => Iter::new(map.into_iter().map(|(_key, value)| value)), 224 | _ => Iter::new(::core::iter::empty()), 225 | } 226 | } 227 | } 228 | 229 | impl OwnedValue { 230 | /// Create a new owned value. 231 | #[must_use] 232 | pub fn new(value: Value<'_>) -> Self { 233 | value.into_owned() 234 | } 235 | 236 | /// Return the inner value. 237 | #[must_use] 238 | pub fn into_inner(self) -> Value<'static> { 239 | self.0 240 | } 241 | } 242 | 243 | impl Deref for OwnedValue { 244 | type Target = Value<'static>; 245 | 246 | fn deref(&self) -> &Self::Target { 247 | &self.0 248 | } 249 | } 250 | 251 | impl DerefMut for OwnedValue { 252 | fn deref_mut(&mut self) -> &mut Self::Target { 253 | &mut self.0 254 | } 255 | } 256 | 257 | impl ::core::fmt::Display for Value<'_> { 258 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 259 | match self { 260 | Value::Null => f.write_str("null"), 261 | Value::Bool(b) if *b => f.write_str("true"), 262 | Value::Bool(_) => f.write_str("false"), 263 | Value::Integer(int) => ::core::fmt::Display::fmt(int, f), 264 | Value::Float(float) => ::core::fmt::Display::fmt(float, f), 265 | Value::Bytes(bytes) => { 266 | f.write_str("0x")?; 267 | for (i, byte) in bytes.iter().enumerate() { 268 | if i > 0 && i % 4 == 0 { 269 | f.write_char('_')?; 270 | } 271 | write!(f, "{byte:02X}")?; 272 | } 273 | Ok(()) 274 | } 275 | Value::String(s) => f.write_str(s), 276 | Value::Array(arr) => { 277 | f.write_char('[')?; 278 | for (i, value) in arr.iter().enumerate() { 279 | if i > 0 { 280 | f.write_str(", ")?; 281 | } 282 | ::core::fmt::Display::fmt(value, f)?; 283 | } 284 | f.write_char(']') 285 | } 286 | Value::Map(map) => { 287 | f.write_char('{')?; 288 | for (i, (key, value)) in map.iter().enumerate() { 289 | if i > 0 { 290 | f.write_str(", ")?; 291 | } 292 | ::core::fmt::Display::fmt(key, f)?; 293 | f.write_str(": ")?; 294 | ::core::fmt::Display::fmt(value, f)?; 295 | } 296 | f.write_char('}') 297 | } 298 | } 299 | } 300 | } 301 | 302 | impl ::core::fmt::Display for Integer { 303 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 304 | match self { 305 | Integer::Unsigned(int) => ::core::fmt::Display::fmt(int, f), 306 | Integer::Signed(int) => ::core::fmt::Display::fmt(int, f), 307 | } 308 | } 309 | } 310 | 311 | impl ::core::fmt::Display for Float { 312 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 313 | match self { 314 | Float::F32(float) => ::core::fmt::Display::fmt(float, f), 315 | Float::F64(float) => ::core::fmt::Display::fmt(float, f), 316 | } 317 | } 318 | } 319 | 320 | impl Serialize for Value<'_> { 321 | fn serialize(&self, serializer: S) -> Result 322 | where 323 | S: ::serde::Serializer, 324 | { 325 | match self { 326 | Value::Null => serializer.serialize_none(), 327 | Value::Bool(b) => serializer.serialize_bool(*b), 328 | Value::Integer(Integer::Unsigned(int)) => serializer.serialize_u128(*int), 329 | Value::Integer(Integer::Signed(int)) => serializer.serialize_i128(*int), 330 | Value::Float(Float::F32(float)) => serializer.serialize_f32(*float), 331 | Value::Float(Float::F64(float)) => serializer.serialize_f64(*float), 332 | Value::Bytes(bytes) => serializer.serialize_bytes(bytes), 333 | Value::String(s) => serializer.serialize_str(s), 334 | Value::Array(arr) => { 335 | use ::serde::ser::SerializeSeq; 336 | 337 | let mut ser = serializer.serialize_seq(Some(arr.len()))?; 338 | for value in arr { 339 | ser.serialize_element(value)?; 340 | } 341 | ser.end() 342 | } 343 | Value::Map(map) => { 344 | use ::serde::ser::SerializeMap; 345 | 346 | let mut ser = serializer.serialize_map(Some(map.len()))?; 347 | for (key, value) in map { 348 | ser.serialize_entry(key, value)?; 349 | } 350 | ser.end() 351 | } 352 | } 353 | } 354 | } 355 | 356 | impl<'de> Deserialize<'de> for Value<'de> { 357 | #[inline] 358 | fn deserialize(deserializer: D) -> Result 359 | where 360 | D: serde::Deserializer<'de>, 361 | { 362 | deserializer.deserialize_any(ValueVisitor::default()) 363 | } 364 | } 365 | 366 | impl<'de> Deserialize<'de> for OwnedValue { 367 | #[inline] 368 | fn deserialize(deserializer: D) -> Result 369 | where 370 | D: serde::Deserializer<'de>, 371 | { 372 | deserializer.deserialize_any(ValueVisitor::default()).map(Value::into_owned) 373 | } 374 | } 375 | 376 | /// Serde [Visitor] for deserializing a [Value]. 377 | #[derive(Debug, Clone, Copy, Default)] 378 | struct ValueVisitor<'a>(PhantomData>); 379 | 380 | impl<'de> ::serde::de::Visitor<'de> for ValueVisitor<'de> { 381 | type Value = Value<'de>; 382 | 383 | #[inline] 384 | fn expecting(&self, formatter: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { 385 | formatter.write_str("any value") 386 | } 387 | 388 | #[inline] 389 | fn visit_bool(self, v: bool) -> Result 390 | where 391 | E: serde::de::Error, 392 | { 393 | Ok(Value::Bool(v)) 394 | } 395 | 396 | #[inline] 397 | fn visit_i64(self, v: i64) -> Result 398 | where 399 | E: serde::de::Error, 400 | { 401 | self.visit_i128(i128::from(v)) 402 | } 403 | 404 | #[inline] 405 | fn visit_i128(self, v: i128) -> Result 406 | where 407 | E: serde::de::Error, 408 | { 409 | Ok(Value::Integer(Integer::Signed(v))) 410 | } 411 | 412 | #[inline] 413 | fn visit_u64(self, v: u64) -> Result 414 | where 415 | E: serde::de::Error, 416 | { 417 | self.visit_u128(u128::from(v)) 418 | } 419 | 420 | #[inline] 421 | fn visit_u128(self, v: u128) -> Result 422 | where 423 | E: serde::de::Error, 424 | { 425 | Ok(Value::Integer(Integer::Unsigned(v))) 426 | } 427 | 428 | #[inline] 429 | fn visit_f32(self, v: f32) -> Result 430 | where 431 | E: serde::de::Error, 432 | { 433 | Ok(Value::Float(Float::F32(v))) 434 | } 435 | 436 | #[inline] 437 | fn visit_f64(self, v: f64) -> Result 438 | where 439 | E: serde::de::Error, 440 | { 441 | Ok(Value::Float(Float::F64(v))) 442 | } 443 | 444 | #[inline] 445 | fn visit_str(self, v: &str) -> Result 446 | where 447 | E: serde::de::Error, 448 | { 449 | Ok(Value::String(Cow::Owned(v.to_owned()))) 450 | } 451 | 452 | #[inline] 453 | fn visit_borrowed_str(self, v: &'de str) -> Result 454 | where 455 | E: serde::de::Error, 456 | { 457 | Ok(Value::String(Cow::Borrowed(v))) 458 | } 459 | 460 | #[inline] 461 | fn visit_string(self, v: String) -> Result 462 | where 463 | E: serde::de::Error, 464 | { 465 | Ok(Value::String(Cow::Owned(v))) 466 | } 467 | 468 | #[inline] 469 | fn visit_bytes(self, v: &[u8]) -> Result 470 | where 471 | E: serde::de::Error, 472 | { 473 | Ok(Value::Bytes(Cow::Owned(v.to_vec()))) 474 | } 475 | 476 | #[inline] 477 | fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result 478 | where 479 | E: serde::de::Error, 480 | { 481 | Ok(Value::Bytes(Cow::Borrowed(v))) 482 | } 483 | 484 | #[inline] 485 | fn visit_byte_buf(self, v: Vec) -> Result 486 | where 487 | E: serde::de::Error, 488 | { 489 | Ok(Value::Bytes(Cow::Owned(v))) 490 | } 491 | 492 | #[inline] 493 | fn visit_none(self) -> Result 494 | where 495 | E: serde::de::Error, 496 | { 497 | Ok(Value::Null) 498 | } 499 | 500 | #[inline] 501 | fn visit_some(self, deserializer: D) -> Result 502 | where 503 | D: serde::Deserializer<'de>, 504 | { 505 | deserializer.deserialize_any(self) 506 | } 507 | 508 | #[inline] 509 | fn visit_unit(self) -> Result 510 | where 511 | E: serde::de::Error, 512 | { 513 | Ok(Value::Null) 514 | } 515 | 516 | #[inline] 517 | fn visit_seq(self, mut seq: A) -> Result 518 | where 519 | A: serde::de::SeqAccess<'de>, 520 | { 521 | let mut arr = seq.size_hint().map_or_else(VecDeque::new, VecDeque::with_capacity); 522 | 523 | while let Some(value) = seq.next_element()? { 524 | arr.push_back(value); 525 | } 526 | 527 | Ok(Value::Array(arr)) 528 | } 529 | 530 | #[inline] 531 | fn visit_map(self, mut map: A) -> Result 532 | where 533 | A: serde::de::MapAccess<'de>, 534 | { 535 | let mut entries = map.size_hint().map_or_else(VecDeque::new, VecDeque::with_capacity); 536 | 537 | while let Some((key, value)) = map.next_entry()? { 538 | entries.push_back((key, value)); 539 | } 540 | 541 | Ok(Value::Map(entries)) 542 | } 543 | } 544 | 545 | impl PartialEq for Value<'_> { 546 | fn eq(&self, other: &Self) -> bool { 547 | match (self, other) { 548 | (Self::Null, Self::Null) => true, 549 | (Self::Bool(l0), Self::Bool(r0)) => l0 == r0, 550 | (Self::Integer(l0), Self::Integer(r0)) => l0 == r0, 551 | (Self::Float(l0), Self::Float(r0)) => l0 == r0, 552 | (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0, 553 | (Self::String(l0), Self::String(r0)) => l0 == r0, 554 | (Self::Array(l0), Self::Array(r0)) => l0 == r0, 555 | (Self::Map(l0), Self::Map(r0)) => l0 == r0, 556 | _ => false, 557 | } 558 | } 559 | } 560 | 561 | impl PartialEq for Value<'_> { 562 | fn eq(&self, other: &bool) -> bool { 563 | match self { 564 | Value::Bool(b) => b == other, 565 | _ => false, 566 | } 567 | } 568 | } 569 | 570 | impl PartialEq for Value<'_> { 571 | fn eq(&self, other: &u128) -> bool { 572 | match self { 573 | Value::Integer(Integer::Unsigned(int)) => int == other, 574 | _ => false, 575 | } 576 | } 577 | } 578 | 579 | impl PartialEq for Value<'_> { 580 | fn eq(&self, other: &i128) -> bool { 581 | match self { 582 | Value::Integer(Integer::Signed(int)) => int == other, 583 | _ => false, 584 | } 585 | } 586 | } 587 | 588 | impl PartialEq for Value<'_> { 589 | fn eq(&self, other: &f32) -> bool { 590 | match self { 591 | Value::Float(Float::F32(float)) => float == other, 592 | Value::Float(Float::F64(float)) => *float == f64::from(*other), 593 | _ => false, 594 | } 595 | } 596 | } 597 | 598 | impl PartialEq for Value<'_> { 599 | fn eq(&self, other: &f64) -> bool { 600 | match self { 601 | Value::Float(Float::F32(float)) => f64::from(*float) == *other, 602 | Value::Float(Float::F64(float)) => float == other, 603 | _ => false, 604 | } 605 | } 606 | } 607 | 608 | impl PartialEq<[u8]> for Value<'_> { 609 | fn eq(&self, other: &[u8]) -> bool { 610 | match self { 611 | Value::Bytes(bytes) => bytes.as_ref() == other, 612 | _ => false, 613 | } 614 | } 615 | } 616 | 617 | impl PartialEq for Value<'_> { 618 | fn eq(&self, other: &str) -> bool { 619 | match self { 620 | Value::String(s) => s == other, 621 | _ => false, 622 | } 623 | } 624 | } 625 | 626 | impl<'a> From>> for Value<'a> { 627 | #[inline] 628 | fn from(value: Option>) -> Self { 629 | value.unwrap_or_else(|| Value::Null) 630 | } 631 | } 632 | 633 | impl From<()> for Value<'_> { 634 | #[inline] 635 | fn from((): ()) -> Self { 636 | Value::Null 637 | } 638 | } 639 | 640 | impl From for Value<'_> { 641 | #[inline] 642 | fn from(value: bool) -> Self { 643 | Value::Bool(value) 644 | } 645 | } 646 | 647 | impl From for Value<'_> { 648 | #[inline] 649 | fn from(value: u8) -> Self { 650 | Value::Integer(Integer::Unsigned(u128::from(value))) 651 | } 652 | } 653 | 654 | impl From for Value<'_> { 655 | #[inline] 656 | fn from(value: i8) -> Self { 657 | Value::Integer(Integer::Signed(i128::from(value))) 658 | } 659 | } 660 | 661 | impl From for Value<'_> { 662 | #[inline] 663 | fn from(value: u16) -> Self { 664 | Value::Integer(Integer::Unsigned(u128::from(value))) 665 | } 666 | } 667 | 668 | impl From for Value<'_> { 669 | #[inline] 670 | fn from(value: i16) -> Self { 671 | Value::Integer(Integer::Signed(i128::from(value))) 672 | } 673 | } 674 | 675 | impl From for Value<'_> { 676 | #[inline] 677 | fn from(value: u32) -> Self { 678 | Value::Integer(Integer::Unsigned(u128::from(value))) 679 | } 680 | } 681 | 682 | impl From for Value<'_> { 683 | #[inline] 684 | fn from(value: i32) -> Self { 685 | Value::Integer(Integer::Signed(i128::from(value))) 686 | } 687 | } 688 | 689 | impl From for Value<'_> { 690 | #[inline] 691 | fn from(value: u64) -> Self { 692 | Value::Integer(Integer::Unsigned(u128::from(value))) 693 | } 694 | } 695 | 696 | impl From for Value<'_> { 697 | #[inline] 698 | fn from(value: i64) -> Self { 699 | Value::Integer(Integer::Signed(i128::from(value))) 700 | } 701 | } 702 | 703 | impl From for Value<'_> { 704 | #[inline] 705 | fn from(value: usize) -> Self { 706 | Value::Integer(Integer::Unsigned(value as u128)) 707 | } 708 | } 709 | 710 | impl From for Value<'_> { 711 | #[inline] 712 | fn from(value: isize) -> Self { 713 | Value::Integer(Integer::Signed(value as i128)) 714 | } 715 | } 716 | 717 | impl From for Value<'_> { 718 | #[inline] 719 | fn from(value: u128) -> Self { 720 | Value::Integer(Integer::Unsigned(value)) 721 | } 722 | } 723 | 724 | impl From for Value<'_> { 725 | #[inline] 726 | fn from(value: i128) -> Self { 727 | Value::Integer(Integer::Signed(value)) 728 | } 729 | } 730 | 731 | impl From for Value<'_> { 732 | #[inline] 733 | fn from(value: f32) -> Self { 734 | Value::Float(Float::F32(value)) 735 | } 736 | } 737 | 738 | impl From for Value<'_> { 739 | #[inline] 740 | fn from(value: f64) -> Self { 741 | Value::Float(Float::F64(value)) 742 | } 743 | } 744 | 745 | impl<'a> From<&'a [u8]> for Value<'a> { 746 | #[inline] 747 | fn from(value: &'a [u8]) -> Self { 748 | Value::Bytes(Cow::Borrowed(value)) 749 | } 750 | } 751 | 752 | impl From> for Value<'_> { 753 | #[inline] 754 | fn from(value: Vec) -> Self { 755 | Value::Bytes(Cow::Owned(value)) 756 | } 757 | } 758 | 759 | impl<'a> From> for Value<'a> { 760 | #[inline] 761 | fn from(value: Cow<'a, [u8]>) -> Self { 762 | Value::Bytes(value) 763 | } 764 | } 765 | 766 | impl<'a> From<&'a str> for Value<'a> { 767 | #[inline] 768 | fn from(value: &'a str) -> Self { 769 | Value::String(Cow::Borrowed(value)) 770 | } 771 | } 772 | 773 | impl From for Value<'_> { 774 | #[inline] 775 | fn from(value: String) -> Self { 776 | Value::String(Cow::Owned(value)) 777 | } 778 | } 779 | 780 | impl<'a> From> for Value<'a> { 781 | #[inline] 782 | fn from(value: Cow<'a, str>) -> Self { 783 | Value::String(value) 784 | } 785 | } 786 | 787 | impl<'a> From>> for Value<'a> { 788 | #[inline] 789 | fn from(value: VecDeque>) -> Self { 790 | Value::Array(value) 791 | } 792 | } 793 | 794 | impl<'a> From, Value<'a>)>> for Value<'a> { 795 | #[inline] 796 | fn from(value: VecDeque<(Value<'a>, Value<'a>)>) -> Self { 797 | Value::Map(value) 798 | } 799 | } 800 | 801 | impl<'a, T> FromIterator for Value<'a> 802 | where 803 | T: Into>, 804 | { 805 | #[inline] 806 | fn from_iter>(iter: I) -> Self { 807 | Self::from(iter.into_iter().map(Into::into).collect::>()) 808 | } 809 | } 810 | 811 | impl<'a, K, V> FromIterator<(K, V)> for Value<'a> 812 | where 813 | K: Into>, 814 | V: Into>, 815 | { 816 | #[inline] 817 | fn from_iter>(iter: I) -> Self { 818 | Self::from(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect::>()) 819 | } 820 | } 821 | 822 | /// Trait for trait objects of exact size iterators. 823 | trait AllIteratorTrait: 824 | Iterator + ExactSizeIterator + DoubleEndedIterator + ::core::fmt::Debug 825 | { 826 | } 827 | impl AllIteratorTrait for I where 828 | I: Iterator + ExactSizeIterator + DoubleEndedIterator + ::core::fmt::Debug 829 | { 830 | } 831 | 832 | /// Generic iterator. 833 | #[derive(Debug)] 834 | pub struct Iter(Box + Send + Sync>); 835 | 836 | impl Iter { 837 | /// Create a new generic iterator. 838 | fn new(iter: I) -> Self 839 | where 840 | I: AllIteratorTrait + Send + Sync + 'static, 841 | { 842 | Self(Box::new(iter)) 843 | } 844 | } 845 | 846 | impl Iterator for Iter { 847 | type Item = T; 848 | 849 | fn next(&mut self) -> Option { 850 | self.0.next() 851 | } 852 | 853 | fn size_hint(&self) -> (usize, Option) { 854 | self.0.size_hint() 855 | } 856 | 857 | fn count(self) -> usize 858 | where 859 | Self: Sized, 860 | { 861 | self.0.count() 862 | } 863 | 864 | fn last(self) -> Option 865 | where 866 | Self: Sized, 867 | { 868 | self.0.last() 869 | } 870 | 871 | fn nth(&mut self, n: usize) -> Option { 872 | self.0.nth(n) 873 | } 874 | } 875 | 876 | impl ExactSizeIterator for Iter { 877 | fn len(&self) -> usize { 878 | self.0.len() 879 | } 880 | } 881 | 882 | impl DoubleEndedIterator for Iter { 883 | fn next_back(&mut self) -> Option { 884 | self.0.next_back() 885 | } 886 | 887 | fn nth_back(&mut self, n: usize) -> Option { 888 | self.0.nth_back(n) 889 | } 890 | } 891 | 892 | 893 | #[cfg(test)] 894 | mod tests; 895 | -------------------------------------------------------------------------------- /src/value/ser.rs: -------------------------------------------------------------------------------- 1 | //! Serialization from any type into a [Value]. 2 | #![cfg_attr( 3 | feature = "tracing", 4 | allow(clippy::used_underscore_binding, reason = "Only used in tracing::instrument") 5 | )] 6 | 7 | use ::alloc::{borrow::ToOwned, vec}; 8 | 9 | use super::*; 10 | use crate::{Error, Result}; 11 | 12 | /// Serializer to serialize any type into a [Value]. 13 | #[derive(Debug, Clone, Copy)] 14 | pub struct ValueSerializer { 15 | /// Whether to use the `use_indices` format. 16 | use_indices: bool, 17 | } 18 | 19 | impl ValueSerializer { 20 | /// Create a new serializer. 21 | #[must_use] 22 | pub const fn new(use_indices: bool) -> Self { 23 | Self { use_indices } 24 | } 25 | } 26 | 27 | impl ::serde::ser::Serializer for ValueSerializer { 28 | type Ok = Value<'static>; 29 | type Error = Error; 30 | type SerializeSeq = ValueSeqSerializer; 31 | type SerializeTuple = ValueSeqSerializer; 32 | type SerializeTupleStruct = ValueSeqSerializer; 33 | type SerializeTupleVariant = ValueSeqVariantSerializer; 34 | type SerializeMap = ValueMapSerializer; 35 | type SerializeStruct = ValueMapSerializer; 36 | type SerializeStructVariant = ValueMapVariantSerializer; 37 | 38 | #[inline] 39 | fn is_human_readable(&self) -> bool { 40 | true 41 | } 42 | 43 | #[inline] 44 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 45 | fn serialize_bool(self, v: bool) -> Result { 46 | Ok(Value::Bool(v)) 47 | } 48 | 49 | #[inline] 50 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 51 | fn serialize_i8(self, v: i8) -> Result { 52 | Ok(Value::Integer(Integer::Signed(i128::from(v)))) 53 | } 54 | 55 | #[inline] 56 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 57 | fn serialize_i16(self, v: i16) -> Result { 58 | Ok(Value::Integer(Integer::Signed(i128::from(v)))) 59 | } 60 | 61 | #[inline] 62 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 63 | fn serialize_i32(self, v: i32) -> Result { 64 | Ok(Value::Integer(Integer::Signed(i128::from(v)))) 65 | } 66 | 67 | #[inline] 68 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 69 | fn serialize_i64(self, v: i64) -> Result { 70 | Ok(Value::Integer(Integer::Signed(i128::from(v)))) 71 | } 72 | 73 | #[inline] 74 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 75 | fn serialize_u8(self, v: u8) -> Result { 76 | Ok(Value::Integer(Integer::Unsigned(u128::from(v)))) 77 | } 78 | 79 | #[inline] 80 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 81 | fn serialize_u16(self, v: u16) -> Result { 82 | Ok(Value::Integer(Integer::Unsigned(u128::from(v)))) 83 | } 84 | 85 | #[inline] 86 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 87 | fn serialize_u32(self, v: u32) -> Result { 88 | Ok(Value::Integer(Integer::Unsigned(u128::from(v)))) 89 | } 90 | 91 | #[inline] 92 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 93 | fn serialize_u64(self, v: u64) -> Result { 94 | Ok(Value::Integer(Integer::Unsigned(u128::from(v)))) 95 | } 96 | 97 | #[inline] 98 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 99 | fn serialize_i128(self, v: i128) -> Result { 100 | Ok(Value::Integer(Integer::Signed(v))) 101 | } 102 | 103 | #[inline] 104 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 105 | fn serialize_u128(self, v: u128) -> Result { 106 | Ok(Value::Integer(Integer::Unsigned(v))) 107 | } 108 | 109 | #[inline] 110 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 111 | fn serialize_f32(self, v: f32) -> Result { 112 | Ok(Value::Float(Float::F32(v))) 113 | } 114 | 115 | #[inline] 116 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 117 | fn serialize_f64(self, v: f64) -> Result { 118 | Ok(Value::Float(Float::F64(v))) 119 | } 120 | 121 | #[inline] 122 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 123 | fn serialize_char(self, v: char) -> Result { 124 | let mut buffer = [0; 4]; 125 | let s = v.encode_utf8(&mut buffer); 126 | self.serialize_str(s) 127 | } 128 | 129 | #[inline] 130 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 131 | fn serialize_str(self, v: &str) -> Result { 132 | Ok(Value::String(Cow::Owned(v.to_owned()))) 133 | } 134 | 135 | #[inline] 136 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 137 | fn serialize_bytes(self, v: &[u8]) -> Result { 138 | Ok(Value::Bytes(Cow::Owned(v.to_owned()))) 139 | } 140 | 141 | #[inline] 142 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 143 | fn serialize_none(self) -> Result { 144 | Ok(Value::Null) 145 | } 146 | 147 | #[inline] 148 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 149 | fn serialize_some(self, value: &T) -> Result 150 | where 151 | T: ?Sized + serde::Serialize, 152 | { 153 | value.serialize(self) 154 | } 155 | 156 | #[inline] 157 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 158 | fn serialize_unit(self) -> Result { 159 | Ok(Value::Null) 160 | } 161 | 162 | #[inline] 163 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 164 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 165 | Ok(Value::Null) 166 | } 167 | 168 | #[inline] 169 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 170 | fn serialize_unit_variant( 171 | self, 172 | _name: &'static str, 173 | variant_index: u32, 174 | variant: &'static str, 175 | ) -> Result { 176 | if self.use_indices { 177 | Ok(Value::Integer(Integer::Unsigned(u128::from(variant_index)))) 178 | } else { 179 | Ok(Value::String(Cow::Borrowed(variant))) 180 | } 181 | } 182 | 183 | #[inline] 184 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 185 | fn serialize_newtype_struct( 186 | self, 187 | _name: &'static str, 188 | value: &T, 189 | ) -> Result 190 | where 191 | T: ?Sized + serde::Serialize, 192 | { 193 | value.serialize(self) 194 | } 195 | 196 | #[inline] 197 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 198 | fn serialize_newtype_variant( 199 | self, 200 | _name: &'static str, 201 | variant_index: u32, 202 | variant: &'static str, 203 | value: &T, 204 | ) -> Result 205 | where 206 | T: ?Sized + serde::Serialize, 207 | { 208 | let key = if self.use_indices { 209 | Value::Integer(Integer::Unsigned(u128::from(variant_index))) 210 | } else { 211 | Value::String(Cow::Borrowed(variant)) 212 | }; 213 | let value = value.serialize(self)?; 214 | Ok(Value::Map(vec![(key, value)].into())) 215 | } 216 | 217 | #[inline] 218 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 219 | fn serialize_seq(self, len: Option) -> Result { 220 | let arr = len.map_or_else(VecDeque::new, VecDeque::with_capacity); 221 | Ok(ValueSeqSerializer { serializer: self, arr }) 222 | } 223 | 224 | #[inline] 225 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 226 | fn serialize_tuple(self, len: usize) -> Result { 227 | let arr = VecDeque::with_capacity(len); 228 | Ok(ValueSeqSerializer { serializer: self, arr }) 229 | } 230 | 231 | #[inline] 232 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 233 | fn serialize_tuple_struct( 234 | self, 235 | _name: &'static str, 236 | len: usize, 237 | ) -> Result { 238 | let arr = VecDeque::with_capacity(len); 239 | Ok(ValueSeqSerializer { serializer: self, arr }) 240 | } 241 | 242 | #[inline] 243 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 244 | fn serialize_tuple_variant( 245 | self, 246 | _name: &'static str, 247 | variant_index: u32, 248 | variant: &'static str, 249 | len: usize, 250 | ) -> Result { 251 | let key = if self.use_indices { 252 | Value::Integer(Integer::Unsigned(u128::from(variant_index))) 253 | } else { 254 | Value::String(Cow::Borrowed(variant)) 255 | }; 256 | Ok(ValueSeqVariantSerializer { serializer: self, key, arr: VecDeque::with_capacity(len) }) 257 | } 258 | 259 | #[inline] 260 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 261 | fn serialize_map(self, len: Option) -> Result { 262 | let map = len.map_or_else(VecDeque::new, VecDeque::with_capacity); 263 | Ok(ValueMapSerializer { serializer: self, map, field_index: 0 }) 264 | } 265 | 266 | #[inline] 267 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 268 | fn serialize_struct( 269 | self, 270 | _name: &'static str, 271 | len: usize, 272 | ) -> Result { 273 | let map = VecDeque::with_capacity(len); 274 | Ok(ValueMapSerializer { serializer: self, map, field_index: 0 }) 275 | } 276 | 277 | #[inline] 278 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 279 | fn serialize_struct_variant( 280 | self, 281 | _name: &'static str, 282 | variant_index: u32, 283 | variant: &'static str, 284 | len: usize, 285 | ) -> Result { 286 | let key = if self.use_indices { 287 | Value::Integer(Integer::Unsigned(u128::from(variant_index))) 288 | } else { 289 | Value::String(Cow::Borrowed(variant)) 290 | }; 291 | Ok(ValueMapVariantSerializer { 292 | serializer: self, 293 | key, 294 | map: VecDeque::with_capacity(len), 295 | field_index: 0, 296 | }) 297 | } 298 | } 299 | 300 | /// Serializer for a sequence of values. 301 | #[derive(Debug)] 302 | pub struct ValueSeqSerializer { 303 | /// Serializer. 304 | serializer: ValueSerializer, 305 | /// Values. 306 | arr: VecDeque>, 307 | } 308 | 309 | impl ::serde::ser::SerializeSeq for ValueSeqSerializer { 310 | type Ok = Value<'static>; 311 | type Error = Error; 312 | 313 | #[inline] 314 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 315 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 316 | where 317 | T: ?Sized + Serialize, 318 | { 319 | self.arr.push_back(value.serialize(self.serializer)?); 320 | Ok(()) 321 | } 322 | 323 | #[inline] 324 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 325 | fn end(self) -> Result { 326 | Ok(Value::Array(self.arr)) 327 | } 328 | } 329 | 330 | impl ::serde::ser::SerializeTuple for ValueSeqSerializer { 331 | type Ok = Value<'static>; 332 | type Error = Error; 333 | 334 | #[inline] 335 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 336 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 337 | where 338 | T: ?Sized + Serialize, 339 | { 340 | self.arr.push_back(value.serialize(self.serializer)?); 341 | Ok(()) 342 | } 343 | 344 | #[inline] 345 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 346 | fn end(self) -> Result { 347 | Ok(Value::Array(self.arr)) 348 | } 349 | } 350 | 351 | impl ::serde::ser::SerializeTupleStruct for ValueSeqSerializer { 352 | type Ok = Value<'static>; 353 | type Error = Error; 354 | 355 | #[inline] 356 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 357 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 358 | where 359 | T: ?Sized + Serialize, 360 | { 361 | self.arr.push_back(value.serialize(self.serializer)?); 362 | Ok(()) 363 | } 364 | 365 | #[inline] 366 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 367 | fn end(self) -> Result { 368 | Ok(Value::Array(self.arr)) 369 | } 370 | } 371 | 372 | /// Serializer for a map of keys and values. 373 | #[derive(Debug)] 374 | pub struct ValueMapSerializer { 375 | /// Serializer. 376 | serializer: ValueSerializer, 377 | /// Values. 378 | map: VecDeque<(Value<'static>, Value<'static>)>, 379 | /// The current field index. 380 | field_index: u32, 381 | } 382 | 383 | impl ::serde::ser::SerializeMap for ValueMapSerializer { 384 | type Ok = Value<'static>; 385 | type Error = Error; 386 | 387 | #[inline] 388 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 389 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 390 | where 391 | T: ?Sized + Serialize, 392 | { 393 | let key = key.serialize(self.serializer)?; 394 | self.map.push_back((key, Value::Null)); 395 | Ok(()) 396 | } 397 | 398 | #[inline] 399 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 400 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 401 | where 402 | T: ?Sized + Serialize, 403 | { 404 | #![expect(clippy::expect_used, reason = "Serde requirement")] 405 | self.map.back_mut().expect("serialize_key is called before serialize_value").1 = 406 | value.serialize(self.serializer)?; 407 | Ok(()) 408 | } 409 | 410 | #[inline] 411 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 412 | fn end(self) -> Result { 413 | Ok(Value::Map(self.map)) 414 | } 415 | } 416 | 417 | impl ::serde::ser::SerializeStruct for ValueMapSerializer { 418 | type Ok = Value<'static>; 419 | type Error = Error; 420 | 421 | #[inline] 422 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 423 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 424 | where 425 | T: ?Sized + Serialize, 426 | { 427 | let key = if self.serializer.use_indices { 428 | Value::Integer(Integer::Unsigned(u128::from(self.field_index))) 429 | } else { 430 | Value::String(Cow::Borrowed(key)) 431 | }; 432 | self.field_index += 1; 433 | let value = value.serialize(self.serializer)?; 434 | self.map.push_back((key, value)); 435 | Ok(()) 436 | } 437 | 438 | #[inline] 439 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 440 | fn end(self) -> Result { 441 | Ok(Value::Map(self.map)) 442 | } 443 | 444 | #[inline] 445 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 446 | fn skip_field(&mut self, _key: &'static str) -> Result<(), Self::Error> { 447 | self.field_index += 1; 448 | Ok(()) 449 | } 450 | } 451 | 452 | /// Serializer for sequence variants in enums. 453 | #[derive(Debug)] 454 | pub struct ValueSeqVariantSerializer { 455 | /// Serializer. 456 | serializer: ValueSerializer, 457 | /// Variant key. 458 | key: Value<'static>, 459 | /// Values. 460 | arr: VecDeque>, 461 | } 462 | 463 | impl ::serde::ser::SerializeTupleVariant for ValueSeqVariantSerializer { 464 | type Ok = Value<'static>; 465 | type Error = Error; 466 | 467 | #[inline] 468 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 469 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 470 | where 471 | T: ?Sized + Serialize, 472 | { 473 | self.arr.push_back(value.serialize(self.serializer)?); 474 | Ok(()) 475 | } 476 | 477 | #[inline] 478 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip_all))] 479 | fn end(self) -> Result { 480 | Ok(Value::Map(vec![(self.key, Value::Array(self.arr))].into())) 481 | } 482 | } 483 | 484 | /// Serializer for map variants in enums. 485 | #[derive(Debug)] 486 | pub struct ValueMapVariantSerializer { 487 | /// Serializer. 488 | serializer: ValueSerializer, 489 | /// Variant key. 490 | key: Value<'static>, 491 | /// Values. 492 | map: VecDeque<(Value<'static>, Value<'static>)>, 493 | /// The current field index. 494 | field_index: u32, 495 | } 496 | 497 | impl ::serde::ser::SerializeStructVariant for ValueMapVariantSerializer { 498 | type Ok = Value<'static>; 499 | type Error = Error; 500 | 501 | #[inline] 502 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self, value)))] 503 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 504 | where 505 | T: ?Sized + Serialize, 506 | { 507 | let key = if self.serializer.use_indices { 508 | Value::Integer(Integer::Unsigned(u128::from(self.field_index))) 509 | } else { 510 | Value::String(Cow::Borrowed(key)) 511 | }; 512 | self.field_index += 1; 513 | let value = value.serialize(self.serializer)?; 514 | self.map.push_back((key, value)); 515 | Ok(()) 516 | } 517 | 518 | #[inline] 519 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 520 | fn end(self) -> Result { 521 | Ok(Value::Map(vec![(self.key, Value::Map(self.map))].into())) 522 | } 523 | 524 | #[inline] 525 | #[cfg_attr(feature = "tracing", ::tracing::instrument(skip(self)))] 526 | fn skip_field(&mut self, _key: &'static str) -> Result<(), Self::Error> { 527 | self.field_index += 1; 528 | Ok(()) 529 | } 530 | } 531 | -------------------------------------------------------------------------------- /tests/all/json_data.rs: -------------------------------------------------------------------------------- 1 | //! Test with JSON blobs. 2 | #![cfg(feature = "std")] 3 | 4 | use ::serde_brief::value::Value; 5 | 6 | fn roundtrip(value: Value<'_>) { 7 | let bytes = serde_brief::to_vec(&value).expect("serializing"); 8 | let parsed: Value<'_> = serde_brief::from_slice(&bytes).expect("deserializing"); 9 | assert_eq!(parsed, value); 10 | } 11 | 12 | #[test] 13 | fn test_json_blobs() { 14 | for entry in std::fs::read_dir("./tests/data").expect("finding test data") { 15 | let entry = entry.expect("getting directory entry"); 16 | let file = entry.path(); 17 | if file.ends_with(".json") { 18 | println!("Testing `{}`", file.display()); 19 | let json = std::fs::read_to_string(file).expect("reading JSON file"); 20 | let value: Value = serde_json::from_str(&json).expect("parsing JSON"); 21 | roundtrip(value); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/all/main.rs: -------------------------------------------------------------------------------- 1 | //! All integration tests go in this folder to speed up compilation. 2 | #![allow( 3 | clippy::tests_outside_test_module, 4 | clippy::unwrap_used, 5 | clippy::expect_used, 6 | clippy::print_stdout, 7 | reason = "Tests" 8 | )] 9 | 10 | mod json_data; 11 | -------------------------------------------------------------------------------- /tests/data/json-edge-cases.json: -------------------------------------------------------------------------------- 1 | { 2 | "resourceType": "Patient", 3 | "identifier": [ 4 | { 5 | "period": { 6 | "start": "2001-05-06" 7 | }, 8 | "assigner": { 9 | "display": "Acme Healthcare" 10 | }, 11 | "use": "usual", 12 | "system": "urn:oid:1.2.36.146.595.217.0.1", 13 | "value": "12345" 14 | } 15 | ], 16 | "managingOrganization": { 17 | "reference": "Organization/1" 18 | }, 19 | "_active": { 20 | "extension": [ 21 | { 22 | "url": "http://example.org/fhir/StructureDefinition/recordStatus", 23 | "valueCode": "archived" 24 | } 25 | ] 26 | }, 27 | "name": [ 28 | { 29 | "given": [ 30 | "Peter", 31 | "James" 32 | ], 33 | "use": "official", 34 | "family": "Chalmers" 35 | }, 36 | { 37 | "given": [ 38 | "Jim" 39 | ], 40 | "use": "usual" 41 | } 42 | ], 43 | "extension": [ 44 | { 45 | "url": "http://example.org/fhir/StructureDefinition/patientAvatar", 46 | "valueReference": { 47 | "reference": "#pic1", 48 | "display": "Duck image" 49 | } 50 | }, 51 | { 52 | "url": "http://example.org/fhir/StructureDefinition/complexExtensionExample", 53 | "extension": [ 54 | { 55 | "url": "nestedA", 56 | "valueCoding": { 57 | "system": "http://demo.org/id/4", 58 | "code": "AB45", 59 | "extension": [ 60 | { 61 | "url": "http://example.org/fhir/StructureDefinition/extraforcodingWithExt", 62 | "extension": [ 63 | { 64 | "url": "extra1", 65 | "valueString": "extra info" 66 | } 67 | ] 68 | }, 69 | { 70 | "url": "http://example.org/fhir/StructureDefinition/extraforcodingWithValue", 71 | "valueInteger": 45 72 | } 73 | ] 74 | } 75 | }, 76 | { 77 | "url": "nestedB", 78 | "id": "q4", 79 | "extension": [ 80 | { 81 | "url": "nestedB1", 82 | "valueString": "hello" 83 | } 84 | ] 85 | } 86 | ] 87 | } 88 | ], 89 | "modifierExtension": [ 90 | { 91 | "url": "http://example.org/fhir/StructureDefinition/pi", 92 | "valueDecimal": 3.141592653589793 93 | }, 94 | { 95 | "url": "http://example.org/fhir/StructureDefinition/max-decimal-precision", 96 | "valueDecimal": 1.00065022141624642 97 | } 98 | ], 99 | "gender": "male", 100 | "birthDate": "1974-12", 101 | "deceasedBoolean": true, 102 | "address": [ 103 | { 104 | "use": "home", 105 | "line": [ 106 | "534 Erewhon St" 107 | ], 108 | "city": "PleasantVille", 109 | "state": "Vic", 110 | "postalCode": "3999" 111 | } 112 | ], 113 | "maritalStatus": { 114 | "extension": [ 115 | { 116 | "url": "http://example.org/fhir/StructureDefinition/nullFlavor", 117 | "valueCode": "ASKU" 118 | } 119 | ] 120 | }, 121 | "multipleBirthInteger": 3, 122 | "text": { 123 | "status": "generated", 124 | "div": "\u003cdiv xmlns\u003d\"http://www.w3.org/1999/xhtml\"\u003e\n \u003ctable\u003e\n \u003ctbody\u003e\n \u003ctr\u003e\n \u003ctd\u003eName\u003c/td\u003e\n \u003ctd\u003ePeter James \u003cb\u003eChalmers\u003c/b\u003e (\u0026quot;Jim\u0026quot;)\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003eAddress\u003c/td\u003e\n \u003ctd\u003e534 Erewhon, Pleasantville, Vic, 3999\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003eContacts\u003c/td\u003e\n \u003ctd\u003eHome: unknown. Work: (03) 5555 6473\u003c/td\u003e\n \u003c/tr\u003e\n \u003ctr\u003e\n \u003ctd\u003eId\u003c/td\u003e\n \u003ctd\u003eMRN: 12345 (Acme Healthcare)\u003c/td\u003e\n \u003c/tr\u003e\n \u003c/tbody\u003e\n \u003c/table\u003e\n \u003c/div\u003e" 125 | }, 126 | "contained": [ 127 | { 128 | "resourceType": "Binary", 129 | "id": "pic1", 130 | "contentType": "image/gif", 131 | "data": "R0lGODlhEwARAPcAAAAAAAAA/+9aAO+1AP/WAP/eAP/eCP/eEP/eGP/nAP/nCP/nEP/nIf/nKf/nUv/nWv/vAP/vCP/vEP/vGP/vIf/vKf/vMf/vOf/vWv/vY//va//vjP/3c//3lP/3nP//tf//vf///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEAAAEALAAAAAATABEAAAi+AAMIDDCgYMGBCBMSvMCQ4QCFCQcwDBGCA4cLDyEGECDxAoAQHjxwyKhQAMeGIUOSJJjRpIAGDS5wCDly4AALFlYOgHlBwwOSNydM0AmzwYGjBi8IHWoTgQYORg8QIGDAwAKhESI8HIDgwQaRDI1WXXAhK9MBBzZ8/XDxQoUFZC9IiCBh6wEHGz6IbNuwQoSpWxEgyLCXL8O/gAnylNlW6AUEBRIL7Og3KwQIiCXb9HsZQoIEUzUjNEiaNMKAAAA7" 132 | }, 133 | { 134 | "resourceType": "Organization", 135 | "id": "org3141", 136 | "text": { 137 | "status": "generated", 138 | "div": "\u003cdiv xmlns\u003d\"http://www.w3.org/1999/xhtml\"\u003e\n \u003cp\u003eGood Health Clinic\u003c/p\u003e\n \u003c/div\u003e" 139 | }, 140 | "identifier": [ 141 | { 142 | "system": "urn:ietf:rfc:3986", 143 | "value": "2.16.840.1.113883.19.5" 144 | } 145 | ], 146 | "name": "Good Health Clinic" 147 | } 148 | ], 149 | "contact": [ 150 | { 151 | "name": { 152 | "family": "du Marché", 153 | "_family": { 154 | "extension": [ 155 | { 156 | "url": "http://example.org/fhir/StructureDefinition/qualifier", 157 | "valueString": "VV" 158 | }, 159 | { 160 | "url": "http://hl7.org/fhir/StructureDefinitioniso-21090#nullFlavor", 161 | "valueCode": "ASKU" 162 | } 163 | ] 164 | }, 165 | "_given": [ 166 | null, 167 | { 168 | "id": "a3", 169 | "extension": [ 170 | { 171 | "url": "http://hl7.org/fhir/StructureDefinition/qualifier", 172 | "valueCode": "MID" 173 | } 174 | ] 175 | }, 176 | null 177 | ], 178 | "given": [ 179 | "Bénédicte", 180 | "Denise", 181 | "Marie" 182 | ] 183 | }, 184 | "relationship": [ 185 | { 186 | "coding": [ 187 | { 188 | "system": "http://example.org/fhir/CodeSystem/patient-contact-relationship", 189 | "code": "partner" 190 | } 191 | ] 192 | } 193 | ], 194 | "telecom": [ 195 | { 196 | "system": "phone", 197 | "value": "+33 (237) 998327" 198 | } 199 | ] 200 | } 201 | ], 202 | "generalPractitioner": [ 203 | { 204 | "reference": "#org3141" 205 | } 206 | ], 207 | "telecom": [ 208 | { 209 | "use": "home" 210 | }, 211 | { 212 | "system": "phone", 213 | "value": "(03) 5555 6473", 214 | "use": "work" 215 | } 216 | ], 217 | "meta": { 218 | "tag": [ 219 | { 220 | "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", 221 | "code": "HTEST", 222 | "display": "test health data" 223 | } 224 | ] 225 | } 226 | } --------------------------------------------------------------------------------