├── .cargo └── config.toml ├── .github ├── dependabot.yml └── workflows │ ├── draft-new-release.yaml │ ├── nightly.yml │ ├── publish-crates.yaml │ ├── publish-release.yaml │ ├── quality.yaml │ └── tests.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── mesh.rs └── value.rs ├── examples ├── enum1.rs ├── person.rs └── untagged.rs ├── simd-json-derive-int ├── .gitignore ├── Cargo.toml └── src │ ├── args.rs │ ├── deserialize.rs │ ├── deserialize │ ├── enum.rs │ ├── struct.rs │ └── struct │ │ ├── named.rs │ │ └── unnamed.rs │ ├── lib.rs │ ├── serialize.rs │ └── serialize │ ├── enum.rs │ ├── struct.rs │ └── struct │ ├── named.rs │ └── unnamed.rs ├── src ├── de.rs ├── impls.rs ├── impls │ ├── array.rs │ ├── chrono.rs │ ├── collections.rs │ ├── deref.rs │ ├── primitives.rs │ ├── simdjson.rs │ ├── string.rs │ └── tpl.rs └── lib.rs └── tests ├── attrs.rs ├── bench_structs.rs ├── deser.rs ├── enum.rs ├── rename.rs ├── simdjson.rs ├── skip_serializing_if.rs ├── struct.rs └── unknown_fields.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = ["-C", "target-cpu=native"] 3 | 4 | [target.x86_64-unknown-linux-gnu] 5 | linker = "/usr/bin/clang" 6 | rustflags = ["-C", "link-arg=-fuse-ld=lld", "-C", "link-arg=-Wl,--no-rosegment", "-C", "target-cpu=native", "-C", "force-frame-pointers=yes"] -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/draft-new-release.yaml: -------------------------------------------------------------------------------- 1 | name: "Draft new release" 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | new-version: 7 | type: choice 8 | description: "Which version you'd like to release?" 9 | options: 10 | - major (_.X.X) 11 | - minor (X._.X) 12 | - patch (X.X._) 13 | - rc (X.X.X-rc) 14 | - release (removes rc) 15 | required: true 16 | 17 | jobs: 18 | draft-new-release: 19 | name: "Draft a new release" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v3 23 | with: 24 | fetch-depth: 0 25 | - uses: actions-rs/toolchain@v1 26 | with: 27 | profile: minimal 28 | toolchain: stable 29 | - uses: Swatinem/rust-cache@v2 30 | - name: Install cargo-edit 31 | uses: actions-rs/install@v0.1 32 | with: 33 | crate: cargo-edit 34 | version: latest 35 | - name: Extracting version from input 36 | run: | 37 | VERSION=$(echo "${{github.event.inputs.new-version}}" | sed 's/ (.*)$//') 38 | echo "VER=$VERSION" >> $GITHUB_ENV 39 | - name: Bump new version in TOML files 40 | run: | 41 | OLD_VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) 42 | echo "OLD=$OLD_VERSION" >> $GITHUB_ENV 43 | cargo set-version -p simd-json-derive-int --manifest-path=./simd-json-derive-int/Cargo.toml --bump ${{ env.VER }} 44 | cargo set-version -p simd-json-derive --manifest-path=./Cargo.toml --bump ${{ env.VER }} 45 | NEW_VERSION=$(cargo pkgid | cut -d# -f2 | cut -d: -f2) 46 | # update simd-json-derive-int dependency to bumped version 47 | cargo upgrade --offline --recursive false -p "simd-json-derive-int@$NEW_VERSION" 48 | echo "NEW=$NEW_VERSION" >> $GITHUB_ENV 49 | - name: Create release branch 50 | run: | 51 | git checkout -b release/${{ env.NEW }} 52 | - name: Initialize mandatory git config 53 | run: | 54 | git config user.name "GitHub actions" 55 | git config user.email noreply@github.com 56 | - name: Run cargo check 57 | run: | 58 | cargo check --manifest-path=./Cargo.toml 59 | cargo check --manifest-path=./simd-json-derive-int/Cargo.toml 60 | - name: Commit changelog and manifest files 61 | id: make-commit 62 | run: | 63 | 64 | git commit -sa -m "Prepare release ${{ env.NEW }}" 65 | echo "commit=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT 66 | - name: Push new branch 67 | run: git push origin release/${{ env.NEW }} 68 | 69 | - name: Create pull request 70 | run: | 71 | gh pr create -B main --title "Release-v${{ env.NEW }}" --body "Yay release" --label "Release" 72 | env: 73 | # the github provided secrets.GITHUB_TOKEN will not trogger any further actions, 74 | # so we need a personal access token 75 | GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/nightly.yml: -------------------------------------------------------------------------------- 1 | name: Tests - Nightly 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | 10 | jobs: 11 | build-nightly: 12 | strategy: 13 | matrix: 14 | rustflags: 15 | - '-C target-cpu=native' 16 | features: 17 | - '' 18 | - '--features 128bit' 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v1 22 | - uses: actions-rs/toolchain@v1 23 | with: 24 | toolchain: nightly 25 | override: true 26 | - name: Build 27 | env: 28 | RUSTFLAGS: ${{ matrix.rustflags }} 29 | run: cargo build ${{ matrix.features }} 30 | - name: Run tests 31 | env: 32 | RUSTFLAGS: ${{ matrix.rustflags }} 33 | run: cargo test ${{ matrix.features }} 34 | -------------------------------------------------------------------------------- /.github/workflows/publish-crates.yaml: -------------------------------------------------------------------------------- 1 | name: Publish crates 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | publish-simd-json-derive-int: 8 | name: Publish simd-json-derive-int 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | fetch-depth: 0 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: stable 18 | - name: ensure we are not in detached-head state 19 | run: git switch -c v${{ gihub.ref_name }} 20 | - name: Publish simd-json-derive-int to crates.io 21 | uses: katyo/publish-crates@v1 22 | with: 23 | path: './simd-json-derive-int' 24 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} 25 | publish-simd-json-derive: 26 | name: Publish simd-json-derive 27 | needs: publish-simd-json-derive-int 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v3 31 | with: 32 | fetch-depth: 0 33 | - uses: actions-rs/toolchain@v1 34 | with: 35 | profile: minimal 36 | toolchain: stable 37 | - name: ensure we are not in detached-head state 38 | run: git switch -c v${{ gihub.ref_name }} 39 | - name: Publish simd-json-derive to crates.io 40 | uses: katyo/publish-crates@v1 41 | with: 42 | path: './Cargo.toml' 43 | registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/publish-release.yaml: -------------------------------------------------------------------------------- 1 | name: "Publish Release" 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | types: 8 | - closed 9 | - labeled 10 | jobs: 11 | publish-release: 12 | name: Create new release tag 13 | runs-on: ubuntu-latest 14 | if: github.event.pull_request.merged && contains( github.event.pull_request.labels.*.name, 'Release') 15 | steps: 16 | - name: Extract version from branch name (for release branches) 17 | if: startsWith(github.event.pull_request.head.ref, 'release/') 18 | run: | 19 | BRANCH_NAME="${{ github.event.pull_request.head.ref }}" 20 | VERSION=${BRANCH_NAME#release/} 21 | echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | - name: Initialize mandatory git config 25 | run: | 26 | git config user.name "GitHub actions" 27 | git config user.email noreply@github.com 28 | - name: Pushing tags 29 | # we push as github-actions, so this will not trigger any actions that listen on pushed tags or something 30 | run: | 31 | VER=$(echo v${{ env.RELEASE_VERSION }} | sed 's/.*rc.*/rc/') 32 | echo "RC=$VER" >> $GITHUB_ENV 33 | git tag -a -m "Release v${{ env.RELEASE_VERSION }}" "v${{ env.RELEASE_VERSION }}" 34 | git push origin v${{ env.RELEASE_VERSION }} 35 | - name: Publish Release from Tag 36 | run: gh release create v${{ env.RELEASE_VERSION }} --notes "Release v${{ env.RELEASE_VERSION }}" 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }} 39 | -------------------------------------------------------------------------------- /.github/workflows/quality.yaml: -------------------------------------------------------------------------------- 1 | name: Quality 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | 10 | jobs: 11 | quality: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: stable 18 | profiles: minimal 19 | override: true 20 | components: rustfmt,clippy 21 | - name: Validate cargo format 22 | run: cargo fmt -- --check 23 | - name: Check workflow permissions 24 | id: check_permissions 25 | uses: scherermichael-oss/action-has-permission@1.0.6 26 | with: 27 | required-permission: write 28 | env: 29 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 30 | - name: Run clippy action to produce annotations 31 | uses: actions-rs/clippy-check@v1 32 | if: steps.check_permissions.outputs.has-permission 33 | env: 34 | RUSTFLAGS: "-C target-cpu=native" 35 | with: 36 | token: ${{ secrets.GITHUB_TOKEN }} 37 | args: --all 38 | - name: Run clippy manually without annotations 39 | if: ${{ !steps.check_permissions.outputs.has-permission }} 40 | env: 41 | RUSTFLAGS: "-C target-cpu=native" 42 | run: cargo clippy --all -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | tests: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | - name: Install cargo-llvm-cov 19 | uses: taiki-e/install-action@cargo-llvm-cov 20 | - name: Run tests with llvm-cov 21 | env: 22 | RUSTFLAGS: "-C target-cpu=native" 23 | run: cargo llvm-cov --all-features --lcov --output-path lcov.txt 24 | - name: Upload coverage to codecov 25 | uses: codecov/codecov-action@v3 26 | env: 27 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 28 | with: 29 | files: ./lcov.txt # optional 30 | fail_ci_if_error: true # optional (default = false) 31 | verbose: true # optional (default = false) 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | perf.data* 4 | .idea 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simd-json-derive" 3 | version = "0.16.0" 4 | authors = ["Heinz N. Gies "] 5 | edition = "2021" 6 | license = "Apache-2.0/MIT" 7 | description = "derives for simd-json" 8 | documentation = "https://docs.rs/simd-json-derive" 9 | readme = "README.md" 10 | homepage = "https://docs.rs/simd-json-derive" 11 | repository = "https://github.com/simd-lite/simd-json-derive/" 12 | rust-version = "1.80" 13 | 14 | [profile.bench] 15 | debug = 2 16 | 17 | [workspace] 18 | members = ["simd-json-derive-int"] 19 | 20 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 21 | 22 | [dependencies] 23 | simd-json-derive-int = { path = "./simd-json-derive-int", version = "0.16.0" } 24 | value-trait = { version = "0.11.0" } 25 | itoa = "1" 26 | ryu = "1" 27 | chrono = { version = "0.4", optional = true } 28 | simd-json = { version = "0.15.0" } 29 | heap-array = { version = "0.1.1", optional = true } 30 | thiserror = "2.0" 31 | 32 | [dev-dependencies] 33 | serde_json = "1" 34 | serde = "1" 35 | criterion = "0.5" 36 | rand = "0.9" 37 | 38 | 39 | [features] 40 | default = ["impl-chrono"] 41 | impl-chrono = ["chrono"] 42 | 128bit = ["simd-json-derive-int/128bit", "simd-json/128bit"] 43 | heap-array = ["dep:heap-array"] 44 | 45 | 46 | [[example]] 47 | name = "enum1" 48 | 49 | [[example]] 50 | name = "untagged" 51 | 52 | [[example]] 53 | name = "person" 54 | 55 | 56 | [[bench]] 57 | name = "value" 58 | harness = false 59 | 60 | [[bench]] 61 | name = "mesh" 62 | harness = false 63 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simd-json-derive 2 | 3 | [![Latest version](https://img.shields.io/crates/v/simd-json-derive.svg)](https://crates.io/crates/simd-json-derive) 4 | [![documentation](https://img.shields.io/docsrs/simd-json-derive)](https://docs.rs/simd-json-derive) 5 | ![License](https://img.shields.io/crates/l/simd-json-derive.svg) 6 | 7 | 8 | Derives for high performance JSON serialisation and deserialisation. 9 | 10 | ## Usage 11 | 12 | ```rust 13 | 14 | #[derive(Serialize, Deserialize, Debug)] 15 | #[simd_json(deny_unknown_fields, rename_all = "camelCase")] 16 | struct MyStruct { 17 | first_field: String, 18 | #[simd_json(rename = "foo")] 19 | second_field: Option 20 | } 21 | 22 | fn main -> Result<(), simd_json::Error> { 23 | let my_struct = MyStruct { 24 | first_field: "i am first".to_string(), 25 | second_field: None 26 | } 27 | println!("Before: {my_struct:?}"); 28 | let mut json_string = my_struct.json_string()?; 29 | let deserialized = MyStruct::from_str(json_string.as_mut_str())?; 30 | println!("After: {deserialized:?}"); 31 | } 32 | ``` 33 | 34 | ## Supported Attributes 35 | 36 | Attributes are supported for both `#[simd_json(...)]` and for compatibilty also for `#[serde(...)]` and follow the same naming conventions as serde. 37 | 38 | For fields: 39 | 40 | * `rename = "new_name"` - renames a field 41 | 42 | For structs: 43 | 44 | * `rename_all = "camelCase"` - renames all (not otherwise renamed) based on the rule, `camelCase` is currently supported 45 | * `deny_unknown_fields` - Errors if unknown fields are encountered 46 | -------------------------------------------------------------------------------- /benches/mesh.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion, Throughput}; 2 | use simd_json_derive::{Deserialize, Serialize}; 3 | 4 | #[derive(Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize)] 5 | pub struct Vector3 { 6 | pub x: f32, 7 | pub y: f32, 8 | pub z: f32, 9 | } 10 | 11 | impl Default for Vector3 { 12 | fn default() -> Self { 13 | Self { 14 | x: rand::random(), 15 | y: rand::random(), 16 | z: rand::random(), 17 | } 18 | } 19 | } 20 | #[derive( 21 | Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize, Default, 22 | )] 23 | pub struct Triangle { 24 | pub v0: Vector3, 25 | pub v1: Vector3, 26 | pub v2: Vector3, 27 | pub normal: Vector3, 28 | } 29 | #[derive( 30 | Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize, Default, 31 | )] 32 | pub struct Mesh { 33 | pub triangles: Vec, 34 | } 35 | 36 | const BUFFER_LEN: usize = 50_000_000; 37 | 38 | fn deserialize_mesh(c: &mut Criterion) { 39 | let mut group = c.benchmark_group("deserialize"); 40 | for i in [2_usize, 32, 128, 1024] { 41 | let mesh = Mesh { 42 | triangles: (0..i).map(|_| Default::default()).collect(), 43 | }; 44 | let input = mesh.json_vec().unwrap(); 45 | group.throughput(Throughput::Bytes(input.len() as u64)); 46 | 47 | let mut buffer = simd_json::Buffers::new(BUFFER_LEN); 48 | 49 | group.bench_function(format!("simd({i})"), |b| { 50 | b.iter_batched_ref( 51 | || input.clone(), 52 | |deserialize_buffer| { 53 | black_box( 54 | Mesh::from_slice_with_buffers( 55 | deserialize_buffer.as_mut_slice(), 56 | &mut buffer, 57 | ) 58 | .unwrap(), 59 | ); 60 | }, 61 | BatchSize::SmallInput, 62 | ) 63 | }); 64 | group.bench_function(format!("serde({i})"), |b| { 65 | b.iter_batched_ref( 66 | || input.clone(), 67 | |deserialize_buffer| { 68 | black_box( 69 | simd_json::serde::from_slice::<'_, Mesh>(deserialize_buffer.as_mut_slice()) 70 | .unwrap(), 71 | ); 72 | }, 73 | BatchSize::SmallInput, 74 | ) 75 | }); 76 | } 77 | group.finish(); 78 | } 79 | fn serialize_mesh(c: &mut Criterion) { 80 | let mut group = c.benchmark_group("serialize"); 81 | for i in [2_usize, 32, 128, 1024] { 82 | let mut serialize_buffer = vec![0; BUFFER_LEN]; 83 | let mesh = Mesh { 84 | triangles: (0..i).map(|_| Default::default()).collect(), 85 | }; 86 | let input = mesh.json_vec().unwrap(); 87 | group.throughput(Throughput::Bytes(input.len() as u64)); 88 | 89 | group.bench_function(format!("simd({i})"), |b| { 90 | b.iter(|| { 91 | black_box(&mesh) 92 | .json_write(&mut black_box(serialize_buffer.as_mut_slice())) 93 | .unwrap(); 94 | }) 95 | }); 96 | 97 | group.bench_function(format!("serde({i})"), |b| { 98 | b.iter(|| { 99 | simd_json::serde::to_writer( 100 | black_box(serialize_buffer.as_mut_slice()), 101 | black_box(&mesh), 102 | ) 103 | .unwrap(); 104 | black_box(()); 105 | }) 106 | }); 107 | } 108 | group.finish(); 109 | } 110 | 111 | criterion_group!(benches, serialize_mesh, deserialize_mesh); 112 | criterion_main!(benches); 113 | -------------------------------------------------------------------------------- /benches/value.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; 2 | 3 | use simd_json::borrowed::Value as BorrowedValue; 4 | use simd_json::owned::Value as OwnedValue; 5 | use simd_json_derive::Deserialize; 6 | 7 | fn json_array_string(length: usize) -> String { 8 | format!( 9 | "[{}]", 10 | (0..length) 11 | .map(|i| i.to_string()) 12 | .collect::>() 13 | .join(",") 14 | ) 15 | } 16 | 17 | fn deserialize_owned_value_benchmark(c: &mut Criterion) { 18 | let mut group = c.benchmark_group("owned-deserialize-array"); 19 | for i in [2_usize, 32, 128, 1024, 4096] { 20 | let input = json_array_string(i); 21 | group.throughput(Throughput::Bytes(input.len() as u64)); 22 | group.bench_with_input(BenchmarkId::from_parameter(i), &input, |b, input| { 23 | let mut my_input = input.clone(); 24 | b.iter(|| unsafe { OwnedValue::from_str(my_input.as_mut_str()).expect("shizzle") }) 25 | }); 26 | } 27 | group.finish(); 28 | } 29 | 30 | fn deserialize_borrowed_value_benchmark(c: &mut Criterion) { 31 | let mut group = c.benchmark_group("borrowed-deserialize-array"); 32 | for i in [2_usize, 32, 128, 1024, 4096] { 33 | let input = json_array_string(i); 34 | group.throughput(Throughput::Bytes(input.len() as u64)); 35 | group.bench_with_input(BenchmarkId::from_parameter(i), &input, |b, input| { 36 | let mut my_input = input.clone(); 37 | b.iter(|| { 38 | let _x = 39 | unsafe { BorrowedValue::from_str(my_input.as_mut_str()).expect("shizzle") }; 40 | }) 41 | }); 42 | } 43 | group.finish(); 44 | } 45 | 46 | criterion_group!( 47 | benches, 48 | deserialize_owned_value_benchmark, 49 | deserialize_borrowed_value_benchmark 50 | ); 51 | criterion_main!(benches); 52 | -------------------------------------------------------------------------------- /examples/enum1.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[derive(Deserialize, Serialize, Debug)] 4 | pub enum StoredVariants { 5 | YesNo(bool), 6 | Small(u8, i8), 7 | Signy(i64), 8 | Stringy(String), 9 | // Named { a: u8, b: i8 }, 10 | } 11 | 12 | fn main() { 13 | let x = StoredVariants::Signy(-1); 14 | let mut serialized = x.json_string().expect("serialization shouldnt fail :("); 15 | let deserialized = unsafe { StoredVariants::from_str(serialized.as_mut_str()) } 16 | .expect("serialized stuff should be deserializable"); 17 | println!("Serialized: {x:?}"); 18 | println!("Deserialized: {deserialized:?}"); 19 | } 20 | -------------------------------------------------------------------------------- /examples/person.rs: -------------------------------------------------------------------------------- 1 | #![allow(warnings)] 2 | 3 | use serde::Deserialize; 4 | use serde_json; 5 | use simd_json::Buffers; 6 | use simd_json_derive::Deserialize as SimdDeserialize; 7 | use std::time::Instant; 8 | 9 | #[derive(Deserialize, SimdDeserialize)] 10 | struct Person { 11 | id: String, 12 | index: i32, 13 | guid: String, 14 | isActive: bool, 15 | picture: String, 16 | age: u32, 17 | } 18 | 19 | #[derive(Deserialize, SimdDeserialize)] 20 | struct PersonBorrowed<'ser> { 21 | #[serde(borrow)] 22 | id: &'ser str, 23 | index: i32, 24 | #[serde(borrow)] 25 | guid: &'ser str, 26 | isActive: bool, 27 | #[serde(borrow)] 28 | picture: &'ser str, 29 | age: u32, 30 | } 31 | 32 | const N: usize = 100000; 33 | 34 | fn main() { 35 | let json_bytes = br#"{ 36 | "id": "60a6965e5e47ef8456878326", 37 | "index": 0, 38 | "guid": "cfce331d-07f3-40d3-b3d9-0672f651c26d", 39 | "isActive": true, 40 | "picture": "http://placehold.it/32x32", 41 | "age": 22 42 | }"# 43 | .to_vec(); 44 | 45 | let mut json_bytes_2 = json_bytes.clone(); 46 | let now_2 = Instant::now(); 47 | for _ in 0..N { 48 | let p2: simd_json::OwnedValue = simd_json::to_owned_value(&mut json_bytes_2).unwrap(); 49 | } 50 | println!("simd_json {:?}", now_2.elapsed()); 51 | 52 | let mut json_bytes_2 = json_bytes.clone(); 53 | let now_2 = Instant::now(); 54 | for _ in 0..N { 55 | let p2: Person = simd_json::serde::from_slice(&mut json_bytes_2).unwrap(); 56 | criterion::black_box(p2); 57 | } 58 | println!("simd_json (struct) {:?}", now_2.elapsed()); 59 | 60 | let mut json_bytes_2 = json_bytes.clone(); 61 | let now_2 = Instant::now(); 62 | for _ in 0..N { 63 | let p2 = Person::from_slice(&mut json_bytes_2).unwrap(); 64 | criterion::black_box(p2); 65 | } 66 | println!("simd_json (simd-struct) {:?}", now_2.elapsed()); 67 | 68 | let mut json_bytes_2 = json_bytes.clone(); 69 | let now_2 = Instant::now(); 70 | for _ in 0..N { 71 | let p2 = PersonBorrowed::from_slice(&mut json_bytes_2).unwrap(); 72 | criterion::black_box(p2); 73 | } 74 | println!("simd_json (simd-struct borrowed) {:?}", now_2.elapsed()); 75 | 76 | let mut json_bytes_2 = json_bytes.clone(); 77 | let now_2 = Instant::now(); 78 | let mut buffers = Buffers::new(2048); 79 | for _ in 0..N { 80 | let p2 = PersonBorrowed::from_slice_with_buffers(&mut json_bytes_2, &mut buffers).unwrap(); 81 | criterion::black_box(p2); 82 | } 83 | println!( 84 | "simd_json (simd-struct borrowed buffered) {:?}", 85 | now_2.elapsed() 86 | ); 87 | 88 | let mut json_bytes_1 = json_bytes.clone(); 89 | let now_1 = Instant::now(); 90 | for _ in 0..N { 91 | let p: Person = serde_json::from_slice(&json_bytes_1).unwrap(); 92 | criterion::black_box(p); 93 | } 94 | println!("serde {:?}", now_1.elapsed()); 95 | 96 | let mut json_bytes_1 = json_bytes.clone(); 97 | let now_1 = Instant::now(); 98 | for _ in 0..N { 99 | let p: PersonBorrowed = serde_json::from_slice(&json_bytes_1).unwrap(); 100 | criterion::black_box(p); 101 | } 102 | println!("serde (borrowed) {:?}", now_1.elapsed()); 103 | } 104 | -------------------------------------------------------------------------------- /examples/untagged.rs: -------------------------------------------------------------------------------- 1 | use simd_json::Node; 2 | use simd_json_derive::Deserialize; 3 | 4 | #[derive(Debug, Deserialize)] 5 | pub struct LoginResponse<'de> { 6 | pub foo: &'de str, 7 | } 8 | 9 | #[derive(Debug, Deserialize)] 10 | pub struct ErrorResponse<'de> { 11 | pub error: &'de str, 12 | } 13 | 14 | #[derive(Debug)] 15 | #[allow(dead_code)] 16 | enum Response<'de> { 17 | LoginResponse(LoginResponse<'de>), 18 | Error(ErrorResponse<'de>), 19 | } 20 | 21 | fn parse(data: &mut [u8]) -> Result> { 22 | let tape = simd_json::to_tape(data)?; 23 | 24 | if let [Node::Object { len: 1, count: 2 }, Node::String("error"), Node::String(error)] = 25 | tape.0.as_slice() 26 | { 27 | Ok(Response::Error(ErrorResponse { error })) 28 | } else { 29 | let mut itr = tape.0.into_iter().peekable(); 30 | Ok(Response::LoginResponse(LoginResponse::from_tape(&mut itr)?)) 31 | } 32 | } 33 | 34 | fn main() { 35 | let mut data = br#"{"error":"hello world!"}"#.to_vec(); 36 | println!("{:#?}", parse(&mut data)) 37 | } 38 | -------------------------------------------------------------------------------- /simd-json-derive-int/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /simd-json-derive-int/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simd-json-derive-int" 3 | version = "0.16.0" 4 | authors = ["Heinz N. Gies "] 5 | edition = "2021" 6 | license = "Apache-2.0/MIT" 7 | description = "procmacros for simd-json-derive" 8 | documentation = "https://docs.rs/simd-json-derive-int" 9 | repository = "https://github.com/simd-lite/simd-json-derive" 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1.0" 16 | quote = "1.0" 17 | syn = { version = "2.0", features = ["visit", "extra-traits"] } 18 | simd-json = { version = "0.15.0" } 19 | thiserror = "2" 20 | 21 | 22 | [features] 23 | 128bit = ["simd-json/128bit"] 24 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/args.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Ident, Literal}; 2 | use simd_json::prelude::*; 3 | use simd_json::OwnedValue; 4 | use syn::{ 5 | parse::{Parse, ParseStream}, 6 | LitStr, Path, 7 | }; 8 | use syn::{Attribute, Field, Token}; 9 | 10 | #[derive(Debug, Default)] 11 | pub(crate) struct FieldAttrs { 12 | rename: Option, 13 | skip_serializing_if: Option, 14 | } 15 | 16 | impl Parse for FieldAttrs { 17 | fn parse(input: ParseStream) -> syn::Result { 18 | let mut attrs = FieldAttrs::default(); 19 | 20 | while !input.is_empty() { 21 | let attr: Ident = input.parse()?; 22 | match attr.to_string().as_str() { 23 | "rename" => { 24 | let _eqal_token: Token![=] = input.parse()?; 25 | let name: LitStr = input.parse()?; 26 | 27 | attrs.rename = Some(name.value()); 28 | } 29 | "skip_serializing_if" => { 30 | let _eqal_token: Token![=] = input.parse()?; 31 | let function: LitStr = input.parse()?; 32 | 33 | let path: Path = function.parse()?; 34 | 35 | attrs.skip_serializing_if = Some(path); 36 | } 37 | "borrow" => (), 38 | other => { 39 | return Err(syn::Error::new( 40 | attr.span(), 41 | format!("unexpected attribute `{}`", other), 42 | )) 43 | } 44 | } 45 | if !input.is_empty() { 46 | let _comma_token: Token![,] = input.parse()?; 47 | } 48 | } 49 | Ok(attrs) 50 | } 51 | } 52 | 53 | #[derive(Debug)] 54 | pub(crate) enum RenameAll { 55 | None, 56 | CamelCase, 57 | Lowercase, 58 | } 59 | 60 | fn capitalize(field: &str) -> String { 61 | let mut chars = field.chars(); 62 | match chars.next() { 63 | None => String::new(), 64 | Some(first) => first.to_uppercase().collect::() + chars.as_str(), 65 | } 66 | } 67 | 68 | impl RenameAll { 69 | fn apply(&self, field: &str) -> String { 70 | match self { 71 | RenameAll::None => String::from(field), 72 | RenameAll::Lowercase => field.to_lowercase(), 73 | RenameAll::CamelCase => { 74 | let mut parts = field.split('_'); 75 | let first = parts.next().expect("zero length name"); 76 | format!( 77 | "{}{}", 78 | first, 79 | parts.map(capitalize).collect::>().join("") 80 | ) 81 | } 82 | } 83 | } 84 | } 85 | #[derive(Debug)] 86 | pub(crate) struct StructAttrs { 87 | rename_all: RenameAll, 88 | deny_unknown_fields: bool, 89 | } 90 | 91 | impl Default for StructAttrs { 92 | fn default() -> Self { 93 | StructAttrs { 94 | rename_all: RenameAll::None, 95 | deny_unknown_fields: false, 96 | } 97 | } 98 | } 99 | 100 | impl Parse for StructAttrs { 101 | fn parse(input: ParseStream) -> syn::Result { 102 | let mut rename_all = RenameAll::None; 103 | let mut deny_unknown_fields = false; 104 | while !input.is_empty() { 105 | let attr: Ident = input.parse()?; 106 | match attr.to_string().as_str() { 107 | "rename_all" => { 108 | let _eqal_token: Token![=] = input.parse()?; 109 | let name: Literal = input.parse()?; 110 | 111 | match name.to_string().as_str() { 112 | r#""camelCase""# => rename_all = RenameAll::CamelCase, 113 | r#""lowercase""# => rename_all = RenameAll::Lowercase, 114 | other => { 115 | return Err(syn::Error::new( 116 | attr.span(), 117 | format!("unexpected rename_all type `{}`", other), 118 | )) 119 | } 120 | } 121 | } 122 | "deny_unknown_fields" => { 123 | deny_unknown_fields = true; 124 | } 125 | other => { 126 | return Err(syn::Error::new( 127 | attr.span(), 128 | format!("unexpected field attribute `{}`", other), 129 | )) 130 | } 131 | } 132 | if !input.is_empty() { 133 | let _comma_token: Token![,] = input.parse()?; 134 | } 135 | } 136 | Ok(StructAttrs { 137 | rename_all, 138 | deny_unknown_fields, 139 | }) 140 | } 141 | } 142 | 143 | pub fn field_attrs(attr: &Attribute) -> FieldAttrs { 144 | attr.parse_args::() 145 | .expect("failed to parse attributes") 146 | } 147 | 148 | pub fn struct_attrs(attr: &Attribute) -> StructAttrs { 149 | attr.parse_args::() 150 | .expect("failed to parse attributes") 151 | } 152 | 153 | pub fn get_attr<'field>(attrs: &'field [Attribute], name: &str) -> Option<&'field Attribute> { 154 | attrs 155 | .iter() 156 | .find(|a| a.path().get_ident().map(|i| i == name).unwrap_or_default()) 157 | } 158 | 159 | impl StructAttrs { 160 | pub(crate) fn parse(attrs: Vec) -> StructAttrs { 161 | if let Some(attrs) = get_attr(&attrs, "simd_json") { 162 | struct_attrs(attrs) 163 | } else if let Some(attrs) = get_attr(&attrs, "serde") { 164 | struct_attrs(attrs) 165 | } else { 166 | StructAttrs::default() 167 | } 168 | } 169 | pub(crate) fn deny_unknown_fields(&self) -> bool { 170 | self.deny_unknown_fields 171 | } 172 | 173 | pub(crate) fn skip_serializing_if(&self, field: &Field) -> Option { 174 | get_attr(&field.attrs, "simd_json") 175 | .or_else(|| get_attr(&field.attrs, "serde")) 176 | .map(field_attrs) 177 | .and_then(|a| a.skip_serializing_if) 178 | } 179 | pub(crate) fn name(&self, field: &Field) -> String { 180 | if let Some(attr) = get_attr(&field.attrs, "simd_json") 181 | .map(field_attrs) 182 | .and_then(|a| a.rename) 183 | { 184 | format!("{}:", OwnedValue::from(attr).encode()) 185 | } else if let Some(attr) = get_attr(&field.attrs, "serde") 186 | .map(field_attrs) 187 | .and_then(|a| a.rename) 188 | { 189 | format!("{}:", OwnedValue::from(attr).encode()) 190 | } else { 191 | let f = field 192 | .ident 193 | .as_ref() 194 | .expect("Field is missing ident") 195 | .to_string(); 196 | format!("{}:", OwnedValue::from(self.rename_all.apply(&f)).encode()) 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/deserialize.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{self, TokenStream}; 2 | use syn::{parse_macro_input, Data, DeriveInput}; 3 | 4 | use crate::args::*; 5 | 6 | mod r#struct; 7 | 8 | mod r#enum; 9 | 10 | pub(crate) fn derive(input: TokenStream) -> TokenStream { 11 | let input = parse_macro_input!(input as DeriveInput); 12 | match input { 13 | // struct 14 | DeriveInput { 15 | ident, 16 | attrs, 17 | data: Data::Struct(defn), 18 | generics, 19 | .. 20 | } => r#struct::derive(StructAttrs::parse(attrs), ident, generics, defn), // Named 21 | DeriveInput { 22 | ident, 23 | attrs, 24 | data: Data::Enum(defn), 25 | generics, 26 | .. 27 | } => r#enum::derive(StructAttrs::parse(attrs), ident, generics, defn), 28 | _ => unimplemented!("This was trying to derive something odd"), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/deserialize/enum.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::Ident; 3 | use quote::{format_ident, quote}; 4 | use syn::{DataEnum, Fields, GenericParam, Generics, Variant}; 5 | 6 | use crate::args::StructAttrs; 7 | 8 | pub(super) fn derive( 9 | _attrs: StructAttrs, 10 | ident: Ident, 11 | generics: Generics, 12 | data: DataEnum, 13 | ) -> proc_macro::TokenStream { 14 | let params = &generics.params; 15 | let (all_generics, derive_lt) = match params.first() { 16 | None => (quote! { <'input> }, quote! { 'input }), 17 | Some(GenericParam::Lifetime(lifetime)) => (quote! { <#params> }, quote! { #lifetime }), 18 | Some(_) => (quote! { <'input, #params> }, quote! { 'input }), 19 | }; 20 | 21 | // let mut body_elements = Vec::new(); 22 | let variants = data.variants; 23 | let (simple, variants): (Vec<_>, Vec<_>) = 24 | variants.into_iter().partition(|v| v.fields.is_empty()); 25 | let (named, unnamed): (Vec<_>, Vec<_>) = variants.iter().partition(|v| { 26 | matches!( 27 | v, 28 | Variant { 29 | fields: Fields::Named(_), 30 | .. 31 | } 32 | ) 33 | }); 34 | 35 | let (unnamed1, unnamed): (Vec<_>, Vec<_>) = 36 | unnamed.into_iter().partition(|v| v.fields.len() == 1); 37 | if !named.is_empty() { 38 | todo!( 39 | "ENUM variants with named fields are not supported: {:?}", 40 | named 41 | ); 42 | } 43 | let (unnamed_keys, unnamed_values): (Vec<_>, Vec<_>) = unnamed 44 | .iter() 45 | .map(|s| { 46 | ( 47 | &s.ident, 48 | ( 49 | s.ident.to_string(), 50 | s.fields 51 | .iter() 52 | .enumerate() 53 | .map(|f| format_ident!("_unnamed_{}", f.0)) 54 | .collect::>(), 55 | ), 56 | ) 57 | }) 58 | .unzip(); 59 | let (unnamed_values, unnamed_fields): (Vec<_>, Vec<_>) = unnamed_values 60 | .into_iter() 61 | .map(|(v, f)| { 62 | ( 63 | v, 64 | ( 65 | f.len(), 66 | quote! { 67 | #( 68 | { 69 | let #f = ::simd_json_derive::Deserialize::from_tape(__deser_tape)?; 70 | #f 71 | } 72 | ),* 73 | }, 74 | ), 75 | ) 76 | }) 77 | .unzip(); 78 | let (unnamed_len, unnamed_fields): (Vec<_>, Vec<_>) = unnamed_fields.into_iter().unzip(); 79 | let unnamed = quote! { 80 | #( 81 | Some(::simd_json::Node::String(#unnamed_values)) => { 82 | match __deser_tape.next() { 83 | Some(::simd_json::Node::Array{len: #unnamed_len, ..}) => Ok(#ident::#unnamed_keys(#unnamed_fields)), 84 | _ => Err(::simd_json_derive::de::Error::FieldNotAnArray(#unnamed_values)) 85 | } 86 | 87 | }, 88 | )* 89 | }; 90 | // unnamed 1 91 | let (unnamed1_keys, unnamed1_values): (Vec<_>, Vec<_>) = unnamed1 92 | .iter() 93 | .map(|s| (&s.ident, s.ident.to_string())) 94 | .unzip(); 95 | let unnamed1 = quote! { 96 | #( 97 | Some(::simd_json::Node::String(#unnamed1_values)) => Ok(#ident::#unnamed1_keys(::simd_json_derive::Deserialize::from_tape(__deser_tape)?)), 98 | )* 99 | }; 100 | 101 | let (simple_keys, simple_values): (Vec<_>, Vec<_>) = simple 102 | .iter() 103 | .map(|s| (&s.ident, s.ident.to_string())) 104 | .unzip(); 105 | let simple = quote! { 106 | #( 107 | Some(::simd_json::Node::String(#simple_values)) => Ok(#ident::#simple_keys), 108 | )* 109 | }; 110 | let expanded = quote! { 111 | impl #all_generics ::simd_json_derive::Deserialize <#derive_lt> for #ident #generics { 112 | #[inline] 113 | fn from_tape(__deser_tape: &mut ::simd_json_derive::Tape<#derive_lt>) -> ::simd_json_derive::de::Result 114 | where 115 | Self: std::marker::Sized + #derive_lt 116 | { 117 | match __deser_tape.next() { 118 | #simple 119 | Some(::simd_json::Node::Object{len: 1, ..}) => { 120 | match __deser_tape.next() { 121 | #unnamed1 122 | #unnamed 123 | Some(::simd_json::Node::String(__other)) => Err(::simd_json_derive::de::Error::UnknownEnumVariant(__other.to_string()).into()), 124 | Some(_) => Err(::simd_json_derive::de::Error::InvalidEnumRepresentation), 125 | None => Err(::simd_json_derive::de::Error::EOF) 126 | } 127 | }, 128 | Some(__other) => Err(::simd_json_derive::de::Error::InvalidEnumRepresentation), 129 | None => Err(::simd_json_derive::de::Error::EOF) 130 | } 131 | } 132 | } 133 | }; 134 | TokenStream::from(expanded) 135 | } 136 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/deserialize/struct.rs: -------------------------------------------------------------------------------- 1 | pub(super) mod named; 2 | pub(super) mod unnamed; 3 | 4 | use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident}; 5 | 6 | use crate::args::StructAttrs; 7 | 8 | pub(crate) fn derive( 9 | attrs: StructAttrs, 10 | ident: Ident, 11 | generics: Generics, 12 | defn: DataStruct, 13 | ) -> proc_macro::TokenStream { 14 | match defn { 15 | DataStruct { 16 | fields: Fields::Unnamed(FieldsUnnamed { unnamed, .. }), 17 | .. 18 | } => unnamed::derive(attrs, ident, generics, unnamed), 19 | // Named 20 | DataStruct { 21 | fields: Fields::Named(FieldsNamed { named, .. }), 22 | .. 23 | } => named::derive(attrs, ident, generics, named), 24 | DataStruct { 25 | fields: Fields::Unit, 26 | .. 27 | } => { 28 | todo!("Unit structs are not implemented") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/deserialize/struct/named.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{ 4 | punctuated::Punctuated, token::Comma, Field, GenericParam, Generics, Ident, Path, PathSegment, 5 | Type, TypePath, 6 | }; 7 | 8 | use crate::args::StructAttrs; 9 | 10 | /// Named struct as `Struct(u8)` or `Struct(u8, String)` 11 | pub(crate) fn derive( 12 | attrs: StructAttrs, 13 | ident: Ident, 14 | generics: Generics, 15 | fields: Punctuated, 16 | ) -> proc_macro::TokenStream { 17 | let mut value_keys = Vec::new(); 18 | let mut value_locals = Vec::new(); 19 | let mut values = Vec::new(); 20 | 21 | let mut options = Vec::new(); 22 | let mut option_locals = Vec::new(); 23 | let mut option_keys = Vec::new(); 24 | 25 | let deny_unknown_fields: bool = attrs.deny_unknown_fields(); 26 | let params = &generics.params; 27 | let (all_generics, derive_lt) = match params.first() { 28 | None => (quote! { <'input> }, quote! { 'input }), 29 | Some(GenericParam::Lifetime(lifetime)) => (quote! { <#params> }, quote! { #lifetime }), 30 | Some(_) => (quote! { <'input, #params> }, quote! { 'input }), 31 | }; 32 | for (id, f) in fields.iter().enumerate() { 33 | let mut is_option = false; 34 | if let Type::Path(TypePath { 35 | path: Path { segments, .. }, 36 | .. 37 | }) = &f.ty 38 | { 39 | if let Some(PathSegment { ident, .. }) = segments.first() { 40 | is_option = ident == "Option"; 41 | } 42 | } 43 | 44 | let ident = f.ident.clone().expect("Missing ident"); 45 | let name = attrs.name(f); 46 | let name = name.trim_matches(':').trim_matches('"').to_string(); 47 | if is_option { 48 | options.push(ident.clone()); 49 | option_locals.push(format_ident!("__option_{}", id)); 50 | option_keys.push(name); 51 | } else { 52 | values.push(ident); 53 | value_locals.push(format_ident!("__value_{}", id)); 54 | value_keys.push(name); 55 | } 56 | } 57 | 58 | let expanded = quote! { 59 | impl #all_generics ::simd_json_derive::Deserialize <#derive_lt> for #ident #generics { 60 | #[inline] 61 | #[allow(clippy::forget_copy)] 62 | #[allow(clippy::forget_non_drop)] 63 | fn from_tape(__deser_tape: &mut ::simd_json_derive::Tape <#derive_lt>) -> ::simd_json_derive::de::Result 64 | where 65 | Self: std::marker::Sized + #derive_lt 66 | { 67 | let __deser_len: usize = if let Some(::simd_json::Node::Object{len, ..}) = __deser_tape.next() { 68 | len 69 | } else { 70 | return Err(::simd_json_derive::de::Error::InvalidStructRepresentation); 71 | }; 72 | 73 | #(let mut #value_locals = None;)* 74 | #(let mut #option_locals = None;)* 75 | 76 | for _ in 0..__deser_len { 77 | match __deser_tape.next() { 78 | Some(::simd_json::Node::String(__deser_key)) => { 79 | match __deser_key { 80 | #( 81 | #value_keys => { 82 | let v = ::simd_json_derive::Deserialize::from_tape(__deser_tape)?; 83 | #value_locals = Some(v); 84 | } 85 | )* 86 | #( 87 | #option_keys => { 88 | #option_locals = ::simd_json_derive::Deserialize::from_tape(__deser_tape)?; 89 | } 90 | )* 91 | __unknown_field if #deny_unknown_fields => { 92 | return Err(::simd_json_derive::de::Error::UnknownField { 93 | unknown_field: __unknown_field.to_string(), 94 | possible_field_names: &[ #(#value_keys,)* #(#option_keys,)* ] 95 | }); 96 | } 97 | _ => { 98 | // ignore unknown field 99 | ::simd_json_derive::__skip(1, __deser_tape) 100 | } 101 | } 102 | }, 103 | // There are no more elements 104 | _ => break 105 | } 106 | } 107 | Ok(#ident { 108 | #( 109 | #options: #option_locals, 110 | )* 111 | #( 112 | #values: #value_locals.ok_or_else(|| ::simd_json_derive::de::Error::MissingField(#value_keys))?, 113 | )* 114 | }) 115 | } 116 | } 117 | }; 118 | TokenStream::from(expanded) 119 | } 120 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/deserialize/struct/unnamed.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::{punctuated::Punctuated, token::Comma, Field, GenericParam, Generics, Ident}; 4 | 5 | use crate::args::StructAttrs; 6 | 7 | pub(crate) fn derive( 8 | _attrs: StructAttrs, 9 | ident: Ident, 10 | generics: Generics, 11 | fields: Punctuated, 12 | ) -> proc_macro::TokenStream { 13 | let params = &generics.params; 14 | let (all_generics, derive_lt) = match params.first() { 15 | None => (quote! { <'input> }, quote! { 'input }), 16 | Some(GenericParam::Lifetime(lifetime)) => (quote! { <#params> }, quote! { #lifetime }), 17 | Some(_) => (quote! { <'input, #params> }, quote! { 'input }), 18 | }; 19 | 20 | if fields.len() == 1 { 21 | // This is a newtype 22 | 23 | let expanded = quote! { 24 | impl #all_generics ::simd_json_derive::Deserialize <#derive_lt> for #ident #generics { 25 | #[inline] 26 | fn from_tape(__deser_tape: &mut ::simd_json_derive::Tape<#derive_lt>) -> ::simd_json_derive::de::Result 27 | where 28 | Self: std::marker::Sized + #derive_lt 29 | { 30 | ::simd_json_derive::Deserialize::from_tape(__deser_tape).map(Self) 31 | } 32 | } 33 | }; 34 | TokenStream::from(expanded) 35 | } else { 36 | todo!("Only newtype unnamed structs are supported by now") 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | 3 | mod args; 4 | mod deserialize; 5 | mod serialize; 6 | 7 | #[proc_macro_derive(Serialize, attributes(serde, simd_json))] 8 | pub fn derive_serialize(input: TokenStream) -> TokenStream { 9 | serialize::derive(input) 10 | } 11 | 12 | #[proc_macro_derive(Deserialize, attributes(serde, simd_json))] 13 | pub fn derive_deserialize(input: TokenStream) -> TokenStream { 14 | deserialize::derive(input) 15 | } 16 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/serialize.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{self, TokenStream}; 2 | use quote::quote; 3 | use syn::{Data, DeriveInput}; 4 | 5 | use crate::args::StructAttrs; 6 | 7 | mod r#enum; 8 | mod r#struct; 9 | 10 | pub(crate) fn derive(input: TokenStream) -> TokenStream { 11 | let input = syn::parse_macro_input!(input as DeriveInput); 12 | match input { 13 | // struct 14 | DeriveInput { 15 | ident, 16 | attrs, 17 | data: Data::Struct(defn), 18 | generics, 19 | .. 20 | } => r#struct::derive(StructAttrs::parse(attrs), ident, generics, defn), 21 | DeriveInput { 22 | ident, 23 | data: Data::Enum(data), 24 | generics, 25 | attrs, 26 | .. 27 | } => r#enum::derive(StructAttrs::parse(attrs), ident, data, generics), 28 | _ => TokenStream::from(quote! {}), 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/serialize/enum.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::{Ident, Span}; 3 | use quote::quote; 4 | use simd_json::prelude::Writable as _; 5 | use syn::{DataEnum, Fields, Generics, Variant}; 6 | 7 | use crate::args::StructAttrs; 8 | 9 | pub(crate) fn derive( 10 | attrs: StructAttrs, 11 | ident: Ident, 12 | data: DataEnum, 13 | generics: Generics, 14 | ) -> TokenStream { 15 | let mut body_elements = Vec::new(); 16 | let variants = data.variants; 17 | let (simple, variants): (Vec<_>, Vec<_>) = 18 | variants.into_iter().partition(|v| v.fields.is_empty()); 19 | let (named, unnamed): (Vec<_>, Vec<_>) = variants.iter().partition(|v| { 20 | matches!( 21 | v, 22 | Variant { 23 | fields: Fields::Named(_), 24 | .. 25 | } 26 | ) 27 | }); 28 | 29 | let (unnamed1, unnamed): (Vec<_>, Vec<_>) = 30 | unnamed.into_iter().partition(|v| v.fields.len() == 1); 31 | 32 | // enum no fields of Enum::Variant 33 | // They serialize as: "Varriant" 34 | 35 | let (simple_keys, simple_values): (Vec<_>, Vec<_>) = simple 36 | .iter() 37 | .map(|s| { 38 | ( 39 | &s.ident, 40 | simd_json::OwnedValue::from(s.ident.to_string()).encode(), 41 | ) 42 | }) 43 | .unzip(); 44 | let simple = quote! { 45 | #( 46 | #ident::#simple_keys => writer.write_all(#simple_values.as_bytes()) 47 | ),* 48 | }; 49 | 50 | if !simple.is_empty() { 51 | body_elements.push(simple); 52 | } 53 | 54 | // Unnamed enum variants with exactly 1 field of Enum::Variant(type1) 55 | // They serialize as: {"Varriant":..} 56 | 57 | let (unnamed1_idents, unnamed1_keys): (Vec<_>, Vec<_>) = unnamed1 58 | .iter() 59 | .map(|v| { 60 | ( 61 | &v.ident, 62 | format!( 63 | "{{{}:", 64 | simd_json::OwnedValue::from(v.ident.to_string()).encode() 65 | ), 66 | ) 67 | }) 68 | .unzip(); 69 | let unnamed1 = quote! { 70 | #( 71 | #ident::#unnamed1_idents(v) => { 72 | writer.write_all(#unnamed1_keys.as_bytes())?; 73 | v.json_write(writer)?; 74 | writer.write_all(b"}") 75 | } 76 | ),* 77 | }; 78 | if !unnamed1.is_empty() { 79 | body_elements.push(unnamed1); 80 | } 81 | 82 | // Unnamed enum variants with more then 1 field of Enum::Variant(type1, type2, type3) 83 | // They serialize as: {"Varriant":[.., .., ..]} 84 | 85 | let (unnamed_ident_and_vars, unnamed_keys): (Vec<_>, Vec<_>) = unnamed 86 | .iter() 87 | .map(|v| { 88 | ( 89 | ( 90 | &v.ident, 91 | (0..v.fields.len()) 92 | .map(|i| Ident::new(&format!("v{}", i), Span::call_site())) 93 | .collect::>(), 94 | ), 95 | format!( 96 | "{{{}:[", 97 | simd_json::OwnedValue::from(v.ident.to_string()).encode() 98 | ), 99 | ) 100 | }) 101 | .unzip(); 102 | 103 | let (unnamed_idents, unnamed_var_names): (Vec<_>, Vec<_>) = 104 | unnamed_ident_and_vars.into_iter().unzip(); 105 | 106 | let unnamed_vecs = unnamed_var_names.iter().map(|vs| { 107 | let (first, rest) = vs.split_first().expect("zero unnamed vars"); 108 | quote! { 109 | #first.json_write(writer)?; 110 | #( 111 | writer.write_all(b",")?; 112 | #rest.json_write(writer)?; 113 | )* 114 | } 115 | }); 116 | 117 | let unnamed_vars = unnamed_var_names.iter().map(|vs| quote! { #(#vs),* }); 118 | 119 | let unnamed = quote! { 120 | #( 121 | #ident::#unnamed_idents(#unnamed_vars) => 122 | { 123 | writer.write_all(#unnamed_keys.as_bytes())?; 124 | #unnamed_vecs 125 | writer.write_all(b"]}") 126 | } 127 | ),* 128 | }; 129 | if !unnamed.is_empty() { 130 | body_elements.push(unnamed); 131 | } 132 | 133 | // Named enum variants of the form Enum::Variant{key1: type, key2: type...} 134 | // They serialize as: {"Varriant":{"key1":..,"key2":..}} 135 | 136 | let mut named_bodies = Vec::new(); 137 | for v in named { 138 | let named_ident = &v.ident; 139 | let mut keys = Vec::new(); 140 | let mut values = Vec::new(); 141 | let mut skip_if = Vec::new(); 142 | 143 | for f in &v.fields { 144 | let name = attrs.name(f); 145 | let ident = f.ident.clone().expect("Missing ident"); 146 | keys.push(name); 147 | values.push(ident); 148 | skip_if.push(attrs.skip_serializing_if(f)); 149 | } 150 | let variant_name = simd_json::OwnedValue::from(v.ident.to_string()).encode(); 151 | 152 | named_bodies.push(if skip_if.iter().all(Option::is_none) { 153 | let (first_key, rest_keys) = keys.split_first().expect("zero fields"); 154 | let (first_value, rest_values) = values.split_first().expect("zero fields"); 155 | 156 | let start = format!("{{{variant_name}:{{{first_key}",); 157 | let rest_keys = rest_keys 158 | .iter() 159 | .map(|k| format!(",{k}")) 160 | .collect::>(); 161 | 162 | quote! { 163 | #ident::#named_ident{#(#values),*} => { 164 | writer.write_all(#start.as_bytes())?; 165 | #first_value.json_write(writer)?; 166 | #( 167 | writer.write_all(#rest_keys.as_bytes())?; 168 | #rest_values.json_write(writer)?; 169 | 170 | )* 171 | writer.write_all(b"}}") 172 | } 173 | } 174 | } else { 175 | let writes = keys 176 | .iter() 177 | .zip(values.iter()) 178 | .zip(skip_if.iter()) 179 | .map(|((k, v), s)| { 180 | if let Some(s) = s { 181 | quote! { 182 | 183 | if !#s(#v) { 184 | if has_written_key { 185 | writer.write_all(b",")?; 186 | } 187 | has_written_key = true; 188 | writer.write_all(#k.as_bytes())?; 189 | #v.json_write(writer)?; 190 | } 191 | } 192 | } else { 193 | quote! { 194 | if has_written_key { 195 | writer.write_all(b",")?; 196 | } 197 | has_written_key = true; 198 | writer.write_all(#k.as_bytes())?; 199 | #v.json_write(writer)?; 200 | } 201 | } 202 | }) 203 | .collect::>(); 204 | let prefix = format!("{{{variant_name}:{{"); 205 | quote! { 206 | #ident::#named_ident{#(#values),*} => { 207 | writer.write_all(#prefix.as_bytes())?; 208 | let mut has_written_key = false; 209 | #( 210 | #writes 211 | )* 212 | writer.write_all(b"}}") 213 | } 214 | } 215 | }); 216 | } 217 | let named = quote! {#(#named_bodies),*}; 218 | 219 | if !named.is_empty() { 220 | body_elements.push(named); 221 | } 222 | 223 | let match_body = quote! { 224 | #(#body_elements),* 225 | }; 226 | 227 | let expanded = quote! { 228 | impl #generics simd_json_derive::Serialize for #ident #generics { 229 | #[inline] 230 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 231 | where 232 | W: std::io::Write { 233 | match self { 234 | #match_body 235 | } 236 | 237 | } 238 | } 239 | }; 240 | TokenStream::from(expanded) 241 | } 242 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/serialize/struct.rs: -------------------------------------------------------------------------------- 1 | use syn::{DataStruct, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident}; 2 | 3 | use crate::args::StructAttrs; 4 | 5 | pub(super) mod named; 6 | pub(super) mod unnamed; 7 | 8 | pub(crate) fn derive( 9 | attrs: StructAttrs, 10 | ident: Ident, 11 | generics: Generics, 12 | defn: DataStruct, 13 | ) -> proc_macro::TokenStream { 14 | match defn { 15 | DataStruct { 16 | fields: Fields::Unnamed(FieldsUnnamed { unnamed, .. }), 17 | .. 18 | } => unnamed::derive(attrs, ident, generics, unnamed), 19 | // Named 20 | DataStruct { 21 | fields: Fields::Named(FieldsNamed { named, .. }), 22 | .. 23 | } => named::derive(attrs, ident, generics, named), 24 | DataStruct { 25 | fields: Fields::Unit, 26 | .. 27 | } => { 28 | todo!("Unit structs are not implemented") 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/serialize/struct/named.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::Ident; 3 | use quote::quote; 4 | use syn::{punctuated::Punctuated, token::Comma, Field, Generics}; 5 | 6 | use crate::args::StructAttrs; 7 | 8 | /// Named struct as `Struct(u8)` or `Struct(u8, String)` 9 | pub(crate) fn derive( 10 | attrs: StructAttrs, 11 | ident: Ident, 12 | generics: Generics, 13 | fields: Punctuated, 14 | ) -> proc_macro::TokenStream { 15 | let mut keys = Vec::new(); 16 | let mut values = Vec::new(); 17 | let mut skip_if = Vec::new(); 18 | 19 | for f in &fields { 20 | let ident = f.ident.clone().expect("Missing ident"); 21 | let name = attrs.name(f); 22 | keys.push(name); 23 | values.push(ident); 24 | skip_if.push(attrs.skip_serializing_if(f)); 25 | } 26 | let expanded = if skip_if.iter().all(Option::is_none) { 27 | if let Some((first, rest)) = keys.split_first_mut() { 28 | *first = format!("{{{}", first); 29 | for r in rest { 30 | *r = format!(",{}", r); 31 | } 32 | }; 33 | 34 | quote! { 35 | impl #generics simd_json_derive::Serialize for #ident #generics { 36 | #[inline] 37 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 38 | where 39 | W: std::io::Write { 40 | #( 41 | writer.write_all(#keys.as_bytes())?; 42 | self.#values.json_write(writer)?; 43 | )* 44 | writer.write_all(b"}") 45 | } 46 | } 47 | } 48 | } else { 49 | let writes = keys 50 | .iter() 51 | .zip(values.iter()) 52 | .zip(skip_if.iter()) 53 | .map(|((k, v), s)| { 54 | if let Some(s) = s { 55 | quote! { 56 | if !#s(&self.#v) { 57 | if has_written_key { 58 | writer.write_all(b",")?; 59 | } 60 | has_written_key = true; 61 | writer.write_all(#k.as_bytes())?; 62 | self.#v.json_write(writer)?; 63 | } 64 | } 65 | } else { 66 | quote! { 67 | if has_written_key { 68 | writer.write_all(b",")?; 69 | } 70 | has_written_key = true; 71 | writer.write_all(#k.as_bytes())?; 72 | self.#v.json_write(writer)?; 73 | } 74 | } 75 | }) 76 | .collect::>(); 77 | quote! { 78 | impl #generics simd_json_derive::Serialize for #ident #generics { 79 | #[inline] 80 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 81 | where 82 | W: std::io::Write { 83 | writer.write_all(b"{")?; 84 | let mut has_written_key = false; 85 | #( 86 | #writes 87 | )* 88 | writer.write_all(b"}") 89 | } 90 | } 91 | } 92 | }; 93 | TokenStream::from(expanded) 94 | } 95 | -------------------------------------------------------------------------------- /simd-json-derive-int/src/serialize/struct/unnamed.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use proc_macro2::Ident; 3 | use quote::quote; 4 | use syn::token::Comma; 5 | use syn::{punctuated::Punctuated, Field, Generics}; 6 | 7 | use crate::args::StructAttrs; 8 | 9 | /// Unnamed struct as `Struct(u8)` or `Struct(u8, String)` 10 | pub(crate) fn derive( 11 | _attrs: StructAttrs, 12 | ident: Ident, 13 | generics: Generics, 14 | fields: Punctuated, 15 | ) -> proc_macro::TokenStream { 16 | if fields.len() == 1 { 17 | let expanded = quote! { 18 | impl #generics simd_json_derive::Serialize for #ident #generics { 19 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 20 | where 21 | W: std::io::Write { 22 | self.0.json_write(writer) 23 | } 24 | } 25 | }; 26 | TokenStream::from(expanded) 27 | } else { 28 | let keys: Vec<_> = fields 29 | .iter() 30 | .enumerate() 31 | .map(|(i, _)| syn::Index::from(i)) 32 | .skip(1) 33 | .collect(); 34 | let expanded = quote! { 35 | impl #generics simd_json_derive::Serialize for #ident #generics { 36 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 37 | where 38 | W: std::io::Write { 39 | writer.write_all(b"[")?; 40 | self.0.json_write(writer)?; 41 | #( 42 | writer.write_all(b",")?; 43 | self.#keys.json_write(writer)?; 44 | )* 45 | writer.write_all(b"]") 46 | } 47 | } 48 | }; 49 | TokenStream::from(expanded) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | use std::num::TryFromIntError; 2 | 3 | use simd_json::Buffers; 4 | 5 | use crate::Tape; 6 | 7 | fn expected(fields: &[&str]) -> String { 8 | fields 9 | .iter() 10 | .map(|f| format!("`{f}`")) 11 | .collect::>() 12 | .join(", ") 13 | } 14 | /// Deserialisation error 15 | #[derive(Debug, thiserror::Error, PartialEq)] 16 | pub enum Error { 17 | /// Error from simd-json 18 | #[error("json error: {0:?}")] 19 | Json(simd_json::ErrorType), 20 | 21 | /// Error from simd-json 22 | #[error(transparent)] 23 | Simd(#[from] simd_json::Error), 24 | 25 | /// Missing field 26 | #[error("missing field: `{0}`")] 27 | MissingField(&'static str), 28 | /// Unexpected field 29 | #[error( 30 | "unknown field `{unknown_field}`, expected one of {}", 31 | expected(possible_field_names) 32 | )] 33 | UnknownField { 34 | unknown_field: String, 35 | possible_field_names: &'static [&'static str], 36 | }, 37 | #[error("unnamed enum field `{0}` is not an array")] 38 | FieldNotAnArray(&'static str), 39 | #[error("unknwon enum variant `{0}`")] 40 | UnknownEnumVariant(String), 41 | #[error("invalid enum representation, needs to be either a string or an object")] 42 | InvalidEnumRepresentation, 43 | #[error("invalid struct representation, needs to be an object")] 44 | InvalidStructRepresentation, 45 | #[error("Unexpected e,nd of input")] 46 | EOF, 47 | #[error("Invalid integer number")] 48 | InvalidNumber(#[from] TryFromIntError), 49 | #[error("Custom error: {0}")] 50 | Custom(String), 51 | #[error("The universe is broken: {0}")] 52 | BrokenUniverse(#[from] std::convert::Infallible), 53 | } 54 | 55 | impl Error { 56 | /// Create a custom error 57 | pub fn custom(msg: T) -> Self { 58 | Error::Custom(msg.to_string()) 59 | } 60 | /// Expected String error 61 | pub const fn expected_string() -> Self { 62 | Error::Json(simd_json::ErrorType::ExpectedString) 63 | } 64 | /// Expected Map error 65 | pub const fn expected_map() -> Self { 66 | Error::Json(simd_json::ErrorType::ExpectedMap) 67 | } 68 | /// Expected Array error 69 | pub const fn expected_array() -> Self { 70 | Error::Json(simd_json::ErrorType::ExpectedArray) 71 | } 72 | /// Expected Float error 73 | pub const fn expected_float() -> Self { 74 | Error::Json(simd_json::ErrorType::ExpectedFloat) 75 | } 76 | /// Expected Null error 77 | pub fn expected_null() -> Self { 78 | Error::Json(simd_json::ErrorType::ExpectedNull) 79 | } 80 | /// Expected Integer error 81 | pub fn expected_integer() -> Self { 82 | Error::Json(simd_json::ErrorType::ExpectedInteger) 83 | } 84 | /// Expected Boolean error 85 | pub fn expected_boolean() -> Self { 86 | Error::Json(simd_json::ErrorType::ExpectedBoolean) 87 | } 88 | } 89 | 90 | // Deserialisation result 91 | pub type Result = std::result::Result; 92 | 93 | pub trait Deserialize<'input> { 94 | fn from_tape(tape: &mut Tape<'input>) -> Result 95 | where 96 | Self: Sized + 'input; 97 | 98 | #[inline] 99 | fn from_slice(json: &'input mut [u8]) -> Result 100 | where 101 | Self: Sized + 'input, 102 | { 103 | let tape = simd_json::to_tape(json)?; 104 | let mut itr = tape.0.into_iter().peekable(); 105 | Self::from_tape(&mut itr) 106 | } 107 | 108 | #[inline] 109 | fn from_slice_with_buffers(json: &'input mut [u8], buffers: &mut Buffers) -> Result 110 | where 111 | Self: Sized + 'input, 112 | { 113 | let tape = simd_json::Deserializer::from_slice_with_buffers(json, buffers)?.into_tape(); 114 | let mut itr = tape.0.into_iter().peekable(); 115 | Self::from_tape(&mut itr) 116 | } 117 | 118 | #[inline] 119 | /// # Safety 120 | /// 121 | /// user must not use the string afterwards 122 | /// as it most likely will no longer contain valid utf-8 123 | unsafe fn from_str(json: &'input mut str) -> Result 124 | where 125 | Self: Sized + 'input, 126 | { 127 | Self::from_slice(json.as_bytes_mut()) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/impls.rs: -------------------------------------------------------------------------------- 1 | mod array; 2 | #[cfg(feature = "impl-chrono")] 3 | mod chrono; 4 | mod collections; 5 | mod deref; 6 | mod primitives; 7 | mod simdjson; 8 | mod string; 9 | mod tpl; 10 | use crate::{de, *}; 11 | use value_trait::generator::BaseGenerator; 12 | 13 | impl Serialize for Option 14 | where 15 | T: Serialize, 16 | { 17 | #[inline] 18 | fn json_write(&self, writer: &mut W) -> io::Result<()> 19 | where 20 | W: Write, 21 | { 22 | if let Some(e) = self { 23 | e.json_write(writer) 24 | } else { 25 | writer.write_all(b"null") 26 | } 27 | } 28 | } 29 | 30 | impl<'input, T> Deserialize<'input> for Option 31 | where 32 | T: Deserialize<'input>, 33 | { 34 | #[inline] 35 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 36 | where 37 | Self: Sized + 'input, 38 | { 39 | if let Some(simd_json::Node::Static(simd_json::StaticNode::Null)) = tape.peek() { 40 | tape.next(); 41 | Ok(None) 42 | } else { 43 | Ok(Some(T::from_tape(tape)?)) 44 | } 45 | } 46 | } 47 | 48 | impl Serialize for std::result::Result 49 | where 50 | TOk: Serialize, 51 | TErr: Serialize, 52 | { 53 | #[inline] 54 | fn json_write(&self, writer: &mut W) -> io::Result<()> 55 | where 56 | W: Write, 57 | { 58 | match self { 59 | Ok(e) => { 60 | writer.write_all(b"{\"Ok\":")?; 61 | e.json_write(writer)?; 62 | writer.write_all(b"}") 63 | } 64 | Err(e) => { 65 | writer.write_all(b"{\"Err\":")?; 66 | e.json_write(writer)?; 67 | writer.write_all(b"}") 68 | } 69 | } 70 | } 71 | } 72 | 73 | impl<'input, TOk, TErr> Deserialize<'input> for std::result::Result 74 | where 75 | TOk: Deserialize<'input>, 76 | TErr: Deserialize<'input>, 77 | { 78 | #[inline] 79 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 80 | where 81 | Self: Sized + 'input, 82 | { 83 | if let Some(simd_json::Node::Object { len: 1, .. }) = tape.next() { 84 | match tape.next() { 85 | Some(simd_json::Node::String("Ok")) => Ok(Ok(TOk::from_tape(tape)?)), 86 | Some(simd_json::Node::String("Err")) => Ok(Err(TErr::from_tape(tape)?)), 87 | Some(simd_json::Node::String("ok")) => Ok(Ok(TOk::from_tape(tape)?)), 88 | Some(simd_json::Node::String("err")) => Ok(Err(TErr::from_tape(tape)?)), 89 | _ => Err(de::Error::custom("result not `Ok` or `Err`")), 90 | } 91 | } else { 92 | Err(de::Error::InvalidStructRepresentation) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/impls/array.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::mem::MaybeUninit; 3 | use std::ptr; 4 | 5 | // taken from https://github.com/rust-lang/rust/blob/95f6a01e8f8fb121ded7d0eaa86906437cb08652/library/core/src/array/mod.rs#L843 6 | struct Guard<'a, T, const N: usize> { 7 | pub array: &'a mut [MaybeUninit; N], // we include size for a small optimization of pointer size 8 | pub initialized: usize, 9 | } 10 | 11 | impl Guard<'_, T, N> { 12 | #[inline] 13 | pub unsafe fn push_unchecked(&mut self, item: T) { 14 | // SAFETY: If `initialized` was correct before and the caller does not 15 | // invoke this method more than N times then writes will be in-bounds 16 | // and slots will not be initialized more than once. 17 | unsafe { 18 | self.array.get_unchecked_mut(self.initialized).write(item); 19 | self.initialized = self.initialized.wrapping_add(1); // unchecked_add is unstable 20 | } 21 | } 22 | } 23 | 24 | impl Drop for Guard<'_, T, N> { 25 | fn drop(&mut self) { 26 | debug_assert!(self.initialized <= N); 27 | 28 | // SAFETY: this slice will contain only initialized objects. 29 | unsafe { 30 | let slice = 31 | ptr::slice_from_raw_parts_mut(self.array.as_mut_ptr() as *mut T, self.initialized); 32 | ptr::drop_in_place(slice); 33 | } 34 | } 35 | } 36 | 37 | impl<'input, T, const N: usize> Deserialize<'input> for [T; N] 38 | where 39 | T: Deserialize<'input>, 40 | { 41 | #[inline] 42 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 43 | where 44 | Self: Sized + 'input, 45 | { 46 | if let Some(Node::Array { len, .. }) = tape.next() { 47 | if len != N { 48 | return Err(de::Error::custom( 49 | "expected array of len {N} found array of len {len}", 50 | )); 51 | } 52 | 53 | if N == 0 { 54 | // Safety: N is 0, and so *const [T; N] is *const [T; 0] 55 | return Ok(unsafe { ptr::read((&[]) as *const [T; N]) }); 56 | } 57 | 58 | // Safety: elements are still MaybeUninit 59 | let mut array: [MaybeUninit; N] = unsafe { MaybeUninit::uninit().assume_init() }; 60 | 61 | // Guard is here to make sure we drop 62 | let mut guard = Guard { 63 | array: &mut array, 64 | initialized: 0, 65 | }; 66 | 67 | // taken from https://github.com/rust-lang/rust/blob/95f6a01e8f8fb121ded7d0eaa86906437cb08652/library/core/src/array/mod.rs#L812 68 | while guard.initialized < N { 69 | let item = T::from_tape(tape)?; 70 | 71 | // SAFETY: The loop condition ensures we have space to push the item 72 | unsafe { guard.push_unchecked(item) }; 73 | } 74 | core::mem::forget(guard); 75 | 76 | // all elements initialized 77 | Ok(unsafe { array.map(|x| x.assume_init()) }) 78 | } else { 79 | Err(de::Error::expected_array()) 80 | } 81 | } 82 | } 83 | 84 | impl Serialize for [T; N] 85 | where 86 | T: Serialize, 87 | { 88 | #[inline] 89 | fn json_write(&self, writer: &mut W) -> io::Result<()> 90 | where 91 | W: Write, 92 | { 93 | // N is a compile time constant, this wont be checked at runtime 94 | if N == 0 { 95 | return writer.write_all(b"[]"); 96 | } 97 | 98 | let mut i = self.iter(); 99 | if let Some(first) = i.next() { 100 | writer.write_all(b"[")?; 101 | first.json_write(writer)?; 102 | for e in i { 103 | writer.write_all(b",")?; 104 | e.json_write(writer)?; 105 | } 106 | writer.write_all(b"]") 107 | } else { 108 | unreachable!() 109 | } 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod test { 115 | use crate::*; 116 | #[test] 117 | fn arr() { 118 | let s: [u8; 0] = []; 119 | assert_eq!(s.json_string().unwrap(), "[]"); 120 | assert_eq!([1].json_string().unwrap(), "[1]"); 121 | assert_eq!([1, 2].json_string().unwrap(), "[1,2]"); 122 | assert_eq!([1, 2, 3].json_string().unwrap(), "[1,2,3]"); 123 | } 124 | #[test] 125 | fn arr2() { 126 | assert_eq!( 127 | <[u8; 0] as Deserialize<'_>>::from_slice(&mut b"[]".to_vec()), 128 | Ok([]) 129 | ); 130 | assert_eq!( 131 | <[u8; 1] as Deserialize<'_>>::from_slice(&mut b"[1]".to_vec()), 132 | Ok([1]) 133 | ); 134 | assert_eq!( 135 | <[u8; 2] as Deserialize<'_>>::from_slice(&mut b"[1, 2]".to_vec()), 136 | Ok([1, 2]) 137 | ); 138 | assert_eq!( 139 | <[u8; 3] as Deserialize<'_>>::from_slice(&mut b"[1, 2, 3]".to_vec()), 140 | Ok([1, 2, 3]) 141 | ); 142 | } 143 | #[test] 144 | fn slice() { 145 | let s: [u8; 0] = []; 146 | assert_eq!(s.json_string().unwrap(), "[]"); 147 | assert_eq!([1].json_string().unwrap(), "[1]"); 148 | assert_eq!([1, 2].json_string().unwrap(), "[1,2]"); 149 | assert_eq!([1, 2, 3].json_string().unwrap(), "[1,2,3]"); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/impls/chrono.rs: -------------------------------------------------------------------------------- 1 | use super::{BaseGenerator, DummyGenerator}; 2 | use crate::*; 3 | use chrono::{DateTime, FixedOffset, TimeZone}; 4 | use std::{fmt, io}; 5 | 6 | impl Serialize for DateTime { 7 | /// Serialize into a rfc3339 time string 8 | /// 9 | /// See [the `serde` module](./serde/index.html) for alternate 10 | /// serializations. 11 | fn json_write(&self, writer: &mut W) -> io::Result<()> 12 | where 13 | W: Write, 14 | { 15 | struct FormatWrapped<'a, D: 'a> { 16 | inner: &'a D, 17 | } 18 | 19 | impl fmt::Display for FormatWrapped<'_, D> { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | self.inner.fmt(f) 22 | } 23 | } 24 | 25 | // Debug formatting is correct RFC3339, and it allows Zulu. 26 | DummyGenerator(writer).write_string(&format!("{}", FormatWrapped { inner: &self })) 27 | } 28 | } 29 | 30 | impl<'input> Deserialize<'input> for DateTime { 31 | #[inline] 32 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 33 | where 34 | Self: Sized + 'input, 35 | { 36 | match tape.next() { 37 | Some(simd_json::Node::String(s)) => DateTime::parse_from_rfc2822(s) 38 | .map_err(|e| de::Error::custom(format!("Invalid date string `{s}`: {e}"))), 39 | _ => Err(de::Error::expected_string()), 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/impls/collections.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | collections::{BTreeMap, HashMap}, 3 | ops::Range, 4 | }; 5 | 6 | #[cfg(feature = "heap-array")] 7 | use heap_array::HeapArray; 8 | 9 | use crate::{de, Deserialize, Result, Serialize, SerializeAsKey, Tape, Write}; 10 | use std::collections; 11 | use std::io; 12 | 13 | macro_rules! vec_like { 14 | ($t:ty) => { 15 | impl Serialize for $t 16 | where 17 | T: Serialize, 18 | { 19 | #[inline] 20 | fn json_write(&self, writer: &mut W) -> Result 21 | where 22 | W: Write, 23 | { 24 | let mut i = self.iter(); 25 | if let Some(first) = i.next() { 26 | writer.write_all(b"[")?; 27 | first.json_write(writer)?; 28 | for e in i { 29 | writer.write_all(b",")?; 30 | e.json_write(writer)?; 31 | } 32 | writer.write_all(b"]") 33 | } else { 34 | writer.write_all(b"[]") 35 | } 36 | } 37 | } 38 | }; 39 | } 40 | 41 | vec_like!(Vec); 42 | impl<'input, T> Deserialize<'input> for Vec 43 | where 44 | T: Deserialize<'input>, 45 | { 46 | #[inline] 47 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 48 | where 49 | Self: Sized + 'input, 50 | { 51 | let Some(simd_json::Node::Array { len, .. }) = tape.next() else { 52 | return Err(de::Error::expected_array()); 53 | }; 54 | let mut res = Vec::with_capacity(len); 55 | for _ in 0..len { 56 | let t = T::from_tape(tape)?; 57 | res.push(t); 58 | } 59 | Ok(res) 60 | } 61 | } 62 | 63 | vec_like!([T]); 64 | vec_like!(collections::VecDeque); 65 | impl<'input, T> Deserialize<'input> for collections::VecDeque 66 | where 67 | T: Deserialize<'input>, 68 | { 69 | #[inline] 70 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 71 | where 72 | Self: Sized + 'input, 73 | { 74 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 75 | let mut v = collections::VecDeque::new(); 76 | for _ in 0..len { 77 | v.push_back(T::from_tape(tape)?) 78 | } 79 | Ok(v) 80 | } else { 81 | Err(de::Error::expected_array()) 82 | } 83 | } 84 | } 85 | vec_like!(collections::BinaryHeap); 86 | impl<'input, T> Deserialize<'input> for collections::BinaryHeap 87 | where 88 | T: Deserialize<'input> + Ord, 89 | { 90 | #[inline] 91 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 92 | where 93 | Self: Sized + 'input, 94 | { 95 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 96 | let mut v = collections::BinaryHeap::new(); 97 | for _ in 0..len { 98 | v.push(T::from_tape(tape)?) 99 | } 100 | Ok(v) 101 | } else { 102 | Err(de::Error::expected_array()) 103 | } 104 | } 105 | } 106 | vec_like!(collections::BTreeSet); 107 | impl<'input, T> Deserialize<'input> for collections::BTreeSet 108 | where 109 | T: Deserialize<'input> + Ord, 110 | { 111 | #[inline] 112 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 113 | where 114 | Self: Sized + 'input, 115 | { 116 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 117 | let mut v = collections::BTreeSet::new(); 118 | for _ in 0..len { 119 | v.insert(T::from_tape(tape)?); 120 | } 121 | Ok(v) 122 | } else { 123 | Err(de::Error::expected_array()) 124 | } 125 | } 126 | } 127 | vec_like!(collections::LinkedList); 128 | impl<'input, T> Deserialize<'input> for collections::LinkedList 129 | where 130 | T: Deserialize<'input> + Ord, 131 | { 132 | #[inline] 133 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 134 | where 135 | Self: Sized + 'input, 136 | { 137 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 138 | let mut v = collections::LinkedList::new(); 139 | for _ in 0..len { 140 | v.push_back(T::from_tape(tape)?); 141 | } 142 | Ok(v) 143 | } else { 144 | Err(de::Error::expected_array()) 145 | } 146 | } 147 | } 148 | impl Serialize for collections::HashSet 149 | where 150 | T: Serialize, 151 | H: std::hash::BuildHasher, 152 | { 153 | #[inline] 154 | fn json_write(&self, writer: &mut W) -> io::Result<()> 155 | where 156 | W: Write, 157 | { 158 | let mut i = self.iter(); 159 | if let Some(first) = i.next() { 160 | writer.write_all(b"[")?; 161 | first.json_write(writer)?; 162 | for e in i { 163 | writer.write_all(b",")?; 164 | e.json_write(writer)?; 165 | } 166 | writer.write_all(b"]") 167 | } else { 168 | writer.write_all(b"[]") 169 | } 170 | } 171 | } 172 | impl<'input, T, H> Deserialize<'input> for collections::HashSet 173 | where 174 | T: Deserialize<'input> + std::hash::Hash + Eq, 175 | H: std::hash::BuildHasher + Default, 176 | { 177 | #[inline] 178 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 179 | where 180 | Self: Sized + 'input, 181 | { 182 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 183 | let mut v = collections::HashSet::with_capacity_and_hasher(len, H::default()); 184 | for _ in 0..len { 185 | v.insert(T::from_tape(tape)?); 186 | } 187 | Ok(v) 188 | } else { 189 | Err(de::Error::expected_array()) 190 | } 191 | } 192 | } 193 | 194 | macro_rules! ser_map_like { 195 | ($name:ident <$($generic:ident: $constraint:tt),*>) => { 196 | impl<$($generic: $constraint),*> Serialize for $name<$($generic),*> { 197 | #[inline] 198 | fn json_write(&self, writer: &mut W) -> Result 199 | where 200 | W: Write, 201 | { 202 | let mut i = self.iter(); 203 | if let Some((k, v)) = i.next() { 204 | writer.write_all(b"{")?; 205 | k.json_write(writer)?; 206 | writer.write_all(b":")?; 207 | v.json_write(writer)?; 208 | for (k, v) in i { 209 | writer.write_all(b",")?; 210 | k.json_write(writer)?; 211 | writer.write_all(b":")?; 212 | v.json_write(writer)?; 213 | } 214 | writer.write_all(b"}") 215 | } else { 216 | writer.write_all(b"{}") 217 | } 218 | } 219 | } 220 | }; 221 | } 222 | 223 | ser_map_like!(HashMap); 224 | ser_map_like!(BTreeMap); 225 | 226 | impl<'input, K, V, H> Deserialize<'input> for HashMap 227 | where 228 | K: Deserialize<'input> + std::hash::Hash + Eq, 229 | V: Deserialize<'input>, 230 | H: std::hash::BuildHasher + Default, 231 | { 232 | #[inline] 233 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 234 | where 235 | Self: Sized + 'input, 236 | { 237 | if let Some(simd_json::Node::Object { len, .. }) = tape.next() { 238 | let mut v = collections::HashMap::with_capacity_and_hasher(len, H::default()); 239 | for _ in 0..len { 240 | let k = K::from_tape(tape)?; 241 | v.insert(k, V::from_tape(tape)?); 242 | } 243 | Ok(v) 244 | } else { 245 | Err(de::Error::expected_map()) 246 | } 247 | } 248 | } 249 | 250 | impl<'input, K, V> Deserialize<'input> for BTreeMap 251 | where 252 | K: Deserialize<'input> + Ord, 253 | V: Deserialize<'input>, 254 | { 255 | #[inline] 256 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 257 | where 258 | Self: Sized + 'input, 259 | { 260 | if let Some(simd_json::Node::Object { len, .. }) = tape.next() { 261 | let mut v = collections::BTreeMap::new(); 262 | for _ in 0..len { 263 | let k = K::from_tape(tape)?; 264 | v.insert(k, V::from_tape(tape)?); 265 | } 266 | Ok(v) 267 | } else { 268 | Err(de::Error::expected_map()) 269 | } 270 | } 271 | } 272 | 273 | impl Serialize for Range 274 | where 275 | T: Serialize, 276 | { 277 | #[inline] 278 | fn json_write(&self, writer: &mut W) -> Result 279 | where 280 | W: Write, 281 | { 282 | writer.write_all(b"{\"start\":")?; 283 | self.start.json_write(writer)?; 284 | writer.write_all(b",\"end\":")?; 285 | self.end.json_write(writer)?; 286 | writer.write_all(b"}") 287 | } 288 | } 289 | 290 | impl<'input, T> Deserialize<'input> for Range 291 | where 292 | T: Deserialize<'input>, 293 | { 294 | #[inline] 295 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 296 | where 297 | Self: Sized + 'input, 298 | { 299 | if let Some(simd_json::Node::Object { len: 2, .. }) = tape.next() { 300 | match tape.next() { 301 | Some(simd_json::Node::String("start")) => { 302 | let start = Deserialize::from_tape(tape)?; 303 | if let Some(simd_json::Node::String("end")) = tape.next() { 304 | let end = Deserialize::from_tape(tape)?; 305 | Ok(start..end) 306 | } else { 307 | Err(de::Error::expected_string()) 308 | } 309 | } 310 | Some(simd_json::Node::String("end")) => { 311 | let end = Deserialize::from_tape(tape)?; 312 | if let Some(simd_json::Node::String("start")) = tape.next() { 313 | let start = Deserialize::from_tape(tape)?; 314 | Ok(start..end) 315 | } else { 316 | Err(de::Error::expected_string()) 317 | } 318 | } 319 | _ => Err(de::Error::expected_string()), 320 | } 321 | } else { 322 | Err(de::Error::expected_map()) 323 | } 324 | } 325 | } 326 | 327 | #[cfg(feature = "heap-array")] 328 | vec_like!(HeapArray); 329 | 330 | #[cfg(feature = "heap-array")] 331 | impl<'input, T: Deserialize<'input>> Deserialize<'input> for HeapArray { 332 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 333 | where 334 | Self: Sized + 'input, 335 | { 336 | if let Some(simd_json::Node::Array { len, .. }) = tape.next() { 337 | HeapArray::try_from_fn(len, |_| T::from_tape(tape)) 338 | } else { 339 | Err(de::Error::expected_array()) 340 | } 341 | } 342 | } 343 | 344 | #[cfg(test)] 345 | mod test { 346 | use std::ops::Range; 347 | 348 | use crate::*; 349 | #[test] 350 | fn vec() { 351 | let mut v: Vec = Vec::new(); 352 | assert_eq!(v.json_string().unwrap(), "[]"); 353 | 354 | v.push(1); 355 | let mut s = v.json_string().unwrap(); 356 | assert_eq!(s, "[1]"); 357 | let s: Vec = unsafe { Vec::from_str(s.as_mut_str()) }.unwrap(); 358 | assert_eq!(s, v); 359 | 360 | v.push(2); 361 | let mut s = v.json_string().unwrap(); 362 | assert_eq!(s, "[1,2]"); 363 | let s: Vec = unsafe { Vec::from_str(s.as_mut_str()) }.unwrap(); 364 | assert_eq!(s, v); 365 | 366 | v.push(3); 367 | let mut s = v.json_string().unwrap(); 368 | assert_eq!(s, "[1,2,3]"); 369 | let s: Vec = unsafe { Vec::from_str(s.as_mut_str()) }.unwrap(); 370 | assert_eq!(s, v); 371 | } 372 | 373 | #[test] 374 | fn range() { 375 | let r = 1..42; 376 | let mut v = r.json_vec().unwrap(); 377 | assert_eq!(br#"{"start":1,"end":42}"#, v.as_slice()); 378 | let r1 = Range::from_slice(v.as_mut_slice()).unwrap(); 379 | assert_eq!(r, r1); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /src/impls/deref.rs: -------------------------------------------------------------------------------- 1 | use crate::{de, Deserialize, Serialize, Tape}; 2 | 3 | impl<'input, T> Deserialize<'input> for Box 4 | where 5 | T: Deserialize<'input>, 6 | { 7 | #[inline] 8 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 9 | where 10 | Self: std::marker::Sized + 'input, 11 | { 12 | Ok(Box::new(T::from_tape(tape)?)) 13 | } 14 | } 15 | 16 | // Taken from https://docs.serde.rs/src/serde/ser/impls.rs.html#378 17 | macro_rules! deref_impl { 18 | ( 19 | $(#[doc = $doc:tt])* 20 | <$($desc:tt)+ 21 | ) => { 22 | $(#[doc = $doc])* 23 | impl <$($desc)+ { 24 | #[inline] 25 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 26 | where 27 | W: std::io::Write, 28 | { 29 | (**self).json_write(writer) 30 | } 31 | } 32 | }; 33 | } 34 | 35 | deref_impl!(<'a, T> Serialize for &'a T where T: ?Sized + Serialize); 36 | deref_impl!(<'a, T> Serialize for &'a mut T where T: ?Sized + Serialize); 37 | deref_impl!( Serialize for Box where T: Serialize); 38 | deref_impl!(<'a, T: ?Sized> Serialize for std::borrow::Cow<'a, T> where T: Serialize + ToOwned); 39 | -------------------------------------------------------------------------------- /src/impls/primitives.rs: -------------------------------------------------------------------------------- 1 | use crate::{de, Deserialize, Result, Serialize, Tape, Write}; 2 | use std::convert::TryFrom; 3 | 4 | impl Serialize for bool { 5 | #[inline] 6 | fn json_write(&self, writer: &mut W) -> Result { 7 | match *self { 8 | true => writer.write_all(b"true"), 9 | false => writer.write_all(b"false"), 10 | } 11 | } 12 | } 13 | 14 | impl<'input> Deserialize<'input> for bool { 15 | #[inline] 16 | fn from_tape(tape: &mut Tape<'input>) -> de::Result { 17 | if let Some(simd_json::Node::Static(simd_json::StaticNode::Bool(r))) = tape.next() { 18 | Ok(r) 19 | } else { 20 | Err(de::Error::expected_boolean()) 21 | } 22 | } 23 | } 24 | 25 | macro_rules! itoa { 26 | ($t:ty) => { 27 | impl Serialize for $t { 28 | #[inline] 29 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 30 | where 31 | W: Write, 32 | { 33 | let mut buffer = itoa::Buffer::new(); 34 | let s = buffer.format(*self); 35 | writer.write_all(s.as_bytes()) 36 | } 37 | } 38 | 39 | impl<'input> Deserialize<'input> for $t { 40 | #[inline] 41 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 42 | where 43 | Self: std::marker::Sized + 'input, 44 | { 45 | match tape.next() { 46 | Some(simd_json::Node::Static(simd_json::StaticNode::I64(i))) => { 47 | <$t>::try_from(i).map_err(de::Error::from) 48 | } 49 | Some(simd_json::Node::Static(simd_json::StaticNode::U64(i))) => { 50 | <$t>::try_from(i).map_err(de::Error::from) 51 | } 52 | #[cfg(feature = "128bit")] 53 | Some(simd_json::Node::Static(simd_json::StaticNode::U128(i))) => { 54 | <$t>::try_from(i).map_err(de::Error::from) 55 | } 56 | #[cfg(feature = "128bit")] 57 | Some(simd_json::Node::Static(simd_json::StaticNode::I128(i))) => { 58 | <$t>::try_from(i).map_err(de::Error::from) 59 | } 60 | _ => Err(de::Error::expected_integer()), 61 | } 62 | } 63 | } 64 | }; 65 | } 66 | 67 | itoa!(i8); 68 | itoa!(u8); 69 | itoa!(i16); 70 | itoa!(u16); 71 | itoa!(i32); 72 | itoa!(u32); 73 | itoa!(i64); 74 | itoa!(u64); 75 | itoa!(usize); 76 | itoa!(i128); 77 | itoa!(u128); 78 | 79 | macro_rules! ryu { 80 | ($t:ty) => { 81 | impl Serialize for $t { 82 | #[inline] 83 | fn json_write(&self, writer: &mut W) -> std::io::Result<()> 84 | where 85 | W: Write, 86 | { 87 | let mut buffer = ryu::Buffer::new(); 88 | let s = buffer.format_finite(*self); 89 | writer.write_all(s.as_bytes()) 90 | } 91 | } 92 | }; 93 | } 94 | ryu!(f64); 95 | ryu!(f32); 96 | 97 | impl<'input> Deserialize<'input> for f64 { 98 | #[inline] 99 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 100 | where 101 | Self: Sized + 'input, 102 | { 103 | match tape.next() { 104 | Some(simd_json::Node::Static(simd_json::StaticNode::F64(i))) => Ok(i), 105 | Some(simd_json::Node::Static(simd_json::StaticNode::I64(i))) => Ok(i as f64), 106 | Some(simd_json::Node::Static(simd_json::StaticNode::U64(i))) => Ok(i as f64), 107 | #[cfg(feature = "128bit")] 108 | Some(simd_json::Node::Static(simd_json::StaticNode::U128(i))) => Ok(i as f64), 109 | #[cfg(feature = "128bit")] 110 | Some(simd_json::Node::Static(simd_json::StaticNode::I128(i))) => Ok(i as f64), 111 | _ => Err(de::Error::expected_float()), 112 | } 113 | } 114 | } 115 | 116 | impl<'input> Deserialize<'input> for f32 { 117 | #[inline] 118 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 119 | where 120 | Self: Sized + 'input, 121 | { 122 | match tape.next() { 123 | Some(simd_json::Node::Static(simd_json::StaticNode::F64(i))) => Ok(i as f32), 124 | Some(simd_json::Node::Static(simd_json::StaticNode::I64(i))) => Ok(i as f32), 125 | Some(simd_json::Node::Static(simd_json::StaticNode::U64(i))) => Ok(i as f32), 126 | #[cfg(feature = "128bit")] 127 | Some(simd_json::Node::Static(simd_json::StaticNode::U128(i))) => Ok(i as f32), 128 | #[cfg(feature = "128bit")] 129 | Some(simd_json::Node::Static(simd_json::StaticNode::I128(i))) => Ok(i as f32), 130 | _ => Err(de::Error::expected_float()), 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/impls/simdjson.rs: -------------------------------------------------------------------------------- 1 | use crate::{de, Deserialize, Serialize}; 2 | use simd_json::{BorrowedValue, Node, OwnedValue}; 3 | use value_trait::{base::Writable, ValueBuilder}; 4 | 5 | impl Serialize for OwnedValue { 6 | fn json_write(&self, writer: &mut W) -> crate::Result 7 | where 8 | W: std::io::Write, 9 | { 10 | self.write(writer) 11 | } 12 | } 13 | impl Serialize for BorrowedValue<'_> { 14 | fn json_write(&self, writer: &mut W) -> crate::Result 15 | where 16 | W: std::io::Write, 17 | { 18 | self.write(writer) 19 | } 20 | } 21 | 22 | struct OwnedDeser<'input, 'tape>(&'tape mut crate::Tape<'input>); 23 | 24 | impl OwnedDeser<'_, '_> { 25 | #[inline(always)] 26 | fn parse(&mut self) -> OwnedValue { 27 | match self.0.next() { 28 | Some(Node::Static(s)) => OwnedValue::Static(s), 29 | Some(Node::String(s)) => OwnedValue::from(s), 30 | Some(Node::Array { len, .. }) => self.parse_array(len), 31 | Some(Node::Object { len, .. }) => self.parse_map(len), 32 | None => unreachable!("We have validated the tape in the second stage of parsing, this should never happen"), 33 | } 34 | } 35 | #[inline(always)] 36 | fn parse_array(&mut self, len: usize) -> OwnedValue { 37 | let mut res: Vec = Vec::with_capacity(len); 38 | // Rust doesn't optimize the normal loop away here 39 | // so we write our own avoiding the length 40 | // checks during push 41 | for _ in 0..len { 42 | res.push(self.parse()) 43 | } 44 | OwnedValue::Array(Box::new(res)) 45 | } 46 | 47 | #[inline(always)] 48 | fn parse_map(&mut self, len: usize) -> OwnedValue { 49 | let mut res = OwnedValue::object_with_capacity(len); 50 | 51 | // Since we checked if it's empty we know that we at least have one 52 | // element so we eat this 53 | if let OwnedValue::Object(ref mut res) = res { 54 | for _ in 0..len { 55 | if let Some(Node::String(key)) = self.0.next() { 56 | unsafe { res.insert_nocheck(key.into(), self.parse()) }; 57 | } else { 58 | unreachable!("We have validated the tape in the second stage of parsing, this should never happen") 59 | } 60 | } 61 | } else { 62 | unreachable!("We have generated this object and know it is nothing else") 63 | } 64 | res 65 | } 66 | } 67 | impl<'input> Deserialize<'input> for OwnedValue { 68 | fn from_tape(tape: &mut crate::Tape<'input>) -> de::Result 69 | where 70 | Self: Sized + 'input, 71 | { 72 | Ok(OwnedDeser(tape).parse()) 73 | } 74 | } 75 | 76 | struct BorrowedDeser<'input, 'tape>(&'tape mut crate::Tape<'input>); 77 | 78 | impl<'input> BorrowedDeser<'input, '_> { 79 | #[inline(always)] 80 | fn parse(&mut self) -> BorrowedValue<'input> { 81 | match self.0.next() { 82 | Some(Node::Static(s)) => BorrowedValue::Static(s), 83 | Some(Node::String(s)) => BorrowedValue::from(s), 84 | Some(Node::Array { len, .. }) => self.parse_array(len), 85 | Some(Node::Object { len, .. }) => self.parse_map(len), 86 | None => unreachable!("We have validated the tape in the second stage of parsing, this should never happen"), 87 | } 88 | } 89 | #[inline(always)] 90 | fn parse_array(&mut self, len: usize) -> BorrowedValue<'input> { 91 | let mut res: Vec> = Vec::with_capacity(len); 92 | for _ in 0..len { 93 | res.push(self.parse()); 94 | } 95 | BorrowedValue::Array(Box::new(res)) 96 | } 97 | 98 | #[inline(always)] 99 | fn parse_map(&mut self, len: usize) -> BorrowedValue<'input> { 100 | let mut res = BorrowedValue::object_with_capacity(len); 101 | 102 | // Since we checked if it's empty we know that we at least have one 103 | // element so we eat this 104 | if let BorrowedValue::Object(ref mut res) = res { 105 | for _ in 0..len { 106 | if let Some(Node::String(key)) = self.0.next() { 107 | unsafe { res.insert_nocheck(key.into(), self.parse()) }; 108 | } else { 109 | unreachable!("We have validated the tape in the second stage of parsing, this should never happen") 110 | } 111 | } 112 | } else { 113 | unreachable!("We have generated this object and know it is nothing else") 114 | } 115 | 116 | res 117 | } 118 | } 119 | impl<'input> Deserialize<'input> for BorrowedValue<'input> { 120 | fn from_tape(tape: &mut crate::Tape<'input>) -> de::Result 121 | where 122 | Self: Sized + 'input, 123 | { 124 | Ok(BorrowedDeser(tape).parse()) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/impls/string.rs: -------------------------------------------------------------------------------- 1 | use crate::{de, BaseGenerator, Deserialize, DummyGenerator, Result, Serialize, Tape, Write}; 2 | 3 | impl Serialize for String { 4 | #[inline] 5 | fn json_write(&self, writer: &mut W) -> Result 6 | where 7 | W: Write, 8 | { 9 | DummyGenerator(writer).write_string(self) 10 | } 11 | } 12 | 13 | impl<'input> Deserialize<'input> for String { 14 | #[inline] 15 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 16 | where 17 | Self: Sized + 'input, 18 | { 19 | match tape.next() { 20 | Some(simd_json::Node::String(s)) => Ok(String::from(s)), 21 | _ => Err(de::Error::expected_string()), 22 | } 23 | } 24 | } 25 | 26 | impl<'input> Deserialize<'input> for &'input str { 27 | #[inline] 28 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 29 | where 30 | Self: Sized + 'input, 31 | { 32 | match tape.next() { 33 | Some(simd_json::Node::String(s)) => Ok(s), 34 | _ => Err(de::Error::expected_string()), 35 | } 36 | } 37 | } 38 | 39 | impl Serialize for str { 40 | #[inline] 41 | fn json_write(&self, writer: &mut W) -> Result 42 | where 43 | W: Write, 44 | { 45 | DummyGenerator(writer).write_string(self) 46 | } 47 | } 48 | 49 | // "Figure this out". <-- PS. you cant as no one manages str's memory, 50 | // and its also dynamically sized so you cant really allocate it on the stack risking overflow 51 | // even if you should dynamically allocate on the stack which rust can't 52 | // you could do a Box though 53 | // 54 | // impl<'input> Deserialize<'input> for str { 55 | // #[inline] 56 | // fn from_tape( 57 | // tape: &mut Tape<'input>, 58 | // ) -> simd_json::Result 59 | // where 60 | // Self: std::marker::Sized + 'input, 61 | // { 62 | // match tape.next() { 63 | // Some(simd_json::Node::String(s)) => Ok(s), 64 | // _ => Err(de::Error::simd( 65 | // de::Error::expected_string(), 66 | // )), 67 | // } 68 | // } 69 | // } 70 | 71 | impl<'input> Deserialize<'input> for Box { 72 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 73 | where 74 | Self: Sized + 'input, 75 | { 76 | match tape.next() { 77 | Some(simd_json::Node::String(s)) => Ok(Box::from(s)), 78 | _ => Err(de::Error::expected_string()), 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/impls/tpl.rs: -------------------------------------------------------------------------------- 1 | use crate::{de, Deserialize, Result, Serialize, Tape, Write}; 2 | 3 | impl Serialize for () { 4 | #[inline] 5 | fn json_write(&self, writer: &mut W) -> Result 6 | where 7 | W: Write, 8 | { 9 | writer.write_all(b"null") 10 | } 11 | } 12 | 13 | impl<'input> Deserialize<'input> for () { 14 | #[inline] 15 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 16 | where 17 | Self: std::marker::Sized + 'input, 18 | { 19 | if let Some(simd_json::Node::Static(simd_json::StaticNode::Null)) = tape.next() { 20 | Ok(()) 21 | } else { 22 | Err(de::Error::expected_null()) 23 | } 24 | } 25 | } 26 | 27 | // takenn from https://docs.serde.rs/src/serde/ser/impls.rs.html#306 28 | 29 | macro_rules! tuple_impls { 30 | ($($len:expr => ($($n:tt $name:ident)+))+) => { 31 | $( 32 | impl<$($name),+> Serialize for ($($name,)+) 33 | where 34 | $($name: Serialize,)+ 35 | { 36 | #[inline] 37 | fn json_write(&self, writer: &mut W) -> Result 38 | where 39 | W: Write, 40 | { 41 | writer.write_all(b"[")?; 42 | $( 43 | if $n != 0 { 44 | writer.write_all(b",")?; 45 | } 46 | self.$n.json_write(writer)?; 47 | )+ 48 | writer.write_all(b"]") 49 | } 50 | } 51 | impl<'input, $($name),+> Deserialize<'input> for ($($name,)+) 52 | where 53 | $($name: Deserialize<'input>,)+ 54 | { 55 | #[inline] 56 | fn from_tape(tape: &mut Tape<'input>) -> de::Result 57 | where 58 | Self: std::marker::Sized + 'input, 59 | { 60 | if let Some(simd_json::Node::Array{len: $len, ..}) = tape.next() { 61 | Ok(( 62 | $($name::from_tape(tape)?,)+ 63 | )) 64 | } else { 65 | Err(de::Error::expected_array()) 66 | } 67 | } 68 | } 69 | )+ 70 | } 71 | } 72 | 73 | tuple_impls! { 74 | 1 => (0 T0) 75 | 2 => (0 T0 1 T1) 76 | 3 => (0 T0 1 T1 2 T2) 77 | 4 => (0 T0 1 T1 2 T2 3 T3) 78 | 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) 79 | 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) 80 | 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) 81 | 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) 82 | 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) 83 | 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) 84 | 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) 85 | 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) 86 | 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) 87 | 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) 88 | 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) 89 | 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) 90 | } 91 | 92 | #[cfg(test)] 93 | mod test { 94 | use crate::*; 95 | 96 | #[test] 97 | fn tpl() { 98 | assert_eq!((1).json_string().unwrap(), "1"); 99 | assert_eq!((1, 2).json_string().unwrap(), "[1,2]"); 100 | assert_eq!((1, 2, 3).json_string().unwrap(), "[1,2,3]"); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use simd_json::Node; 2 | pub use simd_json_derive_int::*; 3 | use std::io::{self, Write}; 4 | use std::iter::Peekable; 5 | use std::vec::IntoIter; 6 | use value_trait::generator::BaseGenerator; 7 | mod impls; 8 | pub type Result = io::Result<()>; 9 | 10 | pub type Tape<'input> = Peekable>>; 11 | 12 | pub mod de; 13 | 14 | pub use de::Deserialize; 15 | 16 | pub fn __skip(n: usize, tape: &mut Tape) { 17 | for _ in 0..n { 18 | match tape.next() { 19 | Some(Node::Array { count, .. }) => { 20 | for _ in 0..count { 21 | if tape.next().is_none() { 22 | return; 23 | } 24 | } 25 | } 26 | Some(Node::Object { count, .. }) => { 27 | for _ in 0..count { 28 | if tape.next().is_none() { 29 | return; 30 | } 31 | } 32 | } 33 | Some(_) => {} 34 | None => return, 35 | } 36 | } 37 | } 38 | 39 | pub trait Serialize { 40 | fn json_write(&self, writer: &mut W) -> Result 41 | where 42 | W: Write; 43 | 44 | #[inline] 45 | fn json_vec(&self) -> io::Result> { 46 | let mut v = Vec::with_capacity(512); 47 | self.json_write(&mut v)?; 48 | Ok(v) 49 | } 50 | #[inline] 51 | fn json_string(&self) -> io::Result { 52 | self.json_vec() 53 | .map(|v| unsafe { String::from_utf8_unchecked(v) }) 54 | } 55 | } 56 | 57 | pub trait SerializeAsKey { 58 | fn json_write(&self, writer: &mut W) -> Result 59 | where 60 | W: Write; 61 | } 62 | impl> SerializeAsKey for T { 63 | #[inline] 64 | fn json_write(&self, writer: &mut W) -> Result 65 | where 66 | W: Write, 67 | { 68 | let s: &str = self.as_ref(); 69 | s.json_write(writer) 70 | } 71 | } 72 | 73 | struct DummyGenerator(W); 74 | impl BaseGenerator for DummyGenerator { 75 | type T = W; 76 | #[inline] 77 | fn get_writer(&mut self) -> &mut ::T { 78 | &mut self.0 79 | } 80 | #[inline] 81 | fn write_min(&mut self, _: &[u8], _: u8) -> io::Result<()> { 82 | unimplemented!("write min is not supported") 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/attrs.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[test] 4 | fn rename() { 5 | #[derive(Serialize, Deserialize, PartialEq, Debug)] 6 | struct Bla<'f3> { 7 | #[serde(rename = "f3")] 8 | f1: u8, 9 | #[simd_json(rename = "f4")] 10 | f2: String, 11 | #[serde(borrow, rename = "f1")] 12 | f3: &'f3 str, 13 | f5: u8, 14 | } 15 | 16 | let b = Bla { 17 | f1: 1, 18 | f2: "snot".into(), 19 | f3: "snot", 20 | f5: 8, 21 | }; 22 | let mut serialized = b.json_string().unwrap(); 23 | println!("{}", &serialized); 24 | 25 | assert_eq!(r#"{"f3":1,"f4":"snot","f1":"snot","f5":8}"#, serialized); 26 | let b1 = unsafe { Bla::from_str(&mut serialized) } 27 | .expect("Expected serde roundtrip with rename to work"); 28 | println!("{:?}", &b1); 29 | assert_eq!(b, b1); 30 | } 31 | 32 | #[test] 33 | fn rename_all_camelcase() { 34 | #[derive(simd_json_derive::Serialize)] 35 | #[serde(rename_all = "camelCase")] 36 | struct Bla { 37 | field_one: u8, 38 | field_two: String, 39 | #[serde(rename = "f3")] 40 | field_three: u8, 41 | } 42 | 43 | let b = Bla { 44 | field_one: 1, 45 | field_two: "snot".into(), 46 | field_three: 8, 47 | }; 48 | println!("{}", b.json_string().unwrap()); 49 | assert_eq!( 50 | r#"{"fieldOne":1,"fieldTwo":"snot","f3":8}"#, 51 | b.json_string().unwrap() 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /tests/bench_structs.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[test] 4 | fn mesh() { 5 | #[derive(Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize)] 6 | pub struct Vector3 { 7 | pub x: f32, 8 | pub y: f32, 9 | pub z: f32, 10 | } 11 | 12 | impl Default for Vector3 { 13 | fn default() -> Self { 14 | Self { 15 | x: rand::random(), 16 | y: rand::random(), 17 | z: rand::random(), 18 | } 19 | } 20 | } 21 | #[derive( 22 | Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize, Default, 23 | )] 24 | pub struct Triangle { 25 | pub v0: Vector3, 26 | pub v1: Vector3, 27 | pub v2: Vector3, 28 | pub normal: Vector3, 29 | } 30 | #[derive( 31 | Serialize, Deserialize, PartialEq, Debug, serde::Serialize, serde::Deserialize, Default, 32 | )] 33 | pub struct Mesh { 34 | pub triangles: Vec, 35 | } 36 | 37 | let m = Mesh { 38 | triangles: (0..128).map(|_| Triangle::default()).collect(), 39 | }; 40 | 41 | let simd = m.json_string().unwrap(); 42 | let ser = serde_json::to_string(&m).unwrap(); 43 | println!("{simd} == {ser}"); 44 | assert_eq!(simd, ser); 45 | } 46 | -------------------------------------------------------------------------------- /tests/deser.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | // #[test] 4 | // fn unnamed1() { 5 | // #[derive(Serialize)] 6 | // struct Bla(u8); 7 | // let b = Bla(1); 8 | // println!("{}", b.json_string().unwrap()); 9 | // assert_eq!(r#"1"#, b.json_string().unwrap()) 10 | // } 11 | // #[test] 12 | // fn unnamed2() { 13 | // #[derive(Serialize)] 14 | // struct Bla(u8, u16); 15 | // let b = Bla(1, 2); 16 | // println!("{}", b.json_string().unwrap()); 17 | // assert_eq!(r#"[1,2]"#, b.json_string().unwrap()) 18 | // } 19 | 20 | #[test] 21 | fn deser() { 22 | #[derive(Serialize, Deserialize, PartialEq, Debug)] 23 | enum SnotBadger { 24 | Snot, 25 | Badger, 26 | } 27 | #[derive(Serialize, Deserialize, PartialEq, Debug)] 28 | struct Bla { 29 | f1: Option, 30 | f2: String, 31 | f3: SnotBadger, 32 | } 33 | 34 | let b = Bla { 35 | f1: Some(1), 36 | f2: "snot".into(), 37 | f3: SnotBadger::Snot, 38 | }; 39 | let mut s = b.json_string().unwrap(); 40 | println!("{}", s); 41 | assert_eq!(r#"{"f1":1,"f2":"snot","f3":"Snot"}"#, s); 42 | let b1 = unsafe { Bla::from_str(s.as_mut_str()) }.unwrap(); 43 | assert_eq!(b, b1); 44 | } 45 | 46 | #[test] 47 | fn opt() { 48 | #[derive(Deserialize, Debug, PartialEq)] 49 | struct MyString(String); 50 | #[derive(Deserialize, Debug, PartialEq)] 51 | struct Bla { 52 | logo: Option, 53 | name: MyString, 54 | } 55 | 56 | let mut s = String::from(r#"{"name":"snot"}"#); 57 | let b = unsafe { Bla::from_str(s.as_mut_str()) }.unwrap(); 58 | assert_eq!( 59 | b, 60 | Bla { 61 | logo: None, 62 | name: MyString("snot".into()) 63 | } 64 | ); 65 | 66 | let mut s = String::from(r#"{"name":"snot", "logo": null}"#); 67 | let b = unsafe { Bla::from_str(s.as_mut_str()) }.unwrap(); 68 | assert_eq!( 69 | b, 70 | Bla { 71 | logo: None, 72 | name: MyString("snot".into()) 73 | } 74 | ); 75 | 76 | let mut s = String::from(r#"{"name":"snot", "logo": "badger"}"#); 77 | let b = unsafe { Bla::from_str(s.as_mut_str()) }.unwrap(); 78 | assert_eq!( 79 | b, 80 | Bla { 81 | logo: Some("badger".into()), 82 | name: MyString("snot".into()) 83 | } 84 | ); 85 | 86 | let mut s = String::from(r#"{"logo": "badger"}"#); 87 | unsafe { 88 | assert!(Bla::from_str(s.as_mut_str()).is_err()); 89 | } 90 | 91 | let mut s = String::from(r#"{}"#); 92 | unsafe { 93 | assert!(Bla::from_str(s.as_mut_str()).is_err()); 94 | } 95 | 96 | let mut s = String::from(r#"{"name":"snot", "logo": 42}"#); 97 | unsafe { 98 | assert!(Bla::from_str(s.as_mut_str()).is_err()); 99 | } 100 | 101 | let mut s = String::from(r#"{"name":"snot", "logo": "badger", "snot":42}"#); 102 | unsafe { 103 | assert_eq!( 104 | Bla { 105 | name: MyString("snot".to_string()), 106 | logo: Some("badger".to_string()) 107 | }, 108 | Bla::from_str(s.as_mut_str()).expect("Didn't ignore unknown field 'snot'") 109 | ); 110 | } 111 | } 112 | #[test] 113 | fn event() { 114 | #[derive(Deserialize, Debug, PartialEq)] 115 | struct Ids(Vec<(u64, u64)>); 116 | 117 | #[derive(Deserialize, Debug, PartialEq)] 118 | struct Event { 119 | id: Ids, 120 | } 121 | 122 | let mut s = String::from(r#"{"id":[[0,0]]}"#); 123 | let e = unsafe { Event::from_str(s.as_mut_str()) }.unwrap(); 124 | 125 | assert_eq!( 126 | e, 127 | Event { 128 | id: Ids(vec![(0, 0)]), 129 | } 130 | ); 131 | } 132 | 133 | #[test] 134 | fn enum_ser() { 135 | #[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] 136 | pub enum StoredVariants { 137 | YesNo(bool), 138 | Small(u8, i8), 139 | Signy(i64), 140 | Stringy(String), 141 | Res(Result), 142 | } 143 | 144 | let mut s = String::from(r#"{"Small":[1,2]}"#); 145 | let e = unsafe { StoredVariants::from_str(s.as_mut_str()) }.unwrap(); 146 | assert_eq!(StoredVariants::Small(1, 2), e); 147 | 148 | let mut s = String::from(r#"{"YesNo":true}"#); 149 | let e = unsafe { StoredVariants::from_str(s.as_mut_str()) }.unwrap(); 150 | assert_eq!(StoredVariants::YesNo(true), e); 151 | 152 | let mut s = String::from(r#"{"Res":{"Ok":42}}"#); 153 | let e = unsafe { StoredVariants::from_str(s.as_mut_str()) }.unwrap(); 154 | assert_eq!(StoredVariants::Res(Ok(42)), e); 155 | 156 | let mut s = String::from(r#"{"Res":{"Err":"snot"}}"#); 157 | let e = unsafe { StoredVariants::from_str(s.as_mut_str()) }.unwrap(); 158 | assert_eq!(StoredVariants::Res(Err(String::from("snot"))), e); 159 | 160 | let e = StoredVariants::Res(Ok(42)).json_string().unwrap(); 161 | assert_eq!(r#"{"Res":{"Ok":42}}"#, e); 162 | 163 | let e = StoredVariants::Res(Err(String::from("snot"))) 164 | .json_string() 165 | .unwrap(); 166 | assert_eq!(r#"{"Res":{"Err":"snot"}}"#, e); 167 | } 168 | 169 | #[test] 170 | fn bar_string() { 171 | #[derive(Deserialize)] 172 | struct FooString { 173 | bar: String, 174 | } 175 | 176 | let mut json = br#"{"bar":"baz"}"#.to_vec(); 177 | let res = FooString::from_slice(&mut json); 178 | assert!(res.is_ok()); 179 | assert_eq!(res.unwrap().bar, "baz"); 180 | } 181 | 182 | #[test] 183 | fn foo_str() { 184 | #[derive(Deserialize)] 185 | struct FooStr<'de> { 186 | foo: &'de str, 187 | } 188 | let mut json = br#"{"foo":"bar"}"#.to_vec(); 189 | let res = FooStr::from_slice(&mut json); 190 | assert!(res.is_ok()); 191 | assert_eq!(res.unwrap().foo, "bar"); 192 | } 193 | -------------------------------------------------------------------------------- /tests/enum.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::Serialize; 2 | 3 | #[test] 4 | fn enum_stuff_01() { 5 | #[derive(simd_json_derive::Serialize)] 6 | enum Bla { 7 | Blubb, 8 | Wobbble(u8), 9 | Wobbble2(u8, u16), 10 | Gobble { k1: u8, k2: u16 }, 11 | } 12 | 13 | let b = Bla::Blubb; 14 | assert_eq!(r#""Blubb""#, b.json_string().unwrap()); 15 | let b = Bla::Wobbble(1); 16 | assert_eq!(r#"{"Wobbble":1}"#, b.json_string().unwrap()); 17 | let b = Bla::Wobbble2(1, 2); 18 | assert_eq!(r#"{"Wobbble2":[1,2]}"#, b.json_string().unwrap()); 19 | let b = Bla::Gobble { k1: 2, k2: 3 }; 20 | assert_eq!(r#"{"Gobble":{"k1":2,"k2":3}}"#, b.json_string().unwrap()); 21 | } 22 | 23 | #[test] 24 | fn enum_stuff_02() { 25 | #[derive(simd_json_derive::Serialize)] 26 | enum Bla { 27 | Blubb, 28 | Wobble, 29 | } 30 | 31 | let b = Bla::Blubb; 32 | assert_eq!(r#""Blubb""#, b.json_string().unwrap()); 33 | let b = Bla::Wobble; 34 | assert_eq!(r#""Wobble""#, b.json_string().unwrap()); 35 | } 36 | 37 | #[test] 38 | fn enum_stuff_01_lifeimte() { 39 | #[derive(simd_json_derive::Serialize)] 40 | enum Bla<'a, 'b> { 41 | Blubb, 42 | Wobbble(&'a str), 43 | Wobbble2(&'a str, &'b str), 44 | Gobble { k1: u8, k2: u16 }, 45 | } 46 | 47 | let b = Bla::Blubb; 48 | assert_eq!(r#""Blubb""#, b.json_string().unwrap()); 49 | let b = Bla::Wobbble("snot"); 50 | assert_eq!(r#"{"Wobbble":"snot"}"#, b.json_string().unwrap()); 51 | let b = Bla::Wobbble2("snot", "badger"); 52 | assert_eq!( 53 | r#"{"Wobbble2":["snot","badger"]}"#, 54 | b.json_string().unwrap() 55 | ); 56 | let b = Bla::Gobble { k1: 2, k2: 3 }; 57 | assert_eq!(r#"{"Gobble":{"k1":2,"k2":3}}"#, b.json_string().unwrap()); 58 | } 59 | 60 | #[test] 61 | fn enum_ser() { 62 | #[derive(Serialize)] 63 | pub enum StoredVariants { 64 | YesNo(bool), 65 | Small(u8, i8), 66 | } 67 | 68 | let d = StoredVariants::YesNo(true); 69 | assert_eq!(r#"{"YesNo":true}"#, d.json_string().unwrap()); 70 | 71 | let d = StoredVariants::Small(1, 2); 72 | assert_eq!(r#"{"Small":[1,2]}"#, d.json_string().unwrap()); 73 | 74 | // let e = StoredVariants::from_str(s.as_mut_str()).unwrap(); 75 | } 76 | -------------------------------------------------------------------------------- /tests/rename.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::Deserialize; 2 | 3 | #[test] 4 | fn opt() { 5 | #[derive(Deserialize, PartialEq, Debug)] 6 | #[serde(rename_all = "camelCase")] 7 | struct Ranme { 8 | logo_name: Option, 9 | #[serde(rename = "Name")] 10 | name: String, 11 | } 12 | let mut s = r#"{"Name": "snot", "logoName": "badger"}"#.to_string(); 13 | let de = 14 | unsafe { Ranme::from_str(s.as_mut_str()) }.expect("expected serialize with rename to work"); 15 | assert_eq!( 16 | Ranme { 17 | logo_name: Some("badger".to_string()), 18 | name: "snot".to_string() 19 | }, 20 | de 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /tests/simdjson.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[test] 4 | fn owned_value() { 5 | let input = r#"{"snot":["badger",true,false,12.5,null,{"inner":[{}]},[[]]]}"#.to_string(); 6 | let value = simd_json::owned::to_value(unsafe { input.clone().as_bytes_mut() }) 7 | .expect("Expected the literal to work"); 8 | let res = value.json_string(); 9 | assert!(res.is_ok()); 10 | let mut serialized = res.ok().unwrap(); 11 | assert_eq!(input, serialized); 12 | let deserialized = unsafe { simd_json::owned::Value::from_str(serialized.as_mut_str()) } 13 | .expect("Expected serialized input to be deserialized ok"); 14 | println!("{}", deserialized); 15 | assert_eq!(value, deserialized); 16 | } 17 | -------------------------------------------------------------------------------- /tests/skip_serializing_if.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::Serialize; 2 | 3 | #[test] 4 | fn skip_in_struct() { 5 | #[derive(Serialize)] 6 | struct Bla { 7 | #[serde(skip_serializing_if = "Option::is_none")] 8 | f1: Option, 9 | #[serde(skip_serializing_if = "Option::is_none")] 10 | f2: Option, 11 | f3: Option, 12 | } 13 | 14 | let b = Bla { 15 | f1: None, 16 | f2: None, 17 | f3: None, 18 | }; 19 | let s = b.json_string().unwrap(); 20 | assert_eq!(r#"{"f3":null}"#, s); 21 | 22 | let b = Bla { 23 | f1: Some(1), 24 | f2: None, 25 | f3: None, 26 | }; 27 | let s = b.json_string().unwrap(); 28 | assert_eq!(r#"{"f1":1,"f3":null}"#, s); 29 | 30 | let b = Bla { 31 | f1: None, 32 | f2: Some(2), 33 | f3: None, 34 | }; 35 | let s = b.json_string().unwrap(); 36 | assert_eq!(r#"{"f2":2,"f3":null}"#, s); 37 | 38 | let b = Bla { 39 | f1: Some(1), 40 | f2: None, 41 | f3: Some(3), 42 | }; 43 | let s = b.json_string().unwrap(); 44 | assert_eq!(r#"{"f1":1,"f3":3}"#, s); 45 | } 46 | 47 | #[test] 48 | fn skip_in_enum() { 49 | #[derive(Serialize)] 50 | enum Bla { 51 | Blubb { 52 | #[serde(skip_serializing_if = "Option::is_none")] 53 | f1: Option, 54 | #[serde(skip_serializing_if = "Option::is_none")] 55 | f2: Option, 56 | f3: Option, 57 | }, 58 | Blargh { 59 | #[serde(skip_serializing_if = "Option::is_none")] 60 | f1: Option, 61 | #[serde(skip_serializing_if = "Option::is_none")] 62 | f2: Option, 63 | f3: Option, 64 | }, 65 | } 66 | 67 | let b = Bla::Blubb { 68 | f1: None, 69 | f2: None, 70 | f3: None, 71 | }; 72 | let s = b.json_string().unwrap(); 73 | assert_eq!(r#"{"Blubb":{"f3":null}}"#, s); 74 | 75 | let b = Bla::Blubb { 76 | f1: Some(1), 77 | f2: None, 78 | f3: None, 79 | }; 80 | let s = b.json_string().unwrap(); 81 | assert_eq!(r#"{"Blubb":{"f1":1,"f3":null}}"#, s); 82 | 83 | let b = Bla::Blargh { 84 | f1: None, 85 | f2: Some(2), 86 | f3: None, 87 | }; 88 | let s = b.json_string().unwrap(); 89 | assert_eq!(r#"{"Blargh":{"f2":2,"f3":null}}"#, s); 90 | 91 | let b = Bla::Blargh { 92 | f1: Some(1), 93 | f2: None, 94 | f3: Some(3), 95 | }; 96 | let s = b.json_string().unwrap(); 97 | assert_eq!(r#"{"Blargh":{"f1":1,"f3":3}}"#, s); 98 | } 99 | -------------------------------------------------------------------------------- /tests/struct.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[test] 4 | fn unnamed1() { 5 | #[derive(simd_json_derive::Serialize)] 6 | struct Bla(u8); 7 | let b = Bla(1); 8 | println!("{}", b.json_string().unwrap()); 9 | assert_eq!(r#"1"#, b.json_string().unwrap()) 10 | } 11 | 12 | #[test] 13 | fn unnamed2() { 14 | #[derive(simd_json_derive::Serialize)] 15 | struct Bla(u8, u16); 16 | let b = Bla(1, 2); 17 | println!("{}", b.json_string().unwrap()); 18 | assert_eq!(r#"[1,2]"#, b.json_string().unwrap()) 19 | } 20 | 21 | #[test] 22 | fn named() { 23 | #[derive(simd_json_derive::Serialize, simd_json_derive::Deserialize, PartialEq, Debug)] 24 | struct Bla { 25 | f1: u8, 26 | f2: String, 27 | } 28 | 29 | let b = Bla { 30 | f1: 1, 31 | f2: "snot".into(), 32 | }; 33 | let mut s = b.json_string().unwrap(); 34 | println!("{}", s); 35 | assert_eq!(r#"{"f1":1,"f2":"snot"}"#, s); 36 | let b1 = unsafe { Bla::from_str(s.as_mut_str()) }.unwrap(); 37 | assert_eq!(b, b1); 38 | } 39 | 40 | #[test] 41 | fn unnamed1_lifetime() { 42 | #[derive(simd_json_derive::Serialize)] 43 | struct BlaU1L<'a>(&'a str); 44 | let b = BlaU1L("snot"); 45 | println!("{}", b.json_string().unwrap()); 46 | assert_eq!(r#""snot""#, b.json_string().unwrap()) 47 | } 48 | 49 | #[test] 50 | fn unnamed2_lifetime() { 51 | #[derive(simd_json_derive::Serialize)] 52 | struct BlaU2L<'a, 'b>(&'a str, &'b str); 53 | let b = BlaU2L("hello", "world"); 54 | println!("{}", b.json_string().unwrap()); 55 | assert_eq!(r#"["hello","world"]"#, b.json_string().unwrap()) 56 | } 57 | 58 | #[test] 59 | fn named_lifetime() { 60 | #[derive(simd_json_derive::Serialize)] 61 | struct BlaN2L<'a, 'b> { 62 | f1: &'a str, 63 | f2: &'b str, 64 | } 65 | 66 | let b = BlaN2L { 67 | f1: "snot", 68 | f2: "badger", 69 | }; 70 | println!("{}", b.json_string().unwrap()); 71 | assert_eq!(r#"{"f1":"snot","f2":"badger"}"#, b.json_string().unwrap()) 72 | } 73 | 74 | #[test] 75 | fn borrowed() { 76 | #[derive(simd_json_derive::Serialize, simd_json_derive::Deserialize, PartialEq, Debug)] 77 | struct SIMDExample<'sin> { 78 | id: u64, 79 | #[serde(borrow)] 80 | id_str: &'sin str, 81 | } 82 | let mut s = r#"{"id":23,"id_str":"42"}"#.to_string(); 83 | unsafe { 84 | assert_eq!( 85 | SIMDExample { 86 | id: 23, 87 | id_str: "42" 88 | }, 89 | SIMDExample::from_str(s.as_mut_str()).unwrap() 90 | ); 91 | } 92 | } 93 | 94 | #[test] 95 | fn tpl_array() { 96 | #[derive(simd_json_derive::Serialize, simd_json_derive::Deserialize, PartialEq, Debug)] 97 | struct Bla { 98 | tpl: (u8, u8), 99 | array: [u8; 2], 100 | } 101 | let b = Bla { 102 | tpl: (1, 2), 103 | array: [3, 4], 104 | }; 105 | println!("{}", b.json_string().unwrap()); 106 | let mut s = r#"{"tpl":[1,2],"array":[3,4]}"#.to_string(); 107 | assert_eq!(s, b.json_string().unwrap()); 108 | unsafe { 109 | assert_eq!(b, Bla::from_str(s.as_mut_str()).unwrap()); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/unknown_fields.rs: -------------------------------------------------------------------------------- 1 | use simd_json_derive::{Deserialize, Serialize}; 2 | 3 | #[test] 4 | fn deny_unknown_fields() { 5 | #[derive(Serialize, Deserialize, Debug, PartialEq)] 6 | #[simd_json(deny_unknown_fields)] 7 | struct Strict { 8 | snot: String, 9 | badger: f64, 10 | opt: Option, 11 | } 12 | 13 | let mut s = r#"{"snot":"foo", "badger":0.5, "unknown": "bla"}"#.to_string(); 14 | let res = unsafe { Strict::from_str(s.as_mut_str()) }; 15 | assert!(res.is_err()); 16 | let err = res.err().unwrap(); 17 | assert_eq!( 18 | err.to_string(), 19 | "unknown field `unknown`, expected one of `snot`, `badger`, `opt`" 20 | ); 21 | 22 | let mut s = r#"{"unknown": "bla", "snot":"foo", "badger":0.5}"#.to_string(); 23 | let res = unsafe { Strict::from_str(s.as_mut_str()) }; 24 | assert!(res.is_err()); 25 | let err = res.err().unwrap(); 26 | assert_eq!( 27 | err.to_string(), 28 | "unknown field `unknown`, expected one of `snot`, `badger`, `opt`" 29 | ); 30 | } 31 | 32 | #[test] 33 | fn allow_unknown_fields() { 34 | #[derive(Serialize, Deserialize, Debug, PartialEq)] 35 | struct NonStrict<'de> { 36 | text: &'de str, 37 | num: u8, 38 | } 39 | 40 | let mut s = r#"{"text":"foo", "unknown": [1,2], "num": 3}"#.to_string(); 41 | let res = unsafe { NonStrict::from_str(s.as_mut_str()) }; 42 | assert!(res.is_ok()); 43 | assert_eq!( 44 | res.unwrap(), 45 | NonStrict { 46 | text: "foo", 47 | num: 3 48 | } 49 | ); 50 | let mut s = r#"{"unknown": {"snot": "badger"}, "num": 1, "text":"foo"}"#.to_string(); 51 | let res = unsafe { NonStrict::from_str(s.as_mut_str()) }; 52 | assert!(res.is_ok()); 53 | assert_eq!( 54 | res.unwrap(), 55 | NonStrict { 56 | text: "foo", 57 | num: 1 58 | } 59 | ); 60 | } 61 | 62 | #[test] 63 | fn missing_required_fields() { 64 | #[derive(Serialize, Deserialize, Debug, PartialEq)] 65 | struct SomeFields { 66 | snot: (u16, u32), 67 | badger: Option, 68 | something: Vec, 69 | } 70 | 71 | let mut s = r#"{}"#.to_string(); 72 | let res = unsafe { SomeFields::from_str(s.as_mut_str()) }; 73 | assert!(res.is_err()); 74 | let err = res.err().unwrap(); 75 | assert_eq!(err.to_string(), "missing field: `snot`"); 76 | 77 | s = r#"{"snot": [65535, 65536]}"#.to_string(); 78 | let res = unsafe { SomeFields::from_str(s.as_mut_str()) }; 79 | assert!(res.is_err()); 80 | let err = res.err().unwrap(); 81 | assert_eq!(err.to_string(), "missing field: `something`"); 82 | } 83 | --------------------------------------------------------------------------------