├── .github └── workflows │ ├── publish.yml │ └── toolchain.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE.txt ├── LICENSE-MIT.txt ├── README.md ├── comparison.csv ├── comparison.xlsx ├── examples ├── comparison.rs └── generate.rs ├── graphs ├── graph00.png ├── graph01.png ├── graph02.png └── graph03.png ├── src ├── array.rs ├── de.rs ├── lib.rs ├── macros.rs ├── number.rs ├── object.rs ├── ser.rs ├── string.rs ├── thin.rs └── value.rs └── test_data └── template.hbs /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | on: 2 | release: 3 | types: [published] 4 | 5 | name: Publish 6 | 7 | jobs: 8 | release: 9 | name: Release 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: dtolnay/rust-toolchain@stable 14 | - run: cargo login ${{secrets.CARGO_TOKEN}} 15 | - run: cargo publish 16 | -------------------------------------------------------------------------------- /.github/workflows/toolchain.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: CI 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | - uses: actions-rs/toolchain@v1 12 | with: 13 | profile: minimal 14 | toolchain: stable 15 | override: true 16 | - uses: actions-rs/cargo@v1 17 | with: 18 | command: check 19 | args: --all-features 20 | 21 | fmt: 22 | name: Rustfmt 23 | runs-on: ubuntu-latest 24 | steps: 25 | - uses: actions/checkout@v2 26 | - uses: actions-rs/toolchain@v1 27 | with: 28 | profile: minimal 29 | toolchain: stable 30 | override: true 31 | - run: rustup component add rustfmt 32 | - uses: actions-rs/cargo@v1 33 | with: 34 | command: fmt 35 | args: -- --check 36 | 37 | clippy: 38 | name: Clippy 39 | runs-on: ubuntu-latest 40 | steps: 41 | - uses: actions/checkout@v2 42 | - uses: actions-rs/toolchain@v1 43 | with: 44 | profile: minimal 45 | toolchain: stable 46 | override: true 47 | - run: rustup component add clippy 48 | - uses: actions-rs/cargo@v1 49 | with: 50 | command: clippy 51 | args: --all-features -- -D warnings 52 | 53 | test: 54 | name: Test 55 | runs-on: ubuntu-latest 56 | steps: 57 | - uses: actions/checkout@v2 58 | - uses: actions-rs/toolchain@v1 59 | with: 60 | profile: minimal 61 | toolchain: stable 62 | override: true 63 | - uses: actions-rs/cargo@v1 64 | with: 65 | command: test 66 | args: -- --test-threads=1 67 | 68 | test_miri: 69 | name: Test (Miri) 70 | runs-on: ubuntu-latest 71 | steps: 72 | - uses: actions/checkout@v2 73 | - uses: actions-rs/toolchain@v1 74 | with: 75 | profile: minimal 76 | toolchain: nightly 77 | override: true 78 | components: miri 79 | - uses: actions-rs/cargo@v1 80 | with: 81 | command: miri 82 | args: test 83 | env: 84 | MIRIFLAGS: "-Zmiri-disable-isolation" 85 | 86 | test_miri_32bit_linux: 87 | name: Test (Miri i686-unknown-linux-gnu) 88 | runs-on: ubuntu-latest 89 | steps: 90 | - uses: actions/checkout@v2 91 | - uses: actions-rs/toolchain@v1 92 | with: 93 | profile: minimal 94 | toolchain: nightly 95 | override: true 96 | components: miri 97 | target: i686-unknown-linux-gnu 98 | - uses: actions-rs/cargo@v1 99 | with: 100 | command: miri 101 | args: test --target i686-unknown-linux-gnu 102 | env: 103 | MIRIFLAGS: "-Zmiri-disable-isolation" 104 | 105 | test_miri_32bit_windows: 106 | name: Test (Miri i686-pc-windows-msvc) 107 | runs-on: ubuntu-latest 108 | steps: 109 | - uses: actions/checkout@v2 110 | - uses: actions-rs/toolchain@v1 111 | with: 112 | profile: minimal 113 | toolchain: nightly 114 | override: true 115 | components: miri 116 | target: i686-pc-windows-msvc 117 | - uses: actions-rs/cargo@v1 118 | with: 119 | command: miri 120 | args: test --target i686-pc-windows-msvc 121 | env: 122 | MIRIFLAGS: "-Zmiri-disable-isolation" 123 | 124 | test_nightly: 125 | name: Test (Nightly) 126 | runs-on: ubuntu-20.04 127 | steps: 128 | - uses: actions/checkout@v2 129 | - uses: actions-rs/toolchain@v1 130 | with: 131 | profile: minimal 132 | toolchain: nightly 133 | override: true 134 | components: llvm-tools-preview 135 | - uses: actions-rs/cargo@v1 136 | with: 137 | command: install 138 | args: cargo-binutils rustfilt 139 | - name: Build tests 140 | run: | 141 | cargo rustc --message-format=json --tests -- -Zinstrument-coverage | jq -r '.executable | strings' > executables.txt 142 | - name: Run tests 143 | run: | 144 | while read p; do 145 | exec "$p" --test-threads=1 146 | done lcov.info 153 | - name: Upload coverage report 154 | uses: codecov/codecov-action@v1 155 | with: 156 | fail_ci_if_error: true 157 | - uses: actions/upload-artifact@v4 158 | with: 159 | name: Upload coverage artifact 160 | path: lcov.info 161 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | /test_data/*.json 4 | /~$comparison.xlsx 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ijson" 3 | version = "0.1.4" 4 | authors = ["Diggory Blake "] 5 | edition = "2018" 6 | readme = "README.md" 7 | license = "MIT OR Apache-2.0" 8 | repository = "https://github.com/Diggsey/ijson" 9 | description = "A more memory efficient replacement for serde_json::Value" 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | [features] 13 | tracing = ["mockalloc/tracing"] 14 | 15 | [dependencies] 16 | dashmap = { version = "5.5", features = ["raw-api"] } 17 | lazy_static = "1.4.0" 18 | serde = "1.0.173" 19 | serde_json = "1.0.103" 20 | ctor = { version = "0.2.4", optional = true } 21 | indexmap = { version = "2.0.0", optional = true } 22 | 23 | [dev-dependencies] 24 | mockalloc = "0.1.2" 25 | ctor = "0.1.16" 26 | rand = "0.8.4" 27 | -------------------------------------------------------------------------------- /LICENSE-APACHE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ijson 2 | 3 | [![CI Status](https://github.com/Diggsey/ijson/workflows/CI/badge.svg)](https://github.com/Diggsey/ijson/actions?query=workflow%3ACI) 4 | [![Documentation](https://docs.rs/ijson/badge.svg)](https://docs.rs/ijson) 5 | [![crates.io](https://img.shields.io/crates/v/ijson.svg)](https://crates.io/crates/ijson) 6 | [![codecov](https://codecov.io/gh/Diggsey/ijson/branch/master/graph/badge.svg?token=XZ1UCUKSYB)](https://codecov.io/gh/Diggsey/ijson) 7 | 8 | This crate offers a replacement for `serde-json`'s `Value` type, which is 9 | significantly more memory efficient. 10 | 11 | As a ballpark figure, it will typically use half as much memory as 12 | `serde-json` when deserializing a value and the memory footprint of cloning 13 | a value is more than 7x smaller. 14 | 15 | ## Memory savings 16 | 17 | These graphs show memory savings as a function of JSON size (in bytes). The 18 | JSON is randomly generated using the template in the `test_data` folder 19 | using the javascript `dummyjson` tool. 20 | 21 | ![Peak memory usage when deserializing](graphs/graph00.png) 22 | ![Total allocations when deserializing](graphs/graph01.png) 23 | ![Memory overhead of cloning](graphs/graph02.png) 24 | ![Total allocations when cloning](graphs/graph03.png) 25 | 26 | You can reproduce them yourself by installing `dummy-json` from NPM, and then 27 | running these commands in the root directory: 28 | 29 | ``` 30 | cargo run --example generate --release 31 | cargo run --example comparison --release > comparison.csv 32 | ``` 33 | 34 | The `comparison.xlsx` Excel file uses this CSV as a data-source to generate 35 | the graphs. 36 | 37 | ## Usage 38 | 39 | ### `IValue` 40 | 41 | The primary type exposed by this crate is the `IValue` type. It is guaranteed 42 | to be pointer-sized and has a niche (so `Option` is also guaranteed 43 | to be pointer-sized). 44 | 45 | Compared to `serde_json::Value` this type is a struct rather than an enum, as 46 | this is necessary to achieve the important size reductions. This means that 47 | you cannot directly `match` on an `IValue` to determine its type. 48 | 49 | Instead, an `IValue` offers several ways to get at the inner type: 50 | 51 | - Destructuring using `IValue::destructure[{_ref,_mut}]()` 52 | 53 | These methods return wrapper enums which you _can_ directly match on, so 54 | these methods are the most direct replacement for matching on a `Value`. 55 | 56 | - Borrowing using `IValue::as_{array,object,string,number}[_mut]()` 57 | 58 | These methods return an `Option` of the corresponding reference if the 59 | type matches the one expected. These methods exist for the variants 60 | which are not `Copy`. 61 | 62 | - Converting using `IValue::into_{array,object,string,number}()` 63 | 64 | These methods return a `Result` of the corresponding type (or the 65 | original `IValue` if the type is not the one expected). These methods 66 | also exist for the variants which are not `Copy`. 67 | 68 | - Getting using `IValue::to_{bool,{i,u,f}{32,64}}[_lossy]}()` 69 | 70 | These methods return an `Option` of the corresponding type. These 71 | methods exist for types where the return value would be `Copy`. 72 | 73 | You can also check the type of the inner value without specifically 74 | accessing it using one of these methods: 75 | 76 | - Checking using `IValue::is_{null,bool,number,string,array,object,true,false}()` 77 | 78 | These methods exist for all types. 79 | 80 | - Getting the type with `IValue::type_()` 81 | 82 | This method returns the `ValueType` enum, which has a variant for each of the 83 | six JSON types. 84 | 85 | ### INumber 86 | 87 | The `INumber` type represents a JSON number. It is decoupled from any specific 88 | representation, and internally uses several. There is no way to determine the 89 | internal representation: instead the caller is expected to convert the number 90 | using one of the fallible `to_xxx` functions and handle the cases where the 91 | number does not convert to the desired type. 92 | 93 | Special floating point values (eg. NaN, Infinity, etc.) cannot be stored within 94 | an `INumber`. 95 | 96 | Whilst `INumber` does not consider `2.0` and `2` to be different numbers (ie. 97 | they will compare equal) it does allow you to distinguish them using the 98 | method `INumber::has_decimal_point()`. That said, calling `to_i32` on 99 | `2.0` will succeed with the value `2`. 100 | 101 | Currently `INumber` can store any number representable with an `f64`, `i64` or 102 | `u64`. It is expected that in the future it will be further expanded to store 103 | integers and possibly decimals to arbitrary precision, but that is not currently 104 | the case. 105 | 106 | Any number representable with an `i8` or a `u8` can be stored in an `INumber` 107 | without a heap allocation (so JSON byte arrays are relatively efficient). 108 | Integers up to 24 bits can be stored with a 4-byte heap allocation. 109 | 110 | ### IString 111 | 112 | The `IString` type is an interned, immutable string, and is where this crate 113 | gets its name. 114 | 115 | Cloning an `IString` is cheap, and it can be easily converted from `&str` or 116 | `String` types. Comparisons between `IString`s is a simple pointer 117 | comparison. 118 | 119 | The memory backing an `IString` is reference counted, so that unlike many 120 | string interning libraries, memory is not leaked as new strings are interned. 121 | Interning uses `DashSet`, an implementation of a concurrent hash-set, allowing 122 | many strings to be interned concurrently without becoming a bottleneck. 123 | 124 | Given the nature of `IString` it is better to intern a string once and reuse 125 | it, rather than continually convert from `&str` to `IString`. 126 | 127 | ### IArray 128 | 129 | The `IArray` type is similar to a `Vec`. The primary difference is 130 | that the length and capacity are stored _inside_ the heap allocation, so that 131 | the `IArray` itself can be a single pointer. 132 | 133 | ### IObject 134 | 135 | The `IObject` type is similar to a `HashMap`. As with the 136 | `IArray`, the length and capacity are stored _inside_ the heap allocation. 137 | In addition, `IObject`s preserve the insertion order of their elements, in 138 | case that is important in the original JSON. 139 | 140 | Removing from an `IObject` will disrupt the insertion order. 141 | 142 | ## Technical details 143 | 144 | ### `IValue` 145 | 146 | The six JSON types are broken down into those with a small fixed set of values: 147 | 148 | - null 149 | - bool 150 | 151 | And those without: 152 | 153 | - number 154 | - string 155 | - array 156 | - object 157 | 158 | Conveniently, this means we only need to distinguish between four different 159 | heap allocated types (those without) and this can be done using only 2 bits. 160 | 161 | We make sure our heap allocations have an alignment of at least 4 (which 162 | is generally the case _anyway_) and this leaves us the two lower bits of 163 | a pointer to store a "tag" value. 164 | 165 | As an added bonus, the alignment of 4 means there are 3 constant pointer 166 | values (other than the null pointer) which can never be returned from 167 | `alloc`: 168 | 169 | - 0x1 170 | - 0x2 171 | - 0x3 172 | 173 | These three pointer values map neatly onto the fixed values `null`, `false` 174 | and `true` respectively. And with that, we've covered all the possible JSON 175 | types! 176 | 177 | All that's left is to find a way to store numbers, strings, arrays and 178 | objects behind a thin pointer. 179 | 180 | ### `INumber` 181 | 182 | It's not uncommon to store byte arrays in JSON. If we need to a heap 183 | allocation for every single byte in such an array it would be extremely 184 | inefficient. Also, some numbers are more common than others (0, 1, -1). 185 | 186 | As a result, we need a way to encode numbers more efficiently the smaller 187 | they are, and ideally encode all possible byte values without a heap 188 | allocation. But we only have a single pointer to work with, and we've 189 | already used the tag bits! 190 | 191 | The good news is that there just aren't that many byte values (256 to 192 | be exact) and even if we extend the range to signed bytes too, it's 193 | only 384. We can simply reserve a static array in our binary for these 194 | small integers. 195 | 196 | In practice we use a nice round 512-entry array storing values from 197 | `-128` to `383` which more than covers the byte value range. When we 198 | need to store one of these numbers we simply set our pointer to the 199 | appropriate entry in the array, and skip any allocating or freeing. 200 | 201 | ### `IString` 202 | 203 | As mentioned previously, strings are interned. As well as saving a ton 204 | of memory when keys are repeated many times in arrays of objects, this 205 | also makes it trivial to store a string with a single pointer, we just 206 | set the pointer to the location of the interned string. 207 | 208 | We also use a similar trick as for numbers to store the empty string 209 | more cheaply: we just declare a static variable to be the empty string, 210 | and use pointers to that. 211 | 212 | ### `IArray` 213 | 214 | This works just like a `Vec`, but we reserve extra space at the beginning 215 | of the allocation to store the length and capacity. 216 | 217 | We again use the static variable optimization so that the empty `Vec` does 218 | not require an allocation. 219 | 220 | ### `IObject` 221 | 222 | Same idea as the `IArray` and the same static variable optimization. 223 | 224 | Internally we actually store two arrays in the allocation: the first is 225 | a simple array of `IValue`s and the second is the hash-table itself. 226 | The hash table just stored indices into the first array. 227 | 228 | This simplifies the hash table implementation whilst also allowing us to 229 | preserve the insertion order and makes iteration very cheap (since we 230 | don't need to skip over empty entries). 231 | 232 | New values are always pushed onto the end of the array before their 233 | index is inserted into the hash table. Removed values are first swapped 234 | to the end of the array (see `Vec::swap_remove`) so that removals are 235 | still constant time. 236 | 237 | The hash values are not stored, since the keys (`IString`s) are interned 238 | and so the hash function can be a very fast operation that only looks 239 | at the pointer value. 240 | -------------------------------------------------------------------------------- /comparison.csv: -------------------------------------------------------------------------------- 1 | "Filename","JSON size (B)","serde-json peak memory usage (B)","ijson peak memory usage (B)","serde-json allocations","ijson allocations","serde-json clone memory usage (B)","ijson clone memory usage (B)","serde-json clone allocations","ijson clone allocations" 2 | "rnd0000.json",1639393,5145733,2493780,98558,36383,5093701,1549004,98547,12760 3 | "rnd0001.json",356951,1132031,579884,21519,8768,1112351,336932,21510,2488 4 | "rnd0002.json",1663507,5215719,2522408,99956,36854,5166759,1570976,99945,12946 5 | "rnd0003.json",1924953,5993709,2843916,115661,42067,5978253,1818068,115650,15040 6 | "rnd0004.json",437189,1370689,686744,26334,10586,1361281,412688,26325,3130 7 | "rnd0005.json",860267,2695837,1331780,51760,20106,2675133,812708,51750,6520 8 | "rnd0006.json",194219,614566,317452,11738,4916,606790,183060,11730,1184 9 | "rnd0007.json",2319329,7429998,3387812,139357,49717,7202958,2190900,139345,18199 10 | "rnd0008.json",1163211,3727927,1750188,69941,26478,3614935,1098740,69930,8944 11 | "rnd0009.json",1887110,5882992,2800404,113439,41405,5862736,1783124,113428,14744 12 | "rnd0010.json",1409804,4460984,2168588,84726,31753,4379544,1331388,84715,10915 13 | "rnd0011.json",463224,1448183,721008,27896,11154,1442007,437240,27886,3338 14 | "rnd0012.json",2390481,7642880,3473768,143664,51097,7424928,2258656,143652,18774 15 | "rnd0013.json",1567797,4930960,2404972,94206,34895,4869744,1480540,94195,12179 16 | "rnd0014.json",1216703,3887558,1818360,73164,27621,3781382,1149464,73153,9374 17 | "rnd0015.json",386735,1220183,620044,23291,9441,1204183,364788,23281,2724 18 | "rnd0016.json",2052777,6637056,3065384,123357,44573,6375872,1939136,123345,16066 19 | "rnd0017.json",1981775,6164627,2915192,119121,43242,6156563,1872536,119110,15501 20 | "rnd0018.json",1130070,3629629,1707420,67961,25771,3512413,1067588,67950,8680 21 | "rnd0019.json",644575,2053122,1027204,38780,15246,2004738,608520,38770,4789 22 | "rnd0020.json",1674947,5251912,2538632,100708,37132,5204456,1582784,100696,13046 23 | "rnd0021.json",1335173,4239843,2007688,80271,30076,4148899,1261296,80260,10321 24 | "rnd0022.json",765461,2412553,1207564,46028,17988,2379561,722540,46018,5756 25 | "rnd0023.json",569012,1829126,886148,34269,13522,1771078,537524,34259,4188 26 | "rnd0024.json",1455170,4597313,2250700,87489,32640,4521697,1374844,87478,11284 27 | "rnd0025.json",1728402,5408947,2602600,103855,38161,5368275,1632328,103844,13466 28 | "rnd0026.json",2188055,7038230,3231284,131440,47260,6794230,2066324,131428,17144 29 | "rnd0027.json",959532,2990497,1458692,57699,22237,2982433,906156,57689,7312 30 | "rnd0028.json",261978,850069,422488,15831,6481,818165,247464,15822,1730 31 | "rnd0029.json",1695818,5312249,2560952,101908,37464,5267353,1601664,101896,13206 32 | "rnd0030.json",1716571,5374438,2587656,103166,37906,5332326,1621480,103155,13374 33 | "rnd0031.json",1677331,5258239,2539200,100823,37116,5211039,1584640,100812,13062 34 | "rnd0032.json",2465830,7866342,3565136,148165,52562,7658022,2329464,148153,19374 35 | "rnd0033.json",122939,384900,206472,7427,3178,384324,115280,7420,609 36 | "rnd0034.json",1768626,5530991,2651216,106341,38935,5495663,1671464,106330,13797 37 | "rnd0035.json",1373938,4354065,2076520,82566,30871,4268017,1297404,82555,10627 38 | "rnd0036.json",143294,462287,245272,8660,3704,447855,134656,8652,774 39 | "rnd0037.json",1711247,5359644,2580044,102881,37775,5316924,1616996,102870,13336 40 | "rnd0038.json",2443690,7800103,3540092,146827,52199,7588999,2308428,146815,19195 41 | "rnd0039.json",2256637,7242567,3312952,135566,48528,7007399,2131232,135554,17694 42 | "rnd0040.json",1759967,5504190,2643660,105791,38839,5467678,1662780,105780,13724 43 | "rnd0041.json",1205921,3855196,1804160,72508,27373,3747580,1139104,72496,9286 44 | "rnd0042.json",557577,1794969,874988,33580,13322,1735481,526676,33570,4096 45 | "rnd0043.json",1849235,5770564,2752744,111175,40610,5745508,1747496,111164,14442 46 | "rnd0044.json",2302110,7379277,3366212,138342,49360,7150061,2174900,138330,18064 47 | "rnd0045.json",1537616,4842073,2353280,92426,34310,4777049,1452504,92415,11942 48 | "rnd0046.json",705234,2234229,1124832,42440,16667,2193653,666104,42430,5277 49 | "rnd0047.json",967519,3013730,1468048,58163,22377,3006626,913464,58153,7374 50 | "rnd0048.json",1532390,4825570,2350664,92079,34135,4759746,1447060,92068,11896 51 | "rnd0049.json",819783,2574774,1278464,49310,19189,2548854,774192,49300,6193 52 | "rnd0050.json",1686605,5284234,2549776,101334,37288,5238154,1592672,101323,13130 53 | "rnd0051.json",370714,1173576,599884,22361,9103,1155592,350156,22351,2600 54 | "rnd0052.json",350037,1111332,570188,21098,8599,1090724,330316,21089,2432 55 | "rnd0053.json",1803932,5634554,2697848,108415,39731,5603610,1704072,108404,14074 56 | "rnd0054.json",1747633,5468291,2626692,105073,38540,5430147,1651460,105061,13628 57 | "rnd0055.json",2048471,6624262,3060820,123099,44497,6362438,1935100,123087,16032 58 | "rnd0056.json",1987340,6181067,2921336,119456,43320,6173707,1877776,119445,15546 59 | "rnd0057.json",1908057,5944880,2824608,114681,41755,5927344,1802680,114670,14909 60 | "rnd0058.json",1585937,4986100,2428912,95335,35359,4927252,1498280,95324,12330 61 | "rnd0059.json",819362,2574831,1277748,49325,19180,2548943,774428,49315,6195 62 | "rnd0060.json",2169962,6986580,3211436,130420,46957,6740404,2050276,130408,17008 63 | "rnd0061.json",212746,669296,342608,12835,5354,663760,200296,12826,1330 64 | "rnd0062.json",490529,1529775,757632,29546,11774,1527119,463200,29536,3558 65 | "rnd0063.json",445660,1395980,698168,26842,10771,1387596,420696,26833,3198 66 | "rnd0064.json",421176,1323220,666368,25376,10235,1311668,397592,25366,3002 67 | "rnd0065.json",1604413,5041126,2450200,96446,35699,4984678,1515752,96435,12478 68 | "rnd0066.json",43649,139873,79800,2648,1368,137185,40888,2641,181 69 | "rnd0067.json",1059670,3418598,1618192,63685,24320,3292230,1000320,63674,8110 70 | "rnd0068.json",947466,2955229,1442288,56993,21926,2945629,895056,56983,7218 71 | "rnd0069.json",1470488,4642752,2251728,88403,32950,4569056,1389232,88392,11406 72 | "rnd0070.json",431511,1354185,679096,26001,10446,1343977,407472,25992,3086 73 | "rnd0071.json",2453877,7831602,3549904,147476,52323,7621842,2318616,147464,19282 74 | "rnd0072.json",1216541,3887195,1823336,73161,27640,3781083,1149432,73150,9373 75 | "rnd0073.json",1020190,3169642,1534396,61302,23465,3169194,962812,61291,7792 76 | "rnd0074.json",1997706,6211321,2933328,120053,43512,6205145,1887192,120042,15626 77 | "rnd0075.json",971323,3025996,1472860,58418,22455,3019436,917476,58408,7408 78 | "rnd0076.json",573529,1841543,901572,34510,13651,1784039,541308,34500,4220 79 | "rnd0077.json",382966,1209878,615248,23094,9366,1193558,361712,23085,2698 80 | "rnd0078.json",1768376,5528383,2651948,106271,38959,5492895,1670332,106260,13788 81 | "rnd0079.json",1836783,5733447,2738544,110426,40397,5706823,1735704,110415,14342 82 | "rnd0080.json",1631565,5120608,2484640,98033,36259,5067456,1540744,98022,12690 83 | "rnd0081.json",1626195,5106045,2480924,97753,36232,5052285,1536292,97741,12652 84 | "rnd0082.json",2021228,6283210,2961128,121524,43942,6280202,1910328,121513,15822 85 | "rnd0083.json",663291,2109435,1068280,39924,15747,2063451,626496,39914,4942 86 | "rnd0084.json",1240406,3957182,1847676,74559,28091,3853982,1171412,74548,9560 87 | "rnd0085.json",817638,2567836,1275616,49162,19125,2541500,771856,49152,6174 88 | "rnd0086.json",715825,2266679,1143296,43107,16902,2227415,676544,43096,5366 89 | "rnd0087.json",1523744,4801076,2353708,91599,34154,4734228,1439508,91588,11832 90 | "rnd0088.json",2251500,7226601,3306712,135234,48446,6990665,2126024,135222,17650 91 | "rnd0089.json",724077,2290652,1148584,43583,17057,2252444,684072,43573,5430 92 | "rnd0090.json",667952,2122559,1076420,40179,15835,2077119,630508,40169,4976 93 | "rnd0091.json",156286,501003,263776,9440,4018,488235,146928,9432,878 94 | "rnd0092.json",2298706,7368973,3363036,138130,49354,7139245,2171580,138118,18036 95 | "rnd0093.json",649828,2069313,1037868,39113,15408,2021569,613744,39103,4834 96 | "rnd0094.json",2255488,7239491,3308832,135509,48438,7004131,2130304,135496,17686 97 | "rnd0095.json",323726,1033210,532720,19522,7985,1009210,305528,19513,2222 98 | "rnd0096.json",2249710,7224321,3303460,135220,48397,6988385,2125796,135208,17648 99 | "rnd0097.json",1254658,4000862,1864412,75456,28357,3899646,1185540,75445,9679 100 | "rnd0098.json",453131,1418879,709092,27311,10985,1411455,428036,27301,3260 101 | "rnd0099.json",1707286,5346754,2577932,102606,37771,5303458,1612700,102595,13299 102 | -------------------------------------------------------------------------------- /comparison.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Diggsey/ijson/9b9663e3998d5ce88e95a4d8c0ecf34c711f6861/comparison.xlsx -------------------------------------------------------------------------------- /examples/comparison.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::System; 2 | use std::error::Error; 3 | use std::fs; 4 | 5 | use mockalloc::{AllocInfo, Mockalloc}; 6 | 7 | #[global_allocator] 8 | static ALLOCATOR: Mockalloc = Mockalloc(System); 9 | 10 | fn test_json_decode(data: &[u8]) -> (AllocInfo, AllocInfo) { 11 | // Measure peak 12 | let res1 = mockalloc::record_allocs(|| { 13 | let _value: serde_json::Value = serde_json::from_slice(data).unwrap(); 14 | }); 15 | res1.result().unwrap(); 16 | 17 | // Measure clone cost 18 | let value: serde_json::Value = serde_json::from_slice(data).unwrap(); 19 | let res2 = mockalloc::record_allocs(|| { 20 | let _value = value.clone(); 21 | }); 22 | res2.result().unwrap(); 23 | (res1, res2) 24 | } 25 | 26 | fn test_ijson_decode(data: &[u8]) -> (AllocInfo, AllocInfo) { 27 | // Measure peak 28 | let res1 = mockalloc::record_allocs(|| { 29 | let _value: ijson::IValue = serde_json::from_slice(data).unwrap(); 30 | }); 31 | res1.result().unwrap(); 32 | 33 | // Measure clone cost 34 | let value: ijson::IValue = serde_json::from_slice(data).unwrap(); 35 | let res2 = mockalloc::record_allocs(|| { 36 | let _value = value.clone(); 37 | }); 38 | res2.result().unwrap(); 39 | (res1, res2) 40 | } 41 | 42 | fn print_alloc_info( 43 | name: &str, 44 | size: usize, 45 | alloc_info1: (AllocInfo, AllocInfo), 46 | alloc_info2: (AllocInfo, AllocInfo), 47 | ) { 48 | println!( 49 | "{:?},{},{},{},{},{},{},{},{},{}", 50 | name, 51 | size, 52 | alloc_info1.0.peak_mem(), 53 | alloc_info2.0.peak_mem(), 54 | alloc_info1.0.num_allocs(), 55 | alloc_info2.0.num_allocs(), 56 | alloc_info1.1.peak_mem(), 57 | alloc_info2.1.peak_mem(), 58 | alloc_info1.1.num_allocs(), 59 | alloc_info2.1.num_allocs(), 60 | ); 61 | } 62 | 63 | fn main() -> Result<(), Box> { 64 | // The string cache is normally lazily initialized which would erroneously show up as a 65 | // memory leak, so explicitly initialize it here. 66 | ijson::string::init_cache(); 67 | println!( 68 | r#""Filename","JSON size (B)","serde-json peak memory usage (B)","ijson peak memory usage (B)","serde-json allocations","ijson allocations","serde-json clone memory usage (B)","ijson clone memory usage (B)","serde-json clone allocations","ijson clone allocations""# 69 | ); 70 | for test_file in fs::read_dir("test_data")? { 71 | let test_file = test_file?; 72 | if !test_file.file_type()?.is_file() { 73 | continue; 74 | } 75 | let path = test_file.path(); 76 | if path.extension() != Some("json".as_ref()) { 77 | continue; 78 | } 79 | let contents = fs::read(test_file.path())?; 80 | 81 | let json_info = test_json_decode(&contents); 82 | let ijson_info = test_ijson_decode(&contents); 83 | let name = test_file.file_name().to_string_lossy().to_string(); 84 | print_alloc_info(&name, contents.len(), json_info, ijson_info); 85 | } 86 | Ok(()) 87 | } 88 | -------------------------------------------------------------------------------- /examples/generate.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::error::Error; 3 | use std::fs::File; 4 | use std::process::Command; 5 | 6 | fn main() -> Result<(), Box> { 7 | std::env::set_current_dir("test_data")?; 8 | let mut deque = VecDeque::new(); 9 | for i in 0..100 { 10 | let mut cmd = Command::new("dummyjson.cmd"); 11 | deque.push_back( 12 | cmd.arg("template.hbs") 13 | .stdout(File::create(format!("rnd{:04}.json", i))?) 14 | .spawn()?, 15 | ); 16 | if deque.len() >= 20 { 17 | deque.pop_front().unwrap().wait()?; 18 | } 19 | } 20 | for mut child in deque { 21 | child.wait()?; 22 | } 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /graphs/graph00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Diggsey/ijson/9b9663e3998d5ce88e95a4d8c0ecf34c711f6861/graphs/graph00.png -------------------------------------------------------------------------------- /graphs/graph01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Diggsey/ijson/9b9663e3998d5ce88e95a4d8c0ecf34c711f6861/graphs/graph01.png -------------------------------------------------------------------------------- /graphs/graph02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Diggsey/ijson/9b9663e3998d5ce88e95a4d8c0ecf34c711f6861/graphs/graph02.png -------------------------------------------------------------------------------- /graphs/graph03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Diggsey/ijson/9b9663e3998d5ce88e95a4d8c0ecf34c711f6861/graphs/graph03.png -------------------------------------------------------------------------------- /src/array.rs: -------------------------------------------------------------------------------- 1 | //! Functionality relating to the JSON array type 2 | 3 | use std::alloc::{alloc, dealloc, realloc, Layout, LayoutError}; 4 | use std::borrow::{Borrow, BorrowMut}; 5 | use std::cmp::{self, Ordering}; 6 | use std::fmt::{self, Debug, Formatter}; 7 | use std::hash::Hash; 8 | use std::iter::FromIterator; 9 | use std::ops::{Deref, DerefMut, Index, IndexMut}; 10 | use std::slice::SliceIndex; 11 | 12 | use crate::thin::{ThinMut, ThinMutExt, ThinRef, ThinRefExt}; 13 | 14 | use super::value::{IValue, TypeTag}; 15 | 16 | #[repr(C)] 17 | #[repr(align(4))] 18 | struct Header { 19 | len: usize, 20 | cap: usize, 21 | } 22 | 23 | trait HeaderRef<'a>: ThinRefExt<'a, Header> { 24 | fn array_ptr(&self) -> *const IValue { 25 | // Safety: pointers to the end of structs are allowed 26 | unsafe { self.ptr().add(1).cast::() } 27 | } 28 | fn items_slice(&self) -> &'a [IValue] { 29 | // Safety: Header `len` must be accurate 30 | unsafe { std::slice::from_raw_parts(self.array_ptr(), self.len) } 31 | } 32 | } 33 | 34 | trait HeaderMut<'a>: ThinMutExt<'a, Header> { 35 | fn array_ptr_mut(mut self) -> *mut IValue { 36 | // Safety: pointers to the end of structs are allowed 37 | unsafe { self.ptr_mut().add(1).cast::() } 38 | } 39 | fn items_slice_mut(self) -> &'a mut [IValue] { 40 | // Safety: Header `len` must be accurate 41 | let len = self.len; 42 | unsafe { std::slice::from_raw_parts_mut(self.array_ptr_mut(), len) } 43 | } 44 | // Safety: Space must already be allocated for the item 45 | unsafe fn push(&mut self, item: IValue) { 46 | let index = self.len; 47 | self.reborrow().array_ptr_mut().add(index).write(item); 48 | self.len += 1; 49 | } 50 | fn pop(&mut self) -> Option { 51 | if self.len == 0 { 52 | None 53 | } else { 54 | self.len -= 1; 55 | let index = self.len; 56 | 57 | // Safety: We just checked that an item exists 58 | unsafe { Some(self.reborrow().array_ptr_mut().add(index).read()) } 59 | } 60 | } 61 | } 62 | 63 | impl<'a, T: ThinRefExt<'a, Header>> HeaderRef<'a> for T {} 64 | impl<'a, T: ThinMutExt<'a, Header>> HeaderMut<'a> for T {} 65 | 66 | /// Iterator over [`IValue`]s returned from [`IArray::into_iter`] 67 | pub struct IntoIter { 68 | reversed_array: IArray, 69 | } 70 | 71 | impl Iterator for IntoIter { 72 | type Item = IValue; 73 | 74 | fn next(&mut self) -> Option { 75 | self.reversed_array.pop() 76 | } 77 | } 78 | 79 | impl ExactSizeIterator for IntoIter { 80 | fn len(&self) -> usize { 81 | self.reversed_array.len() 82 | } 83 | } 84 | 85 | impl Debug for IntoIter { 86 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 87 | f.debug_struct("IntoIter") 88 | .field("reversed_array", &self.reversed_array) 89 | .finish() 90 | } 91 | } 92 | 93 | /// The `IArray` type is similar to a `Vec`. The primary difference is 94 | /// that the length and capacity are stored _inside_ the heap allocation, so that 95 | /// the `IArray` itself can be a single pointer. 96 | #[repr(transparent)] 97 | #[derive(Clone)] 98 | pub struct IArray(pub(crate) IValue); 99 | 100 | value_subtype_impls!(IArray, into_array, as_array, as_array_mut); 101 | 102 | static EMPTY_HEADER: Header = Header { len: 0, cap: 0 }; 103 | 104 | impl IArray { 105 | fn layout(cap: usize) -> Result { 106 | Ok(Layout::new::
() 107 | .extend(Layout::array::(cap)?)? 108 | .0 109 | .pad_to_align()) 110 | } 111 | 112 | fn alloc(cap: usize) -> *mut Header { 113 | unsafe { 114 | let ptr = alloc(Self::layout(cap).unwrap()).cast::
(); 115 | ptr.write(Header { len: 0, cap }); 116 | ptr 117 | } 118 | } 119 | 120 | fn realloc(ptr: *mut Header, new_cap: usize) -> *mut Header { 121 | unsafe { 122 | let old_layout = Self::layout((*ptr).cap).unwrap(); 123 | let new_layout = Self::layout(new_cap).unwrap(); 124 | let ptr = realloc(ptr.cast::(), old_layout, new_layout.size()).cast::
(); 125 | (*ptr).cap = new_cap; 126 | ptr 127 | } 128 | } 129 | 130 | fn dealloc(ptr: *mut Header) { 131 | unsafe { 132 | let layout = Self::layout((*ptr).cap).unwrap(); 133 | dealloc(ptr.cast(), layout); 134 | } 135 | } 136 | 137 | /// Constructs a new empty `IArray`. Does not allocate. 138 | #[must_use] 139 | pub fn new() -> Self { 140 | unsafe { IArray(IValue::new_ref(&EMPTY_HEADER, TypeTag::ArrayOrFalse)) } 141 | } 142 | 143 | /// Constructs a new `IArray` with the specified capacity. At least that many items 144 | /// can be added to the array without reallocating. 145 | #[must_use] 146 | pub fn with_capacity(cap: usize) -> Self { 147 | if cap == 0 { 148 | Self::new() 149 | } else { 150 | IArray(unsafe { IValue::new_ptr(Self::alloc(cap).cast(), TypeTag::ArrayOrFalse) }) 151 | } 152 | } 153 | 154 | fn header(&self) -> ThinRef
{ 155 | unsafe { ThinRef::new(self.0.ptr().cast()) } 156 | } 157 | 158 | // Safety: must not be static 159 | unsafe fn header_mut(&mut self) -> ThinMut
{ 160 | ThinMut::new(self.0.ptr().cast()) 161 | } 162 | 163 | fn is_static(&self) -> bool { 164 | self.capacity() == 0 165 | } 166 | /// Returns the capacity of the array. This is the maximum number of items the array 167 | /// can hold without reallocating. 168 | #[must_use] 169 | pub fn capacity(&self) -> usize { 170 | self.header().cap 171 | } 172 | 173 | /// Returns the number of items currently stored in the array. 174 | #[must_use] 175 | pub fn len(&self) -> usize { 176 | self.header().len 177 | } 178 | 179 | /// Returns `true` if the array is empty. 180 | #[must_use] 181 | pub fn is_empty(&self) -> bool { 182 | self.len() == 0 183 | } 184 | 185 | /// Borrows a slice of [`IValue`]s from the array 186 | #[must_use] 187 | pub fn as_slice(&self) -> &[IValue] { 188 | self.header().items_slice() 189 | } 190 | 191 | /// Borrows a mutable slice of [`IValue`]s from the array 192 | pub fn as_mut_slice(&mut self) -> &mut [IValue] { 193 | if self.is_static() { 194 | &mut [] 195 | } else { 196 | unsafe { self.header_mut().items_slice_mut() } 197 | } 198 | } 199 | fn resize_internal(&mut self, cap: usize) { 200 | if self.is_static() || cap == 0 { 201 | *self = Self::with_capacity(cap); 202 | } else { 203 | unsafe { 204 | let new_ptr = Self::realloc(self.0.ptr().cast(), cap); 205 | self.0.set_ptr(new_ptr.cast()); 206 | } 207 | } 208 | } 209 | 210 | /// Reserves space for at least this many additional items. 211 | pub fn reserve(&mut self, additional: usize) { 212 | let hd = self.header(); 213 | let current_capacity = hd.cap; 214 | let desired_capacity = hd.len.checked_add(additional).unwrap(); 215 | if current_capacity >= desired_capacity { 216 | return; 217 | } 218 | self.resize_internal(cmp::max(current_capacity * 2, desired_capacity.max(4))); 219 | } 220 | 221 | /// Truncates the array by removing items until it is no longer than the specified 222 | /// length. The capacity is unchanged. 223 | pub fn truncate(&mut self, len: usize) { 224 | if self.is_static() { 225 | return; 226 | } 227 | unsafe { 228 | let mut hd = self.header_mut(); 229 | while hd.len > len { 230 | hd.pop(); 231 | } 232 | } 233 | } 234 | 235 | /// Removes all items from the array. The capacity is unchanged. 236 | pub fn clear(&mut self) { 237 | self.truncate(0); 238 | } 239 | 240 | /// Inserts a new item into the array at the specified index. Any existing items 241 | /// on or after this index will be shifted down to accomodate this. For large 242 | /// arrays, insertions near the front will be slow as it will require shifting 243 | /// a large number of items. 244 | pub fn insert(&mut self, index: usize, item: impl Into) { 245 | self.reserve(1); 246 | 247 | unsafe { 248 | // Safety: cannot be static after calling `reserve` 249 | let mut hd = self.header_mut(); 250 | assert!(index <= hd.len); 251 | 252 | // Safety: We just reserved enough space for at least one extra item 253 | hd.push(item.into()); 254 | if index < hd.len { 255 | hd.items_slice_mut()[index..].rotate_right(1); 256 | } 257 | } 258 | } 259 | 260 | /// Removes and returns the item at the specified index from the array. Any 261 | /// items after this index will be shifted back up to close the gap. For large 262 | /// arrays, removals from near the front will be slow as it will require shifting 263 | /// a large number of items. 264 | /// 265 | /// If the order of the array is unimporant, consider using [`IArray::swap_remove`]. 266 | /// 267 | /// If the index is outside the array bounds, `None` is returned. 268 | pub fn remove(&mut self, index: usize) -> Option { 269 | if index < self.len() { 270 | // Safety: cannot be static if index <= len 271 | unsafe { 272 | let mut hd = self.header_mut(); 273 | hd.reborrow().items_slice_mut()[index..].rotate_left(1); 274 | hd.pop() 275 | } 276 | } else { 277 | None 278 | } 279 | } 280 | 281 | /// Removes and returns the item at the specified index from the array by 282 | /// first swapping it with the item currently at the end of the array, and 283 | /// then popping that last item. 284 | /// 285 | /// This can be more efficient than [`IArray::remove`] for large arrays, 286 | /// but will change the ordering of items within the array. 287 | /// 288 | /// If the index is outside the array bounds, `None` is returned. 289 | pub fn swap_remove(&mut self, index: usize) -> Option { 290 | if index < self.len() { 291 | // Safety: cannot be static if index <= len 292 | unsafe { 293 | let mut hd = self.header_mut(); 294 | let last_index = hd.len - 1; 295 | hd.reborrow().items_slice_mut().swap(index, last_index); 296 | hd.pop() 297 | } 298 | } else { 299 | None 300 | } 301 | } 302 | 303 | /// Pushes a new item onto the back of the array. 304 | pub fn push(&mut self, item: impl Into) { 305 | self.reserve(1); 306 | // Safety: We just reserved enough space for at least one extra item 307 | unsafe { 308 | self.header_mut().push(item.into()); 309 | } 310 | } 311 | 312 | /// Pops the last item from the array and returns it. If the array is 313 | /// empty, `None` is returned. 314 | pub fn pop(&mut self) -> Option { 315 | if self.is_static() { 316 | None 317 | } else { 318 | // Safety: not static 319 | unsafe { self.header_mut().pop() } 320 | } 321 | } 322 | 323 | /// Shrinks the memory allocation used by the array such that its 324 | /// capacity becomes equal to its length. 325 | pub fn shrink_to_fit(&mut self) { 326 | self.resize_internal(self.len()); 327 | } 328 | 329 | pub(crate) fn clone_impl(&self) -> IValue { 330 | let src = self.header().items_slice(); 331 | let l = src.len(); 332 | let mut res = Self::with_capacity(l); 333 | 334 | if l > 0 { 335 | unsafe { 336 | // Safety: we cannot be static if len > 0 337 | let mut hd = res.header_mut(); 338 | for v in src { 339 | // Safety: we reserved enough space at the start 340 | hd.push(v.clone()); 341 | } 342 | } 343 | } 344 | res.0 345 | } 346 | pub(crate) fn drop_impl(&mut self) { 347 | self.clear(); 348 | if !self.is_static() { 349 | unsafe { 350 | Self::dealloc(self.0.ptr().cast()); 351 | self.0.set_ref(&EMPTY_HEADER); 352 | } 353 | } 354 | } 355 | } 356 | 357 | impl IntoIterator for IArray { 358 | type Item = IValue; 359 | type IntoIter = IntoIter; 360 | 361 | fn into_iter(mut self) -> Self::IntoIter { 362 | self.reverse(); 363 | IntoIter { 364 | reversed_array: self, 365 | } 366 | } 367 | } 368 | 369 | impl Deref for IArray { 370 | type Target = [IValue]; 371 | 372 | fn deref(&self) -> &Self::Target { 373 | self.as_slice() 374 | } 375 | } 376 | 377 | impl DerefMut for IArray { 378 | fn deref_mut(&mut self) -> &mut Self::Target { 379 | self.as_mut_slice() 380 | } 381 | } 382 | 383 | impl Borrow<[IValue]> for IArray { 384 | fn borrow(&self) -> &[IValue] { 385 | self.as_slice() 386 | } 387 | } 388 | 389 | impl BorrowMut<[IValue]> for IArray { 390 | fn borrow_mut(&mut self) -> &mut [IValue] { 391 | self.as_mut_slice() 392 | } 393 | } 394 | 395 | impl Hash for IArray { 396 | fn hash(&self, state: &mut H) { 397 | self.as_slice().hash(state); 398 | } 399 | } 400 | 401 | impl> Extend for IArray { 402 | fn extend>(&mut self, iter: T) { 403 | let iter = iter.into_iter(); 404 | self.reserve(iter.size_hint().0); 405 | for v in iter { 406 | self.push(v); 407 | } 408 | } 409 | } 410 | 411 | impl> FromIterator for IArray { 412 | fn from_iter>(iter: T) -> Self { 413 | let mut res = IArray::new(); 414 | res.extend(iter); 415 | res 416 | } 417 | } 418 | 419 | impl AsRef<[IValue]> for IArray { 420 | fn as_ref(&self) -> &[IValue] { 421 | self.as_slice() 422 | } 423 | } 424 | 425 | impl PartialEq for IArray { 426 | fn eq(&self, other: &Self) -> bool { 427 | if self.0.raw_eq(&other.0) { 428 | true 429 | } else { 430 | self.as_slice() == other.as_slice() 431 | } 432 | } 433 | } 434 | 435 | impl Eq for IArray {} 436 | impl PartialOrd for IArray { 437 | fn partial_cmp(&self, other: &Self) -> Option { 438 | if self.0.raw_eq(&other.0) { 439 | Some(Ordering::Equal) 440 | } else { 441 | self.as_slice().partial_cmp(other.as_slice()) 442 | } 443 | } 444 | } 445 | 446 | impl> Index for IArray { 447 | type Output = I::Output; 448 | 449 | #[inline] 450 | fn index(&self, index: I) -> &Self::Output { 451 | Index::index(self.as_slice(), index) 452 | } 453 | } 454 | 455 | impl> IndexMut for IArray { 456 | #[inline] 457 | fn index_mut(&mut self, index: I) -> &mut Self::Output { 458 | IndexMut::index_mut(self.as_mut_slice(), index) 459 | } 460 | } 461 | 462 | impl Debug for IArray { 463 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 464 | Debug::fmt(self.as_slice(), f) 465 | } 466 | } 467 | 468 | impl> From> for IArray { 469 | fn from(other: Vec) -> Self { 470 | let mut res = IArray::with_capacity(other.len()); 471 | res.extend(other.into_iter().map(Into::into)); 472 | res 473 | } 474 | } 475 | 476 | impl + Clone> From<&[T]> for IArray { 477 | fn from(other: &[T]) -> Self { 478 | let mut res = IArray::with_capacity(other.len()); 479 | res.extend(other.iter().cloned().map(Into::into)); 480 | res 481 | } 482 | } 483 | 484 | impl<'a> IntoIterator for &'a IArray { 485 | type Item = &'a IValue; 486 | type IntoIter = std::slice::Iter<'a, IValue>; 487 | 488 | fn into_iter(self) -> Self::IntoIter { 489 | self.iter() 490 | } 491 | } 492 | 493 | impl<'a> IntoIterator for &'a mut IArray { 494 | type Item = &'a mut IValue; 495 | type IntoIter = std::slice::IterMut<'a, IValue>; 496 | 497 | fn into_iter(self) -> Self::IntoIter { 498 | self.iter_mut() 499 | } 500 | } 501 | 502 | impl Default for IArray { 503 | fn default() -> Self { 504 | Self::new() 505 | } 506 | } 507 | 508 | #[cfg(test)] 509 | mod tests { 510 | use super::*; 511 | 512 | #[mockalloc::test] 513 | fn can_create() { 514 | let x = IArray::new(); 515 | let y = IArray::with_capacity(10); 516 | 517 | assert_eq!(x, y); 518 | } 519 | 520 | #[mockalloc::test] 521 | fn can_collect() { 522 | let x = vec![IValue::NULL, IValue::TRUE, IValue::FALSE]; 523 | let y: IArray = x.iter().cloned().collect(); 524 | 525 | assert_eq!(x.as_slice(), y.as_slice()); 526 | } 527 | 528 | #[mockalloc::test] 529 | fn can_push_insert() { 530 | let mut x = IArray::new(); 531 | x.insert(0, IValue::NULL); 532 | x.push(IValue::TRUE); 533 | x.insert(1, IValue::FALSE); 534 | 535 | assert_eq!(x.as_slice(), &[IValue::NULL, IValue::FALSE, IValue::TRUE]); 536 | } 537 | 538 | #[mockalloc::test] 539 | fn can_nest() { 540 | let x: IArray = vec![IValue::NULL, IValue::TRUE, IValue::FALSE].into(); 541 | let y: IArray = vec![ 542 | IValue::NULL, 543 | x.clone().into(), 544 | IValue::FALSE, 545 | x.clone().into(), 546 | ] 547 | .into(); 548 | 549 | assert_eq!(&y[1], x.as_ref()); 550 | } 551 | 552 | #[mockalloc::test] 553 | fn can_pop_remove() { 554 | let mut x: IArray = vec![IValue::NULL, IValue::TRUE, IValue::FALSE].into(); 555 | assert_eq!(x.remove(1), Some(IValue::TRUE)); 556 | assert_eq!(x.pop(), Some(IValue::FALSE)); 557 | 558 | assert_eq!(x.as_slice(), &[IValue::NULL]); 559 | } 560 | 561 | #[mockalloc::test] 562 | fn can_swap_remove() { 563 | let mut x: IArray = vec![IValue::NULL, IValue::TRUE, IValue::FALSE].into(); 564 | assert_eq!(x.swap_remove(0), Some(IValue::NULL)); 565 | 566 | assert_eq!(x.as_slice(), &[IValue::FALSE, IValue::TRUE]); 567 | } 568 | 569 | #[mockalloc::test] 570 | fn can_index() { 571 | let mut x: IArray = vec![IValue::NULL, IValue::TRUE, IValue::FALSE].into(); 572 | assert_eq!(x[1], IValue::TRUE); 573 | x[1] = IValue::FALSE; 574 | assert_eq!(x[1], IValue::FALSE); 575 | } 576 | 577 | #[mockalloc::test] 578 | fn can_truncate_and_shrink() { 579 | let mut x: IArray = 580 | vec![IValue::NULL, IValue::TRUE, IArray::with_capacity(10).into()].into(); 581 | x.truncate(2); 582 | assert_eq!(x.len(), 2); 583 | assert_eq!(x.capacity(), 3); 584 | x.shrink_to_fit(); 585 | assert_eq!(x.len(), 2); 586 | assert_eq!(x.capacity(), 2); 587 | } 588 | 589 | // Too slow for miri 590 | #[cfg(not(miri))] 591 | #[mockalloc::test] 592 | fn stress_test() { 593 | use rand::prelude::*; 594 | 595 | for i in 0..10 { 596 | // We want our test to be random but for errors to be reproducible 597 | let mut rng = StdRng::seed_from_u64(i); 598 | let mut arr = IArray::new(); 599 | 600 | for j in 0..1000 { 601 | let index = rng.gen_range(0..arr.len() + 1); 602 | if rng.gen() { 603 | arr.insert(index, j); 604 | } else { 605 | arr.remove(index); 606 | } 607 | } 608 | } 609 | } 610 | } 611 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryFrom; 2 | use std::fmt::{self, Formatter}; 3 | use std::slice; 4 | 5 | use serde::de::{ 6 | DeserializeSeed, EnumAccess, Error as SError, Expected, IntoDeserializer, MapAccess, SeqAccess, 7 | Unexpected, VariantAccess, Visitor, 8 | }; 9 | use serde::{forward_to_deserialize_any, Deserialize, Deserializer}; 10 | use serde_json::error::Error; 11 | 12 | use super::array::IArray; 13 | use super::number::INumber; 14 | use super::object::IObject; 15 | use super::string::IString; 16 | use super::value::{DestructuredRef, IValue}; 17 | 18 | impl<'de> Deserialize<'de> for IValue { 19 | fn deserialize(deserializer: D) -> Result 20 | where 21 | D: Deserializer<'de>, 22 | { 23 | deserializer.deserialize_any(ValueVisitor) 24 | } 25 | } 26 | 27 | impl<'de> Deserialize<'de> for INumber { 28 | fn deserialize(deserializer: D) -> Result 29 | where 30 | D: Deserializer<'de>, 31 | { 32 | deserializer.deserialize_any(NumberVisitor) 33 | } 34 | } 35 | 36 | impl<'de> Deserialize<'de> for IString { 37 | fn deserialize(deserializer: D) -> Result 38 | where 39 | D: Deserializer<'de>, 40 | { 41 | deserializer.deserialize_str(StringVisitor) 42 | } 43 | } 44 | 45 | impl<'de> Deserialize<'de> for IArray { 46 | fn deserialize(deserializer: D) -> Result 47 | where 48 | D: Deserializer<'de>, 49 | { 50 | deserializer.deserialize_seq(ArrayVisitor) 51 | } 52 | } 53 | 54 | impl<'de> Deserialize<'de> for IObject { 55 | fn deserialize(deserializer: D) -> Result 56 | where 57 | D: Deserializer<'de>, 58 | { 59 | deserializer.deserialize_map(ObjectVisitor) 60 | } 61 | } 62 | 63 | struct ValueVisitor; 64 | 65 | impl<'de> Visitor<'de> for ValueVisitor { 66 | type Value = IValue; 67 | 68 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 69 | formatter.write_str("any valid JSON value") 70 | } 71 | 72 | #[inline] 73 | fn visit_bool(self, value: bool) -> Result { 74 | Ok(value.into()) 75 | } 76 | 77 | #[inline] 78 | fn visit_i64(self, value: i64) -> Result { 79 | Ok(value.into()) 80 | } 81 | 82 | #[inline] 83 | fn visit_u64(self, value: u64) -> Result { 84 | Ok(value.into()) 85 | } 86 | 87 | #[inline] 88 | fn visit_f64(self, value: f64) -> Result { 89 | Ok(value.into()) 90 | } 91 | 92 | #[inline] 93 | fn visit_str(self, value: &str) -> Result { 94 | Ok(value.into()) 95 | } 96 | 97 | #[inline] 98 | fn visit_string(self, value: String) -> Result { 99 | Ok(value.into()) 100 | } 101 | 102 | #[inline] 103 | fn visit_none(self) -> Result { 104 | Ok(IValue::NULL) 105 | } 106 | 107 | #[inline] 108 | fn visit_some(self, deserializer: D) -> Result 109 | where 110 | D: Deserializer<'de>, 111 | { 112 | Deserialize::deserialize(deserializer) 113 | } 114 | 115 | #[inline] 116 | fn visit_unit(self) -> Result { 117 | Ok(IValue::NULL) 118 | } 119 | 120 | #[inline] 121 | fn visit_seq(self, visitor: V) -> Result 122 | where 123 | V: SeqAccess<'de>, 124 | { 125 | ArrayVisitor.visit_seq(visitor).map(Into::into) 126 | } 127 | 128 | fn visit_map(self, visitor: V) -> Result 129 | where 130 | V: MapAccess<'de>, 131 | { 132 | ObjectVisitor.visit_map(visitor).map(Into::into) 133 | } 134 | } 135 | 136 | struct NumberVisitor; 137 | 138 | impl Visitor<'_> for NumberVisitor { 139 | type Value = INumber; 140 | 141 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 142 | formatter.write_str("JSON number") 143 | } 144 | 145 | #[inline] 146 | fn visit_i64(self, value: i64) -> Result { 147 | Ok(value.into()) 148 | } 149 | 150 | #[inline] 151 | fn visit_u64(self, value: u64) -> Result { 152 | Ok(value.into()) 153 | } 154 | 155 | #[inline] 156 | fn visit_f64(self, value: f64) -> Result { 157 | INumber::try_from(value).map_err(|_| E::invalid_value(Unexpected::Float(value), &self)) 158 | } 159 | } 160 | 161 | struct StringVisitor; 162 | 163 | impl Visitor<'_> for StringVisitor { 164 | type Value = IString; 165 | 166 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 167 | formatter.write_str("JSON string") 168 | } 169 | 170 | #[inline] 171 | fn visit_str(self, value: &str) -> Result { 172 | Ok(value.into()) 173 | } 174 | 175 | #[inline] 176 | fn visit_string(self, value: String) -> Result { 177 | Ok(value.into()) 178 | } 179 | 180 | #[inline] 181 | fn visit_bytes(self, value: &[u8]) -> Result { 182 | match std::str::from_utf8(value) { 183 | Ok(s) => Ok(s.into()), 184 | Err(_) => Err(SError::invalid_value(Unexpected::Bytes(value), &self)), 185 | } 186 | } 187 | 188 | #[inline] 189 | fn visit_byte_buf(self, value: Vec) -> Result { 190 | match String::from_utf8(value) { 191 | Ok(s) => Ok(s.into()), 192 | Err(e) => Err(SError::invalid_value( 193 | Unexpected::Bytes(&e.into_bytes()), 194 | &self, 195 | )), 196 | } 197 | } 198 | } 199 | 200 | struct ArrayVisitor; 201 | 202 | impl<'de> Visitor<'de> for ArrayVisitor { 203 | type Value = IArray; 204 | 205 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 206 | formatter.write_str("JSON array") 207 | } 208 | 209 | #[inline] 210 | fn visit_seq(self, mut visitor: V) -> Result 211 | where 212 | V: SeqAccess<'de>, 213 | { 214 | let mut arr = IArray::with_capacity(visitor.size_hint().unwrap_or(0)); 215 | while let Some(v) = visitor.next_element::()? { 216 | arr.push(v); 217 | } 218 | Ok(arr) 219 | } 220 | } 221 | 222 | struct ObjectVisitor; 223 | 224 | impl<'de> Visitor<'de> for ObjectVisitor { 225 | type Value = IObject; 226 | 227 | fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { 228 | formatter.write_str("JSON object") 229 | } 230 | 231 | fn visit_map(self, mut visitor: V) -> Result 232 | where 233 | V: MapAccess<'de>, 234 | { 235 | let mut obj = IObject::with_capacity(visitor.size_hint().unwrap_or(0)); 236 | while let Some((k, v)) = visitor.next_entry::()? { 237 | obj.insert(k, v); 238 | } 239 | Ok(obj) 240 | } 241 | } 242 | 243 | macro_rules! deserialize_number { 244 | ($method:ident) => { 245 | fn $method(self, visitor: V) -> Result 246 | where 247 | V: Visitor<'de>, 248 | { 249 | if let Some(v) = self.as_number() { 250 | v.deserialize_any(visitor) 251 | } else { 252 | Err(self.invalid_type(&visitor)) 253 | } 254 | } 255 | }; 256 | } 257 | 258 | impl<'de> Deserializer<'de> for &'de IValue { 259 | type Error = Error; 260 | 261 | #[inline] 262 | fn deserialize_any(self, visitor: V) -> Result 263 | where 264 | V: Visitor<'de>, 265 | { 266 | match self.destructure_ref() { 267 | DestructuredRef::Null => visitor.visit_unit(), 268 | DestructuredRef::Bool(v) => visitor.visit_bool(v), 269 | DestructuredRef::Number(v) => v.deserialize_any(visitor), 270 | DestructuredRef::String(v) => v.deserialize_any(visitor), 271 | DestructuredRef::Array(v) => v.deserialize_any(visitor), 272 | DestructuredRef::Object(v) => v.deserialize_any(visitor), 273 | } 274 | } 275 | 276 | deserialize_number!(deserialize_i8); 277 | deserialize_number!(deserialize_i16); 278 | deserialize_number!(deserialize_i32); 279 | deserialize_number!(deserialize_i64); 280 | deserialize_number!(deserialize_u8); 281 | deserialize_number!(deserialize_u16); 282 | deserialize_number!(deserialize_u32); 283 | deserialize_number!(deserialize_u64); 284 | deserialize_number!(deserialize_f32); 285 | deserialize_number!(deserialize_f64); 286 | 287 | #[inline] 288 | fn deserialize_option(self, visitor: V) -> Result 289 | where 290 | V: Visitor<'de>, 291 | { 292 | if self.is_null() { 293 | visitor.visit_none() 294 | } else { 295 | visitor.visit_some(self) 296 | } 297 | } 298 | 299 | #[inline] 300 | fn deserialize_enum( 301 | self, 302 | name: &'static str, 303 | variants: &'static [&'static str], 304 | visitor: V, 305 | ) -> Result 306 | where 307 | V: Visitor<'de>, 308 | { 309 | match self.destructure_ref() { 310 | DestructuredRef::String(v) => v.deserialize_enum(name, variants, visitor), 311 | DestructuredRef::Object(v) => v.deserialize_enum(name, variants, visitor), 312 | other => Err(SError::invalid_type(other.unexpected(), &"string or map")), 313 | } 314 | } 315 | 316 | #[inline] 317 | fn deserialize_newtype_struct( 318 | self, 319 | _name: &'static str, 320 | visitor: V, 321 | ) -> Result 322 | where 323 | V: Visitor<'de>, 324 | { 325 | visitor.visit_newtype_struct(self) 326 | } 327 | 328 | fn deserialize_bool(self, visitor: V) -> Result 329 | where 330 | V: Visitor<'de>, 331 | { 332 | if let Some(v) = self.to_bool() { 333 | visitor.visit_bool(v) 334 | } else { 335 | Err(self.invalid_type(&visitor)) 336 | } 337 | } 338 | 339 | fn deserialize_char(self, visitor: V) -> Result 340 | where 341 | V: Visitor<'de>, 342 | { 343 | self.deserialize_str(visitor) 344 | } 345 | 346 | fn deserialize_str(self, visitor: V) -> Result 347 | where 348 | V: Visitor<'de>, 349 | { 350 | if let Some(v) = self.as_string() { 351 | v.deserialize_str(visitor) 352 | } else { 353 | Err(self.invalid_type(&visitor)) 354 | } 355 | } 356 | 357 | fn deserialize_string(self, visitor: V) -> Result 358 | where 359 | V: Visitor<'de>, 360 | { 361 | self.deserialize_str(visitor) 362 | } 363 | 364 | fn deserialize_bytes(self, visitor: V) -> Result 365 | where 366 | V: Visitor<'de>, 367 | { 368 | match self.destructure_ref() { 369 | DestructuredRef::String(v) => v.deserialize_bytes(visitor), 370 | DestructuredRef::Array(v) => v.deserialize_bytes(visitor), 371 | other => Err(other.invalid_type(&visitor)), 372 | } 373 | } 374 | 375 | fn deserialize_byte_buf(self, visitor: V) -> Result 376 | where 377 | V: Visitor<'de>, 378 | { 379 | self.deserialize_bytes(visitor) 380 | } 381 | 382 | fn deserialize_unit(self, visitor: V) -> Result 383 | where 384 | V: Visitor<'de>, 385 | { 386 | if self.is_null() { 387 | visitor.visit_unit() 388 | } else { 389 | Err(self.invalid_type(&visitor)) 390 | } 391 | } 392 | 393 | fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result 394 | where 395 | V: Visitor<'de>, 396 | { 397 | self.deserialize_unit(visitor) 398 | } 399 | 400 | fn deserialize_seq(self, visitor: V) -> Result 401 | where 402 | V: Visitor<'de>, 403 | { 404 | if let Some(v) = self.as_array() { 405 | v.deserialize_seq(visitor) 406 | } else { 407 | Err(self.invalid_type(&visitor)) 408 | } 409 | } 410 | 411 | fn deserialize_tuple(self, _len: usize, visitor: V) -> Result 412 | where 413 | V: Visitor<'de>, 414 | { 415 | self.deserialize_seq(visitor) 416 | } 417 | 418 | fn deserialize_tuple_struct( 419 | self, 420 | _name: &'static str, 421 | _len: usize, 422 | visitor: V, 423 | ) -> Result 424 | where 425 | V: Visitor<'de>, 426 | { 427 | self.deserialize_seq(visitor) 428 | } 429 | 430 | fn deserialize_map(self, visitor: V) -> Result 431 | where 432 | V: Visitor<'de>, 433 | { 434 | if let Some(v) = self.as_object() { 435 | v.deserialize_map(visitor) 436 | } else { 437 | Err(self.invalid_type(&visitor)) 438 | } 439 | } 440 | 441 | fn deserialize_struct( 442 | self, 443 | name: &'static str, 444 | fields: &'static [&'static str], 445 | visitor: V, 446 | ) -> Result 447 | where 448 | V: Visitor<'de>, 449 | { 450 | match self.destructure_ref() { 451 | DestructuredRef::Array(v) => v.deserialize_struct(name, fields, visitor), 452 | DestructuredRef::Object(v) => v.deserialize_struct(name, fields, visitor), 453 | other => Err(other.invalid_type(&visitor)), 454 | } 455 | } 456 | 457 | fn deserialize_identifier(self, visitor: V) -> Result 458 | where 459 | V: Visitor<'de>, 460 | { 461 | self.deserialize_str(visitor) 462 | } 463 | 464 | fn deserialize_ignored_any(self, visitor: V) -> Result 465 | where 466 | V: Visitor<'de>, 467 | { 468 | visitor.visit_unit() 469 | } 470 | } 471 | 472 | impl<'de> Deserializer<'de> for &'de INumber { 473 | type Error = Error; 474 | 475 | #[inline] 476 | fn deserialize_any(self, visitor: V) -> Result 477 | where 478 | V: Visitor<'de>, 479 | { 480 | if self.has_decimal_point() { 481 | visitor.visit_f64(self.to_f64().unwrap()) 482 | } else if let Some(v) = self.to_i64() { 483 | visitor.visit_i64(v) 484 | } else { 485 | visitor.visit_u64(self.to_u64().unwrap()) 486 | } 487 | } 488 | 489 | #[inline] 490 | fn deserialize_newtype_struct( 491 | self, 492 | _name: &'static str, 493 | visitor: V, 494 | ) -> Result 495 | where 496 | V: Visitor<'de>, 497 | { 498 | visitor.visit_newtype_struct(self) 499 | } 500 | 501 | forward_to_deserialize_any! { 502 | bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string 503 | bytes byte_buf option unit unit_struct seq tuple 504 | tuple_struct map struct enum identifier ignored_any 505 | } 506 | } 507 | 508 | impl<'de> Deserializer<'de> for &'de IString { 509 | type Error = Error; 510 | 511 | #[inline] 512 | fn deserialize_any(self, visitor: V) -> Result 513 | where 514 | V: Visitor<'de>, 515 | { 516 | visitor.visit_borrowed_str(self.as_str()) 517 | } 518 | 519 | fn deserialize_enum( 520 | self, 521 | _name: &str, 522 | _variants: &'static [&'static str], 523 | visitor: V, 524 | ) -> Result 525 | where 526 | V: Visitor<'de>, 527 | { 528 | visitor.visit_enum(EnumDeserializer { 529 | variant: self, 530 | value: None, 531 | }) 532 | } 533 | 534 | #[inline] 535 | fn deserialize_newtype_struct( 536 | self, 537 | _name: &'static str, 538 | visitor: V, 539 | ) -> Result 540 | where 541 | V: Visitor<'de>, 542 | { 543 | visitor.visit_newtype_struct(self) 544 | } 545 | 546 | forward_to_deserialize_any! { 547 | bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string 548 | bytes byte_buf option unit unit_struct seq tuple 549 | tuple_struct map struct identifier ignored_any 550 | } 551 | } 552 | 553 | impl<'de> Deserializer<'de> for &'de IArray { 554 | type Error = Error; 555 | 556 | #[inline] 557 | fn deserialize_any(self, visitor: V) -> Result 558 | where 559 | V: Visitor<'de>, 560 | { 561 | let len = self.len(); 562 | let mut deserializer = ArrayAccess::new(self); 563 | let seq = visitor.visit_seq(&mut deserializer)?; 564 | let remaining = deserializer.iter.len(); 565 | if remaining == 0 { 566 | Ok(seq) 567 | } else { 568 | Err(SError::invalid_length(len, &"fewer elements in array")) 569 | } 570 | } 571 | 572 | #[inline] 573 | fn deserialize_newtype_struct( 574 | self, 575 | _name: &'static str, 576 | visitor: V, 577 | ) -> Result 578 | where 579 | V: Visitor<'de>, 580 | { 581 | visitor.visit_newtype_struct(self) 582 | } 583 | 584 | forward_to_deserialize_any! { 585 | bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string 586 | bytes byte_buf option unit unit_struct seq tuple 587 | tuple_struct map struct enum identifier ignored_any 588 | } 589 | } 590 | 591 | impl<'de> Deserializer<'de> for &'de IObject { 592 | type Error = Error; 593 | 594 | #[inline] 595 | fn deserialize_any(self, visitor: V) -> Result 596 | where 597 | V: Visitor<'de>, 598 | { 599 | let len = self.len(); 600 | let mut deserializer = ObjectAccess::new(self); 601 | let seq = visitor.visit_map(&mut deserializer)?; 602 | let remaining = deserializer.iter.len(); 603 | if remaining == 0 { 604 | Ok(seq) 605 | } else { 606 | Err(SError::invalid_length(len, &"fewer elements in object")) 607 | } 608 | } 609 | 610 | #[inline] 611 | fn deserialize_enum( 612 | self, 613 | _name: &'static str, 614 | _variants: &'static [&'static str], 615 | visitor: V, 616 | ) -> Result 617 | where 618 | V: Visitor<'de>, 619 | { 620 | let mut iter = self.iter(); 621 | let (variant, value) = iter 622 | .next() 623 | .ok_or_else(|| SError::invalid_value(Unexpected::Map, &"object with a single key"))?; 624 | // enums are encoded in json as maps with a single key:value pair 625 | if iter.next().is_some() { 626 | return Err(SError::invalid_value( 627 | Unexpected::Map, 628 | &"object with a single key", 629 | )); 630 | } 631 | visitor.visit_enum(EnumDeserializer { 632 | variant, 633 | value: Some(value), 634 | }) 635 | } 636 | 637 | #[inline] 638 | fn deserialize_newtype_struct( 639 | self, 640 | _name: &'static str, 641 | visitor: V, 642 | ) -> Result 643 | where 644 | V: Visitor<'de>, 645 | { 646 | visitor.visit_newtype_struct(self) 647 | } 648 | 649 | forward_to_deserialize_any! { 650 | bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string 651 | bytes byte_buf option unit unit_struct seq tuple 652 | tuple_struct map struct identifier ignored_any 653 | } 654 | } 655 | 656 | trait MaybeUnexpected<'de>: Sized { 657 | fn invalid_type(self, exp: &dyn Expected) -> E 658 | where 659 | E: SError, 660 | { 661 | SError::invalid_type(self.unexpected(), exp) 662 | } 663 | 664 | fn unexpected(self) -> Unexpected<'de>; 665 | } 666 | 667 | impl<'de> MaybeUnexpected<'de> for &'de IValue { 668 | fn unexpected(self) -> Unexpected<'de> { 669 | self.destructure_ref().unexpected() 670 | } 671 | } 672 | 673 | impl<'de> MaybeUnexpected<'de> for DestructuredRef<'de> { 674 | fn unexpected(self) -> Unexpected<'de> { 675 | match self { 676 | Self::Null => Unexpected::Unit, 677 | Self::Bool(b) => Unexpected::Bool(b), 678 | Self::Number(v) => v.unexpected(), 679 | Self::String(v) => v.unexpected(), 680 | Self::Array(v) => v.unexpected(), 681 | Self::Object(v) => v.unexpected(), 682 | } 683 | } 684 | } 685 | 686 | impl<'de> MaybeUnexpected<'de> for &'de INumber { 687 | fn unexpected(self) -> Unexpected<'de> { 688 | if self.has_decimal_point() { 689 | Unexpected::Float(self.to_f64().unwrap()) 690 | } else if let Some(v) = self.to_i64() { 691 | Unexpected::Signed(v) 692 | } else { 693 | Unexpected::Unsigned(self.to_u64().unwrap()) 694 | } 695 | } 696 | } 697 | 698 | impl<'de> MaybeUnexpected<'de> for &'de IString { 699 | fn unexpected(self) -> Unexpected<'de> { 700 | Unexpected::Str(self.as_str()) 701 | } 702 | } 703 | 704 | impl<'de> MaybeUnexpected<'de> for &'de IArray { 705 | fn unexpected(self) -> Unexpected<'de> { 706 | Unexpected::Seq 707 | } 708 | } 709 | 710 | impl<'de> MaybeUnexpected<'de> for &'de IObject { 711 | fn unexpected(self) -> Unexpected<'de> { 712 | Unexpected::Map 713 | } 714 | } 715 | 716 | struct EnumDeserializer<'de> { 717 | variant: &'de IString, 718 | value: Option<&'de IValue>, 719 | } 720 | 721 | impl<'de> EnumAccess<'de> for EnumDeserializer<'de> { 722 | type Error = Error; 723 | type Variant = VariantDeserializer<'de>; 724 | 725 | fn variant_seed(self, seed: V) -> Result<(V::Value, Self::Variant), Error> 726 | where 727 | V: DeserializeSeed<'de>, 728 | { 729 | let variant = self.variant.into_deserializer(); 730 | let visitor = VariantDeserializer { value: self.value }; 731 | seed.deserialize(variant).map(|v| (v, visitor)) 732 | } 733 | } 734 | 735 | impl<'de> IntoDeserializer<'de, Error> for &'de IString { 736 | type Deserializer = Self; 737 | 738 | fn into_deserializer(self) -> Self::Deserializer { 739 | self 740 | } 741 | } 742 | 743 | struct VariantDeserializer<'de> { 744 | value: Option<&'de IValue>, 745 | } 746 | 747 | impl<'de> VariantAccess<'de> for VariantDeserializer<'de> { 748 | type Error = Error; 749 | 750 | fn unit_variant(self) -> Result<(), Error> { 751 | if let Some(value) = self.value { 752 | Deserialize::deserialize(value) 753 | } else { 754 | Ok(()) 755 | } 756 | } 757 | 758 | fn newtype_variant_seed(self, seed: T) -> Result 759 | where 760 | T: DeserializeSeed<'de>, 761 | { 762 | if let Some(value) = self.value { 763 | seed.deserialize(value) 764 | } else { 765 | Err(SError::invalid_type( 766 | Unexpected::UnitVariant, 767 | &"newtype variant", 768 | )) 769 | } 770 | } 771 | 772 | fn tuple_variant(self, _len: usize, visitor: V) -> Result 773 | where 774 | V: Visitor<'de>, 775 | { 776 | match self.value.map(IValue::destructure_ref) { 777 | Some(DestructuredRef::Array(v)) => v.deserialize_any(visitor), 778 | Some(other) => Err(SError::invalid_type(other.unexpected(), &"tuple variant")), 779 | None => Err(SError::invalid_type( 780 | Unexpected::UnitVariant, 781 | &"tuple variant", 782 | )), 783 | } 784 | } 785 | 786 | fn struct_variant( 787 | self, 788 | _fields: &'static [&'static str], 789 | visitor: V, 790 | ) -> Result 791 | where 792 | V: Visitor<'de>, 793 | { 794 | match self.value.map(IValue::destructure_ref) { 795 | Some(DestructuredRef::Object(v)) => v.deserialize_any(visitor), 796 | Some(other) => Err(SError::invalid_type(other.unexpected(), &"struct variant")), 797 | None => Err(SError::invalid_type( 798 | Unexpected::UnitVariant, 799 | &"struct variant", 800 | )), 801 | } 802 | } 803 | } 804 | 805 | struct ArrayAccess<'de> { 806 | iter: slice::Iter<'de, IValue>, 807 | } 808 | 809 | impl<'de> ArrayAccess<'de> { 810 | fn new(slice: &'de [IValue]) -> Self { 811 | ArrayAccess { iter: slice.iter() } 812 | } 813 | } 814 | 815 | impl<'de> SeqAccess<'de> for ArrayAccess<'de> { 816 | type Error = Error; 817 | 818 | fn next_element_seed(&mut self, seed: T) -> Result, Error> 819 | where 820 | T: DeserializeSeed<'de>, 821 | { 822 | match self.iter.next() { 823 | Some(value) => seed.deserialize(value).map(Some), 824 | None => Ok(None), 825 | } 826 | } 827 | 828 | fn size_hint(&self) -> Option { 829 | match self.iter.size_hint() { 830 | (lower, Some(upper)) if lower == upper => Some(upper), 831 | _ => None, 832 | } 833 | } 834 | } 835 | 836 | struct ObjectAccess<'de> { 837 | iter: <&'de IObject as IntoIterator>::IntoIter, 838 | value: Option<&'de IValue>, 839 | } 840 | 841 | impl<'de> ObjectAccess<'de> { 842 | fn new(obj: &'de IObject) -> Self { 843 | ObjectAccess { 844 | iter: obj.into_iter(), 845 | value: None, 846 | } 847 | } 848 | } 849 | 850 | impl<'de> MapAccess<'de> for ObjectAccess<'de> { 851 | type Error = Error; 852 | 853 | fn next_key_seed(&mut self, seed: T) -> Result, Error> 854 | where 855 | T: DeserializeSeed<'de>, 856 | { 857 | if let Some((key, value)) = self.iter.next() { 858 | self.value = Some(value); 859 | seed.deserialize(key).map(Some) 860 | } else { 861 | Ok(None) 862 | } 863 | } 864 | 865 | fn next_value_seed(&mut self, seed: T) -> Result 866 | where 867 | T: DeserializeSeed<'de>, 868 | { 869 | if let Some(value) = self.value.take() { 870 | seed.deserialize(value) 871 | } else { 872 | Err(SError::custom("value is missing")) 873 | } 874 | } 875 | 876 | fn size_hint(&self) -> Option { 877 | match self.iter.size_hint() { 878 | (lower, Some(upper)) if lower == upper => Some(upper), 879 | _ => None, 880 | } 881 | } 882 | } 883 | 884 | /// Converts an [`IValue`] to an arbitrary type using that type's [`serde::Deserialize`] 885 | /// implementation. 886 | /// 887 | /// # Errors 888 | /// 889 | /// Will return `Error` if `value` fails to deserialize. 890 | pub fn from_value<'de, T>(value: &'de IValue) -> Result 891 | where 892 | T: Deserialize<'de>, 893 | { 894 | T::deserialize(value) 895 | } 896 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate offers a replacement for `serde-json`'s `Value` type, which is 2 | //! significantly more memory efficient. 3 | //! 4 | //! As a ballpark figure, it will typically use half as much memory as 5 | //! `serde-json` when deserializing a value and the memory footprint of cloning 6 | //! a value is more than 7x smaller. 7 | //! 8 | //! The primary type exposed by this crate is the [`IValue`] type. It is guaranteed 9 | //! to be pointer-sized and has a niche (so `Option` is also guaranteed 10 | //! to be pointer-sized). 11 | //! 12 | //! Cargo features: 13 | //! 14 | //! - `ctor` 15 | //! A global string cache is used when interning strings. This cache is normally 16 | //! initialized lazily on first use. Enabling the `ctor` feature will cause it 17 | //! to be eagerly initialized on startup. 18 | //! There is no performance benefit to this, but it can help avoid false positives 19 | //! from tools like `mockalloc` which try to detect memory leaks during tests. 20 | #![deny(missing_docs, missing_debug_implementations)] 21 | 22 | #[macro_use] 23 | mod macros; 24 | 25 | pub mod array; 26 | pub mod number; 27 | pub mod object; 28 | pub mod string; 29 | mod thin; 30 | mod value; 31 | 32 | pub use array::IArray; 33 | pub use number::INumber; 34 | pub use object::IObject; 35 | pub use string::IString; 36 | pub use value::{ 37 | BoolMut, Destructured, DestructuredMut, DestructuredRef, IValue, ValueIndex, ValueType, 38 | }; 39 | 40 | mod de; 41 | mod ser; 42 | pub use de::from_value; 43 | pub use ser::to_value; 44 | 45 | #[cfg(all(test, not(miri)))] 46 | mod tests { 47 | use mockalloc::Mockalloc; 48 | use std::alloc::System; 49 | 50 | #[global_allocator] 51 | static ALLOCATOR: Mockalloc = Mockalloc(System); 52 | } 53 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! value_subtype_impls { 2 | ($t:ty, $cf:ident, $rcf:ident, $mcf:ident) => { 3 | impl std::convert::AsRef for $t { 4 | fn as_ref(&self) -> &crate::IValue { 5 | &self.0 6 | } 7 | } 8 | impl std::convert::AsMut for $t { 9 | fn as_mut(&mut self) -> &mut crate::IValue { 10 | &mut self.0 11 | } 12 | } 13 | impl std::borrow::Borrow for $t { 14 | fn borrow(&self) -> &crate::IValue { 15 | &self.0 16 | } 17 | } 18 | impl std::borrow::BorrowMut for $t { 19 | fn borrow_mut(&mut self) -> &mut crate::IValue { 20 | &mut self.0 21 | } 22 | } 23 | impl std::convert::From<$t> for crate::IValue { 24 | fn from(other: $t) -> Self { 25 | other.0 26 | } 27 | } 28 | impl std::convert::TryFrom for $t { 29 | type Error = crate::IValue; 30 | fn try_from(other: crate::IValue) -> Result { 31 | other.$cf() 32 | } 33 | } 34 | impl<'a> std::convert::TryFrom<&'a crate::IValue> for &'a $t { 35 | type Error = (); 36 | fn try_from(other: &'a crate::IValue) -> Result { 37 | other.$rcf().ok_or(()) 38 | } 39 | } 40 | impl<'a> std::convert::TryFrom<&'a mut crate::IValue> for &'a mut $t { 41 | type Error = (); 42 | fn try_from(other: &'a mut crate::IValue) -> Result { 43 | other.$mcf().ok_or(()) 44 | } 45 | } 46 | }; 47 | } 48 | 49 | macro_rules! typed_conversions { 50 | ($( 51 | $interm:ty: $( 52 | $src:ty 53 | $(where ($($gb:tt)*))* 54 | ),*; 55 | )*) => { 56 | $( 57 | $( 58 | impl $(<$($gb)*>)* From<$src> for IValue { 59 | fn from(other: $src) -> Self { 60 | <$interm>::from(other).into() 61 | } 62 | } 63 | )* 64 | )* 65 | } 66 | } 67 | 68 | /// Construct an [`IValue`] using familiar JSON syntax. 69 | /// 70 | /// For example: 71 | /// ``` 72 | /// use ijson::{ijson, IValue}; 73 | /// 74 | /// let _: IValue = ijson!({ "foo": null, "bar": [1, 2, 3, 4] }); 75 | /// ``` 76 | /// 77 | /// [`IValue`]: super::IValue 78 | #[macro_export(local_inner_macros)] 79 | macro_rules! ijson { 80 | // Hide implementation details from the generated rustdoc. 81 | ($($json:tt)+) => { 82 | $crate::ijson_internal!($($json)+) 83 | }; 84 | } 85 | 86 | #[macro_export(local_inner_macros)] 87 | #[doc(hidden)] 88 | macro_rules! ijson_internal { 89 | // Done without trailing comma. 90 | (@array $array:ident) => {}; 91 | 92 | // Done with trailing comma. 93 | (@array $array:ident ,) => {}; 94 | 95 | // Next element is `null`. 96 | (@array $array:ident , null $($rest:tt)*) => { 97 | $array.push(ijson_internal!(null)); 98 | ijson_internal!(@array $array $($rest)*) 99 | }; 100 | 101 | // Next element is `true`. 102 | (@array $array:ident , true $($rest:tt)*) => { 103 | $array.push(ijson_internal!(true)); 104 | ijson_internal!(@array $array $($rest)*) 105 | }; 106 | 107 | // Next element is `false`. 108 | (@array $array:ident , false $($rest:tt)*) => { 109 | $array.push(ijson_internal!(false)); 110 | ijson_internal!(@array $array $($rest)*) 111 | }; 112 | 113 | // Next element is an array. 114 | (@array $array:ident , [$($arr:tt)*] $($rest:tt)*) => { 115 | $array.push(ijson_internal!([$($arr)*])); 116 | ijson_internal!(@array $array $($rest)*) 117 | }; 118 | 119 | // Next element is an object. 120 | (@array $array:ident , {$($obj:tt)*} $($rest:tt)*) => { 121 | $array.push(ijson_internal!({$($obj)*})); 122 | ijson_internal!(@array $array $($rest)*) 123 | }; 124 | 125 | // Next element is an expression followed by comma. 126 | (@array $array:ident , $next:expr , $($rest:tt)*) => { 127 | $array.push(ijson_internal!($next)); 128 | ijson_internal!(@array $array , $($rest)*) 129 | }; 130 | 131 | // Last element is an expression with no trailing comma. 132 | (@array $array:ident , $last:expr) => { 133 | $array.push(ijson_internal!($last)); 134 | }; 135 | 136 | // Unexpected token after most recent element. 137 | (@array $array:ident , $unexpected:tt $($rest:tt)*) => { 138 | ijson_unexpected!($unexpected) 139 | }; 140 | 141 | // Unexpected token after most recent element. 142 | (@array $array:ident $unexpected:tt $($rest:tt)*) => { 143 | ijson_unexpected!($unexpected) 144 | }; 145 | 146 | // Done. 147 | (@object $object:ident () () ()) => {}; 148 | 149 | // Insert the current entry followed by trailing comma. 150 | (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { 151 | let _ = $object.insert(($($key)+), $value); 152 | ijson_internal!(@object $object () ($($rest)*) ($($rest)*)); 153 | }; 154 | 155 | // Current entry followed by unexpected token. 156 | (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { 157 | ijson_unexpected!($unexpected); 158 | }; 159 | 160 | // Insert the last entry without trailing comma. 161 | (@object $object:ident [$($key:tt)+] ($value:expr)) => { 162 | let _ = $object.insert(($($key)+), $value); 163 | }; 164 | 165 | // Next value is `null`. 166 | (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { 167 | ijson_internal!(@object $object [$($key)+] (ijson_internal!(null)) $($rest)*); 168 | }; 169 | 170 | // Next value is `true`. 171 | (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => { 172 | ijson_internal!(@object $object [$($key)+] (ijson_internal!(true)) $($rest)*); 173 | }; 174 | 175 | // Next value is `false`. 176 | (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => { 177 | ijson_internal!(@object $object [$($key)+] (ijson_internal!(false)) $($rest)*); 178 | }; 179 | 180 | // Next value is an array. 181 | (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { 182 | ijson_internal!(@object $object [$($key)+] (ijson_internal!([$($array)*])) $($rest)*); 183 | }; 184 | 185 | // Next value is a map. 186 | (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { 187 | ijson_internal!(@object $object [$($key)+] (ijson_internal!({$($map)*})) $($rest)*); 188 | }; 189 | 190 | // Next value is an expression followed by comma. 191 | (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { 192 | ijson_internal!(@object $object [$($key)+] (ijson_internal!($value)) , $($rest)*); 193 | }; 194 | 195 | // Last value is an expression with no trailing comma. 196 | (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { 197 | ijson_internal!(@object $object [$($key)+] (ijson_internal!($value))); 198 | }; 199 | 200 | // Missing value for last entry. Trigger a reasonable error message. 201 | (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { 202 | // "unexpected end of macro invocation" 203 | ijson_internal!(); 204 | }; 205 | 206 | // Missing colon and value for last entry. Trigger a reasonable error 207 | // message. 208 | (@object $object:ident ($($key:tt)+) () $copy:tt) => { 209 | // "unexpected end of macro invocation" 210 | ijson_internal!(); 211 | }; 212 | 213 | // Misplaced colon. Trigger a reasonable error message. 214 | (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { 215 | // Takes no arguments so "no rules expected the token `:`". 216 | ijson_unexpected!($colon); 217 | }; 218 | 219 | // Found a comma inside a key. Trigger a reasonable error message. 220 | (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { 221 | // Takes no arguments so "no rules expected the token `,`". 222 | ijson_unexpected!($comma); 223 | }; 224 | 225 | // Key is fully parenthesized. This avoids clippy double_parens false 226 | // positives because the parenthesization may be necessary here. 227 | (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { 228 | ijson_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); 229 | }; 230 | 231 | // Refuse to absorb colon token into key expression. 232 | (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { 233 | ijson_expect_expr_comma!($($unexpected)+); 234 | }; 235 | 236 | // Munch a token into the current key. 237 | (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { 238 | ijson_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); 239 | }; 240 | 241 | ////////////////////////////////////////////////////////////////////////// 242 | // The main implementation. 243 | // 244 | // Must be invoked as: ijson_internal!($($json)+) 245 | ////////////////////////////////////////////////////////////////////////// 246 | 247 | (null) => { 248 | $crate::IValue::NULL 249 | }; 250 | 251 | (true) => { 252 | $crate::IValue::TRUE 253 | }; 254 | 255 | (false) => { 256 | $crate::IValue::FALSE 257 | }; 258 | 259 | ([]) => { 260 | $crate::IValue::from($crate::IArray::new()) 261 | }; 262 | 263 | ([ $($tt:tt)+ ]) => { 264 | $crate::IValue::from({ 265 | let mut array = $crate::IArray::new(); 266 | ijson_internal!(@array array , $($tt)+); 267 | array 268 | }) 269 | }; 270 | 271 | ({}) => { 272 | $crate::IValue::from($crate::IObject::new()) 273 | }; 274 | 275 | ({ $($tt:tt)+ }) => { 276 | $crate::IValue::from({ 277 | let mut object = $crate::IObject::new(); 278 | ijson_internal!(@object object () ($($tt)+) ($($tt)+)); 279 | object 280 | }) 281 | }; 282 | 283 | // Any Serialize type: numbers, strings, struct literals, variables etc. 284 | // Must be below every other rule. 285 | ($other:expr) => { 286 | $crate::to_value(&$other).unwrap() 287 | }; 288 | } 289 | 290 | #[macro_export] 291 | #[doc(hidden)] 292 | macro_rules! ijson_unexpected { 293 | () => {}; 294 | } 295 | 296 | #[macro_export] 297 | #[doc(hidden)] 298 | macro_rules! ijson_expect_expr_comma { 299 | ($e:expr , $($tt:tt)*) => {}; 300 | } 301 | -------------------------------------------------------------------------------- /src/number.rs: -------------------------------------------------------------------------------- 1 | //! Functionality relating to the JSON number type 2 | #![allow(clippy::float_cmp)] 3 | 4 | use std::alloc::{alloc, dealloc, Layout, LayoutError}; 5 | use std::cmp::Ordering; 6 | use std::convert::{TryFrom, TryInto}; 7 | use std::fmt::{self, Debug, Formatter}; 8 | use std::hash::Hash; 9 | 10 | use crate::thin::{ThinMut, ThinMutExt, ThinRef, ThinRefExt}; 11 | 12 | use super::value::{IValue, TypeTag}; 13 | 14 | #[repr(u8)] 15 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 16 | enum NumberType { 17 | Static, 18 | I24, 19 | I64, 20 | U64, 21 | F64, 22 | } 23 | 24 | #[repr(C)] 25 | #[repr(align(4))] 26 | struct Header { 27 | type_: NumberType, 28 | short: u8, 29 | static_: i16, 30 | } 31 | 32 | fn can_represent_as_f64(x: u64) -> bool { 33 | x.leading_zeros() + x.trailing_zeros() >= 11 34 | } 35 | 36 | fn can_represent_as_f32(x: u64) -> bool { 37 | x.leading_zeros() + x.trailing_zeros() >= 40 38 | } 39 | 40 | fn cmp_i64_to_f64(a: i64, b: f64) -> Ordering { 41 | if a < 0 { 42 | cmp_u64_to_f64(a.wrapping_neg() as u64, -b).reverse() 43 | } else { 44 | cmp_u64_to_f64(a as u64, b) 45 | } 46 | } 47 | 48 | fn cmp_u64_to_f64(a: u64, b: f64) -> Ordering { 49 | if can_represent_as_f64(a) { 50 | // If we can represent as an f64, we can just cast and compare 51 | (a as f64).partial_cmp(&b).unwrap() 52 | } else if b <= (0x0020_0000_0000_0000_u64 as f64) { 53 | // If the floating point number is less than all non-representable 54 | // integers, and our integer is non-representable, then we know 55 | // the integer is greater. 56 | Ordering::Greater 57 | } else if b >= u64::MAX as f64 { 58 | // If the floating point number is larger than the largest u64, then 59 | // the integer is smaller. 60 | Ordering::Less 61 | } else { 62 | // The remaining floating point values can be losslessly converted to u64. 63 | a.cmp(&(b as u64)) 64 | } 65 | } 66 | 67 | trait HeaderRef<'a>: ThinRefExt<'a, Header> { 68 | fn i24_unchecked(&self) -> i32 { 69 | (i32::from(self.static_) << 8) | i32::from(self.short) 70 | } 71 | unsafe fn payload_ptr(&self) -> *const u64 { 72 | self.ptr().cast::().add(1) 73 | } 74 | unsafe fn i64_unchecked(&self) -> &'a i64 { 75 | &*self.payload_ptr().cast() 76 | } 77 | unsafe fn u64_unchecked(&self) -> &'a u64 { 78 | &*self.payload_ptr() 79 | } 80 | unsafe fn f64_unchecked(&self) -> &'a f64 { 81 | &*self.payload_ptr().cast() 82 | } 83 | fn to_i64(&self) -> Option { 84 | // Safety: We only call methods appropriate for the type 85 | unsafe { 86 | match self.type_ { 87 | NumberType::Static => Some(i64::from(self.static_)), 88 | NumberType::I24 => Some(i64::from(self.i24_unchecked())), 89 | NumberType::I64 => Some(*self.i64_unchecked()), 90 | NumberType::U64 => { 91 | let v = *self.u64_unchecked(); 92 | i64::try_from(v).ok() 93 | } 94 | NumberType::F64 => { 95 | let v = *self.f64_unchecked(); 96 | if v.fract() == 0.0 && v > i64::MIN as f64 && v < i64::MAX as f64 { 97 | Some(v as i64) 98 | } else { 99 | None 100 | } 101 | } 102 | } 103 | } 104 | } 105 | fn to_u64(&self) -> Option { 106 | // Safety: We only call methods appropriate for the type 107 | unsafe { 108 | match self.type_ { 109 | NumberType::Static => { 110 | if self.static_ >= 0 { 111 | Some(self.static_ as u64) 112 | } else { 113 | None 114 | } 115 | } 116 | NumberType::I24 => { 117 | let v = self.i24_unchecked(); 118 | if v >= 0 { 119 | Some(v as u64) 120 | } else { 121 | None 122 | } 123 | } 124 | NumberType::I64 => { 125 | let v = *self.i64_unchecked(); 126 | if v >= 0 { 127 | Some(v as u64) 128 | } else { 129 | None 130 | } 131 | } 132 | NumberType::U64 => Some(*self.u64_unchecked()), 133 | NumberType::F64 => { 134 | let v = *self.f64_unchecked(); 135 | if v.fract() == 0.0 && v > 0.0 && v < u64::MAX as f64 { 136 | Some(v as u64) 137 | } else { 138 | None 139 | } 140 | } 141 | } 142 | } 143 | } 144 | fn to_f64(&self) -> Option { 145 | // Safety: We only call methods appropriate for the type 146 | unsafe { 147 | match self.type_ { 148 | NumberType::Static => Some(f64::from(self.static_)), 149 | NumberType::I24 => Some(f64::from(self.i24_unchecked())), 150 | NumberType::I64 => { 151 | let v = *self.i64_unchecked(); 152 | let can_represent = if v < 0 { 153 | can_represent_as_f64(v.wrapping_neg() as u64) 154 | } else { 155 | can_represent_as_f64(v as u64) 156 | }; 157 | if can_represent { 158 | Some(v as f64) 159 | } else { 160 | None 161 | } 162 | } 163 | NumberType::U64 => { 164 | let v = *self.u64_unchecked(); 165 | if can_represent_as_f64(v) { 166 | Some(v as f64) 167 | } else { 168 | None 169 | } 170 | } 171 | NumberType::F64 => Some(*self.f64_unchecked()), 172 | } 173 | } 174 | } 175 | fn to_f32(&self) -> Option { 176 | // Safety: We only call methods appropriate for the type 177 | unsafe { 178 | match self.type_ { 179 | NumberType::Static => Some(f32::from(self.static_)), 180 | NumberType::I24 => Some(self.i24_unchecked() as f32), 181 | NumberType::I64 => { 182 | let v = *self.i64_unchecked(); 183 | let can_represent = if v < 0 { 184 | can_represent_as_f32(v.wrapping_neg() as u64) 185 | } else { 186 | can_represent_as_f32(v as u64) 187 | }; 188 | if can_represent { 189 | Some(v as f32) 190 | } else { 191 | None 192 | } 193 | } 194 | NumberType::U64 => { 195 | let v = *self.u64_unchecked(); 196 | if can_represent_as_f32(v) { 197 | Some(v as f32) 198 | } else { 199 | None 200 | } 201 | } 202 | NumberType::F64 => { 203 | let v = *self.f64_unchecked(); 204 | let u = v as f32; 205 | if v == f64::from(u) { 206 | Some(u) 207 | } else { 208 | None 209 | } 210 | } 211 | } 212 | } 213 | } 214 | fn has_decimal_point(&self) -> bool { 215 | match self.type_ { 216 | NumberType::Static | NumberType::I24 | NumberType::I64 | NumberType::U64 => false, 217 | NumberType::F64 => true, 218 | } 219 | } 220 | fn to_f64_lossy(&self) -> f64 { 221 | unsafe { 222 | match self.type_ { 223 | NumberType::Static => f64::from(self.static_), 224 | NumberType::I24 => f64::from(self.i24_unchecked()), 225 | NumberType::I64 => *self.i64_unchecked() as f64, 226 | NumberType::U64 => *self.u64_unchecked() as f64, 227 | NumberType::F64 => *self.f64_unchecked(), 228 | } 229 | } 230 | } 231 | fn cmp<'b>(&self, other: impl HeaderRef<'b>) -> Ordering { 232 | // Fast path 233 | if self.type_ == other.type_ { 234 | // Safety: We only call methods for the appropriate type 235 | unsafe { 236 | match self.type_ { 237 | NumberType::Static => self.static_.cmp(&other.static_), 238 | NumberType::I24 => self.i24_unchecked().cmp(&other.i24_unchecked()), 239 | NumberType::I64 => self.i64_unchecked().cmp(other.i64_unchecked()), 240 | NumberType::U64 => self.u64_unchecked().cmp(other.u64_unchecked()), 241 | NumberType::F64 => self 242 | .f64_unchecked() 243 | .partial_cmp(other.f64_unchecked()) 244 | .unwrap(), 245 | } 246 | } 247 | } else { 248 | // Safety: We only call methods for the appropriate type 249 | unsafe { 250 | match (self.type_, other.type_) { 251 | (NumberType::U64, NumberType::F64) => { 252 | cmp_u64_to_f64(*self.u64_unchecked(), *other.f64_unchecked()) 253 | } 254 | (NumberType::F64, NumberType::U64) => { 255 | cmp_u64_to_f64(*other.u64_unchecked(), *self.f64_unchecked()).reverse() 256 | } 257 | (NumberType::I64, NumberType::F64) => { 258 | cmp_i64_to_f64(*self.i64_unchecked(), *other.f64_unchecked()) 259 | } 260 | (NumberType::F64, NumberType::I64) => { 261 | cmp_i64_to_f64(*other.i64_unchecked(), *self.f64_unchecked()).reverse() 262 | } 263 | (_, NumberType::F64) => self 264 | .to_f64() 265 | .unwrap() 266 | .partial_cmp(other.f64_unchecked()) 267 | .unwrap(), 268 | (NumberType::F64, _) => other 269 | .to_f64() 270 | .unwrap() 271 | .partial_cmp(self.f64_unchecked()) 272 | .unwrap() 273 | .reverse(), 274 | (NumberType::U64, _) => Ordering::Greater, 275 | (_, NumberType::U64) => Ordering::Less, 276 | _ => self.to_i64().cmp(&other.to_i64()), 277 | } 278 | } 279 | } 280 | } 281 | } 282 | 283 | trait HeaderMut<'a>: ThinMutExt<'a, Header> { 284 | unsafe fn payload_ptr_mut(mut self) -> *mut u64 { 285 | self.ptr_mut().cast::().add(1) 286 | } 287 | unsafe fn i64_unchecked_mut(self) -> &'a mut i64 { 288 | &mut *self.payload_ptr_mut().cast() 289 | } 290 | unsafe fn u64_unchecked_mut(self) -> &'a mut u64 { 291 | &mut *self.payload_ptr_mut() 292 | } 293 | unsafe fn f64_unchecked_mut(self) -> &'a mut f64 { 294 | &mut *self.payload_ptr_mut().cast() 295 | } 296 | } 297 | 298 | impl<'a, T: ThinRefExt<'a, Header>> HeaderRef<'a> for T {} 299 | impl<'a, T: ThinMutExt<'a, Header>> HeaderMut<'a> for T {} 300 | 301 | macro_rules! define_static_numbers { 302 | (@recurse $from:ident ($($offset:expr,)*) ()) => { 303 | [$(Header { 304 | type_: NumberType::Static, 305 | short: 0, 306 | static_: $from + ($offset), 307 | }),*] 308 | }; 309 | (@recurse $from:ident ($($offset:expr,)*) ($u:literal $($v:literal)*)) => { 310 | define_static_numbers!(@recurse $from ($($offset,)* $($offset | (1 << $u),)*) ($($v)*)) 311 | }; 312 | ($from:ident $($v:literal)*) => { 313 | define_static_numbers!(@recurse $from (0,) ($($v)*)) 314 | }; 315 | } 316 | 317 | // We want to cover the range -128..256 with static numbers so that arrays of i8 and u8 can be 318 | // stored reasonably efficiently. In practice, we end up covering -128..384. 319 | const STATIC_LOWER: i16 = -128; 320 | const STATIC_LEN: usize = 512; 321 | const STATIC_UPPER: i16 = STATIC_LOWER + STATIC_LEN as i16; 322 | static STATIC_NUMBERS: [Header; STATIC_LEN] = 323 | define_static_numbers!(STATIC_LOWER 0 1 2 3 4 5 6 7 8); 324 | 325 | // Range of a 24-bit signed integer. 326 | const SHORT_LOWER: i64 = -0x0080_0000; 327 | const SHORT_UPPER: i64 = 0x0080_0000; 328 | 329 | /// The `INumber` type represents a JSON number. It is decoupled from any specific 330 | /// representation, and internally uses several. There is no way to determine the 331 | /// internal representation: instead the caller is expected to convert the number 332 | /// using one of the fallible `to_xxx` functions and handle the cases where the 333 | /// number does not convert to the desired type. 334 | /// 335 | /// Special floating point values (eg. NaN, Infinity, etc.) cannot be stored within 336 | /// an `INumber`. 337 | /// 338 | /// Whilst `INumber` does not consider `2.0` and `2` to be different numbers (ie. 339 | /// they will compare equal) it does allow you to distinguish them using the 340 | /// method `INumber::has_decimal_point()`. That said, calling `to_i32` on 341 | /// `2.0` will succeed with the value `2`. 342 | /// 343 | /// Currently `INumber` can store any number representable with an `f64`, `i64` or 344 | /// `u64`. It is expected that in the future it will be further expanded to store 345 | /// integers and possibly decimals to arbitrary precision, but that is not currently 346 | /// the case. 347 | /// 348 | /// Any number representable with an `i8` or a `u8` can be stored in an `INumber` 349 | /// without a heap allocation (so JSON byte arrays are relatively efficient). 350 | /// Integers up to 24 bits can be stored with a 4-byte heap allocation. 351 | #[repr(transparent)] 352 | #[derive(Clone)] 353 | pub struct INumber(pub(crate) IValue); 354 | 355 | value_subtype_impls!(INumber, into_number, as_number, as_number_mut); 356 | 357 | impl INumber { 358 | fn layout(type_: NumberType) -> Result { 359 | let mut res = Layout::new::
(); 360 | match type_ { 361 | NumberType::Static => unreachable!(), 362 | NumberType::I24 => {} 363 | // On 32-bit Linux, 64-bit values have 4 byte alignment be we assume they have 8 364 | // like on all other platforms. Therefore, ensure they are aligned to 8 bytes minimum. 365 | NumberType::I64 => { 366 | res = res 367 | .extend(Layout::new::().align_to(8)?)? 368 | .0 369 | .pad_to_align() 370 | } 371 | NumberType::U64 => { 372 | res = res 373 | .extend(Layout::new::().align_to(8)?)? 374 | .0 375 | .pad_to_align() 376 | } 377 | NumberType::F64 => { 378 | res = res 379 | .extend(Layout::new::().align_to(8)?)? 380 | .0 381 | .pad_to_align() 382 | } 383 | } 384 | Ok(res) 385 | } 386 | 387 | fn alloc(type_: NumberType) -> *mut Header { 388 | unsafe { 389 | let ptr = alloc(Self::layout(type_).unwrap()).cast::
(); 390 | ptr.write(Header { 391 | type_, 392 | static_: 0, 393 | short: 0, 394 | }); 395 | ptr 396 | } 397 | } 398 | 399 | fn dealloc(ptr: *mut Header) { 400 | unsafe { 401 | let layout = Self::layout((*ptr).type_).unwrap(); 402 | dealloc(ptr.cast::(), layout); 403 | } 404 | } 405 | 406 | /// Returns the number zero (without a decimal point). Does not allocate. 407 | #[must_use] 408 | pub fn zero() -> Self { 409 | // Safety: 0 is in the static range 410 | unsafe { Self::new_static(0) } 411 | } 412 | /// Returns the number one (without a decimal point). Does not allocate. 413 | #[must_use] 414 | pub fn one() -> Self { 415 | // Safety: 1 is in the static range 416 | unsafe { Self::new_static(1) } 417 | } 418 | // Safety: Value must be in the range STATIC_LOWER..STATIC_UPPER 419 | unsafe fn new_static(value: i16) -> Self { 420 | INumber(IValue::new_ref( 421 | &STATIC_NUMBERS[(value - STATIC_LOWER) as usize], 422 | TypeTag::Number, 423 | )) 424 | } 425 | fn new_ptr(type_: NumberType) -> Self { 426 | unsafe { 427 | INumber(IValue::new_ptr( 428 | Self::alloc(type_).cast::(), 429 | TypeTag::Number, 430 | )) 431 | } 432 | } 433 | fn header(&self) -> ThinRef
{ 434 | unsafe { ThinRef::new(self.0.ptr().cast()) } 435 | } 436 | 437 | fn header_mut(&mut self) -> ThinMut
{ 438 | unsafe { ThinMut::new(self.0.ptr().cast()) } 439 | } 440 | 441 | fn is_static(&self) -> bool { 442 | self.header().type_ == NumberType::Static 443 | } 444 | 445 | // Value must fit in an i24 446 | fn new_short(value: i32) -> Self { 447 | if value >= i32::from(STATIC_LOWER) && value < i32::from(STATIC_UPPER) { 448 | // Safety: We checked the value is in the static range 449 | unsafe { Self::new_static(value as i16) } 450 | } else { 451 | let lo_bits = value as u8; 452 | let hi_bits = (value >> 8) as i16; 453 | let mut res = Self::new_ptr(NumberType::I24); 454 | let mut hd = res.header_mut(); 455 | hd.short = lo_bits; 456 | hd.static_ = hi_bits; 457 | res 458 | } 459 | } 460 | 461 | fn new_i64(value: i64) -> Self { 462 | if (SHORT_LOWER..SHORT_UPPER).contains(&value) { 463 | Self::new_short(value as i32) 464 | } else { 465 | let mut res = Self::new_ptr(NumberType::I64); 466 | // Safety: We know this is an i64 because we just created it 467 | unsafe { 468 | *res.header_mut().i64_unchecked_mut() = value; 469 | } 470 | res 471 | } 472 | } 473 | 474 | fn new_u64(value: u64) -> Self { 475 | if let Ok(res) = i64::try_from(value) { 476 | Self::new_i64(res) 477 | } else { 478 | let mut res = Self::new_ptr(NumberType::U64); 479 | // Safety: We know this is an i64 because we just created it 480 | unsafe { 481 | *res.header_mut().u64_unchecked_mut() = value; 482 | } 483 | res 484 | } 485 | } 486 | 487 | fn new_f64(value: f64) -> Self { 488 | let mut res = Self::new_ptr(NumberType::F64); 489 | // Safety: We know this is an i64 because we just created it 490 | unsafe { 491 | *res.header_mut().f64_unchecked_mut() = value; 492 | } 493 | res 494 | } 495 | 496 | pub(crate) fn clone_impl(&self) -> IValue { 497 | let hd = self.header(); 498 | // Safety: We only call methods appropriate for the matched type 499 | unsafe { 500 | match hd.type_ { 501 | NumberType::Static => self.0.raw_copy(), 502 | NumberType::I24 => Self::new_short(hd.i24_unchecked()).0, 503 | NumberType::I64 => Self::new_i64(*hd.i64_unchecked()).0, 504 | NumberType::U64 => Self::new_u64(*hd.u64_unchecked()).0, 505 | NumberType::F64 => Self::new_f64(*hd.f64_unchecked()).0, 506 | } 507 | } 508 | } 509 | pub(crate) fn drop_impl(&mut self) { 510 | if !self.is_static() { 511 | unsafe { 512 | Self::dealloc(self.0.ptr().cast()); 513 | self.0.set_ref(&STATIC_NUMBERS[0]); 514 | } 515 | } 516 | } 517 | 518 | /// Converts this number to an i64 if it can be represented exactly. 519 | #[must_use] 520 | pub fn to_i64(&self) -> Option { 521 | self.header().to_i64() 522 | } 523 | /// Converts this number to an f64 if it can be represented exactly. 524 | #[must_use] 525 | pub fn to_u64(&self) -> Option { 526 | self.header().to_u64() 527 | } 528 | /// Converts this number to an f64 if it can be represented exactly. 529 | #[must_use] 530 | pub fn to_f64(&self) -> Option { 531 | self.header().to_f64() 532 | } 533 | /// Converts this number to an f32 if it can be represented exactly. 534 | #[must_use] 535 | pub fn to_f32(&self) -> Option { 536 | self.header().to_f32() 537 | } 538 | /// Converts this number to an i32 if it can be represented exactly. 539 | #[must_use] 540 | pub fn to_i32(&self) -> Option { 541 | self.header().to_i64().and_then(|x| x.try_into().ok()) 542 | } 543 | /// Converts this number to a u32 if it can be represented exactly. 544 | #[must_use] 545 | pub fn to_u32(&self) -> Option { 546 | self.header().to_u64().and_then(|x| x.try_into().ok()) 547 | } 548 | /// Converts this number to an isize if it can be represented exactly. 549 | #[must_use] 550 | pub fn to_isize(&self) -> Option { 551 | self.header().to_i64().and_then(|x| x.try_into().ok()) 552 | } 553 | /// Converts this number to a usize if it can be represented exactly. 554 | #[must_use] 555 | pub fn to_usize(&self) -> Option { 556 | self.header().to_u64().and_then(|x| x.try_into().ok()) 557 | } 558 | /// Converts this number to an f64, potentially losing precision in the process. 559 | #[must_use] 560 | pub fn to_f64_lossy(&self) -> f64 { 561 | self.header().to_f64_lossy() 562 | } 563 | /// Converts this number to an f32, potentially losing precision in the process. 564 | #[must_use] 565 | pub fn to_f32_lossy(&self) -> f32 { 566 | self.to_f64_lossy() as f32 567 | } 568 | 569 | /// This allows distinguishing between `1.0` and `1` in the original JSON. 570 | /// Numeric operations will otherwise treat these two values as equivalent. 571 | #[must_use] 572 | pub fn has_decimal_point(&self) -> bool { 573 | self.header().has_decimal_point() 574 | } 575 | } 576 | 577 | impl Hash for INumber { 578 | fn hash(&self, state: &mut H) { 579 | let hd = self.header(); 580 | if let Some(v) = hd.to_i64() { 581 | v.hash(state); 582 | } else if let Some(v) = hd.to_u64() { 583 | v.hash(state); 584 | } else if let Some(v) = hd.to_f64() { 585 | let bits = if v == 0.0 { 586 | 0 // this accounts for +0.0 and -0.0 587 | } else { 588 | v.to_bits() 589 | }; 590 | bits.hash(state); 591 | } 592 | } 593 | } 594 | 595 | impl From for INumber { 596 | fn from(v: u64) -> Self { 597 | Self::new_u64(v) 598 | } 599 | } 600 | impl From for INumber { 601 | fn from(v: u32) -> Self { 602 | Self::new_u64(u64::from(v)) 603 | } 604 | } 605 | impl From for INumber { 606 | fn from(v: u16) -> Self { 607 | Self::new_short(i32::from(v)) 608 | } 609 | } 610 | impl From for INumber { 611 | fn from(v: u8) -> Self { 612 | // Safety: All u8s are in the static range 613 | unsafe { Self::new_static(i16::from(v)) } 614 | } 615 | } 616 | impl From for INumber { 617 | fn from(v: usize) -> Self { 618 | Self::new_u64(v as u64) 619 | } 620 | } 621 | 622 | impl From for INumber { 623 | fn from(v: i64) -> Self { 624 | Self::new_i64(v) 625 | } 626 | } 627 | impl From for INumber { 628 | fn from(v: i32) -> Self { 629 | Self::new_i64(i64::from(v)) 630 | } 631 | } 632 | impl From for INumber { 633 | fn from(v: i16) -> Self { 634 | Self::new_short(i32::from(v)) 635 | } 636 | } 637 | impl From for INumber { 638 | fn from(v: i8) -> Self { 639 | // Safety: All i8s are in the static range 640 | unsafe { Self::new_static(i16::from(v)) } 641 | } 642 | } 643 | impl From for INumber { 644 | fn from(v: isize) -> Self { 645 | Self::new_i64(v as i64) 646 | } 647 | } 648 | 649 | impl TryFrom for INumber { 650 | type Error = (); 651 | fn try_from(v: f64) -> Result { 652 | if v.is_finite() { 653 | Ok(Self::new_f64(v)) 654 | } else { 655 | Err(()) 656 | } 657 | } 658 | } 659 | 660 | impl TryFrom for INumber { 661 | type Error = (); 662 | fn try_from(v: f32) -> Result { 663 | if v.is_finite() { 664 | Ok(Self::new_f64(f64::from(v))) 665 | } else { 666 | Err(()) 667 | } 668 | } 669 | } 670 | 671 | impl PartialEq for INumber { 672 | fn eq(&self, other: &Self) -> bool { 673 | self.cmp(other) == Ordering::Equal 674 | } 675 | } 676 | 677 | impl Eq for INumber {} 678 | impl Ord for INumber { 679 | fn cmp(&self, other: &Self) -> Ordering { 680 | if self.0.raw_eq(&other.0) { 681 | Ordering::Equal 682 | } else { 683 | self.header().cmp(other.header()) 684 | } 685 | } 686 | } 687 | impl PartialOrd for INumber { 688 | fn partial_cmp(&self, other: &Self) -> Option { 689 | Some(self.cmp(other)) 690 | } 691 | } 692 | 693 | impl Debug for INumber { 694 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 695 | if let Some(v) = self.to_i64() { 696 | Debug::fmt(&v, f) 697 | } else if let Some(v) = self.to_u64() { 698 | Debug::fmt(&v, f) 699 | } else if let Some(v) = self.to_f64() { 700 | Debug::fmt(&v, f) 701 | } else { 702 | unreachable!() 703 | } 704 | } 705 | } 706 | 707 | impl Default for INumber { 708 | fn default() -> Self { 709 | Self::zero() 710 | } 711 | } 712 | 713 | #[cfg(test)] 714 | mod tests { 715 | use super::*; 716 | 717 | #[mockalloc::test] 718 | fn can_create() { 719 | let x = INumber::zero(); 720 | let y: INumber = (0.0).try_into().unwrap(); 721 | 722 | assert_eq!(x, y); 723 | assert!(!x.has_decimal_point()); 724 | assert!(y.has_decimal_point()); 725 | assert_eq!(x.to_i32(), Some(0)); 726 | assert_eq!(y.to_i32(), Some(0)); 727 | assert!(INumber::try_from(f32::INFINITY).is_err()); 728 | assert!(INumber::try_from(f64::INFINITY).is_err()); 729 | assert!(INumber::try_from(f32::NEG_INFINITY).is_err()); 730 | assert!(INumber::try_from(f64::NEG_INFINITY).is_err()); 731 | assert!(INumber::try_from(f32::NAN).is_err()); 732 | assert!(INumber::try_from(f64::NAN).is_err()); 733 | } 734 | 735 | #[mockalloc::test] 736 | fn can_store_various_numbers() { 737 | let x: INumber = 256.into(); 738 | assert_eq!(x.to_i64(), Some(256)); 739 | assert_eq!(x.to_u64(), Some(256)); 740 | assert_eq!(x.to_f64(), Some(256.0)); 741 | 742 | let x: INumber = 0x1000000.into(); 743 | assert_eq!(x.to_i64(), Some(0x1000000)); 744 | assert_eq!(x.to_u64(), Some(0x1000000)); 745 | assert_eq!(x.to_f64(), Some(16_777_216.0)); 746 | 747 | let x: INumber = i64::MIN.into(); 748 | assert_eq!(x.to_i64(), Some(i64::MIN)); 749 | assert_eq!(x.to_u64(), None); 750 | assert_eq!(x.to_f64(), Some(-9_223_372_036_854_775_808.0)); 751 | 752 | let x: INumber = i64::MAX.into(); 753 | assert_eq!(x.to_i64(), Some(i64::MAX)); 754 | assert_eq!(x.to_u64(), Some(i64::MAX as u64)); 755 | assert_eq!(x.to_f64(), None); 756 | 757 | let x: INumber = u64::MAX.into(); 758 | assert_eq!(x.to_i64(), None); 759 | assert_eq!(x.to_u64(), Some(u64::MAX)); 760 | assert_eq!(x.to_f64(), None); 761 | 762 | let x: INumber = 13369629.into(); 763 | assert_eq!(x.to_i64(), Some(13_369_629)); 764 | assert_eq!(x.to_u64(), Some(13_369_629)); 765 | assert_eq!(x.to_f64(), Some(13_369_629.0)); 766 | 767 | let x: INumber = 0x800000.into(); 768 | assert_eq!(x.to_i64(), Some(0x800000)); 769 | assert_eq!(x.to_u64(), Some(0x800000)); 770 | 771 | let x: INumber = (-0x800000).into(); 772 | assert_eq!(x.to_i64(), Some(-0x800000)); 773 | assert_eq!(x.to_u64(), None); 774 | 775 | let x: INumber = 0x7FFFFF.into(); 776 | assert_eq!(x.to_i64(), Some(0x7FFFFF)); 777 | assert_eq!(x.to_u64(), Some(0x7FFFFF)); 778 | 779 | let x: INumber = (-0x7FFFFF).into(); 780 | assert_eq!(x.to_i64(), Some(-0x7FFFFF)); 781 | assert_eq!(x.to_u64(), None); 782 | } 783 | 784 | #[mockalloc::test] 785 | fn can_compare_various_numbers() { 786 | assert!(INumber::from(1) < INumber::try_from(1.5).unwrap()); 787 | assert!(INumber::from(2) > INumber::try_from(1.5).unwrap()); 788 | assert!(INumber::from(-2) < INumber::try_from(1.5).unwrap()); 789 | assert!(INumber::from(-2) < INumber::try_from(-1.5).unwrap()); 790 | assert!(INumber::from(-2) == INumber::try_from(-2.0).unwrap()); 791 | assert!(INumber::try_from(-1.5).unwrap() > INumber::from(-2)); 792 | assert!(INumber::try_from(1e30).unwrap() > INumber::from(u64::MAX)); 793 | assert!(INumber::try_from(1e30).unwrap() > INumber::from(i64::MAX)); 794 | assert!(INumber::try_from(-1e30).unwrap() < INumber::from(i64::MIN)); 795 | assert!(INumber::try_from(-1e30).unwrap() < INumber::from(i64::MIN)); 796 | assert!(INumber::try_from(99_999_999_000.0).unwrap() < INumber::from(99_999_999_001_u64)); 797 | } 798 | } 799 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | use serde::ser::{ 2 | Error as _, Impossible, SerializeMap, SerializeSeq, SerializeStruct, SerializeStructVariant, 3 | SerializeTuple, SerializeTupleStruct, SerializeTupleVariant, 4 | }; 5 | use serde::{Serialize, Serializer}; 6 | use serde_json::error::Error; 7 | 8 | use super::array::IArray; 9 | use super::number::INumber; 10 | use super::object::IObject; 11 | use super::string::IString; 12 | use super::value::{DestructuredRef, IValue}; 13 | 14 | impl Serialize for IValue { 15 | #[inline] 16 | fn serialize(&self, serializer: S) -> Result 17 | where 18 | S: Serializer, 19 | { 20 | match self.destructure_ref() { 21 | DestructuredRef::Null => serializer.serialize_unit(), 22 | DestructuredRef::Bool(b) => serializer.serialize_bool(b), 23 | DestructuredRef::Number(n) => n.serialize(serializer), 24 | DestructuredRef::String(s) => s.serialize(serializer), 25 | DestructuredRef::Array(v) => v.serialize(serializer), 26 | DestructuredRef::Object(o) => o.serialize(serializer), 27 | } 28 | } 29 | } 30 | 31 | impl Serialize for INumber { 32 | fn serialize(&self, serializer: S) -> Result 33 | where 34 | S: Serializer, 35 | { 36 | if self.has_decimal_point() { 37 | serializer.serialize_f64(self.to_f64().unwrap()) 38 | } else if let Some(v) = self.to_i64() { 39 | serializer.serialize_i64(v) 40 | } else { 41 | serializer.serialize_u64(self.to_u64().unwrap()) 42 | } 43 | } 44 | } 45 | 46 | impl Serialize for IString { 47 | fn serialize(&self, serializer: S) -> Result 48 | where 49 | S: Serializer, 50 | { 51 | serializer.serialize_str(self.as_str()) 52 | } 53 | } 54 | 55 | impl Serialize for IArray { 56 | fn serialize(&self, serializer: S) -> Result 57 | where 58 | S: Serializer, 59 | { 60 | let mut s = serializer.serialize_seq(Some(self.len()))?; 61 | for v in self { 62 | s.serialize_element(v)?; 63 | } 64 | s.end() 65 | } 66 | } 67 | 68 | impl Serialize for IObject { 69 | fn serialize(&self, serializer: S) -> Result 70 | where 71 | S: Serializer, 72 | { 73 | let mut m = serializer.serialize_map(Some(self.len()))?; 74 | for (k, v) in self { 75 | m.serialize_entry(k, v)?; 76 | } 77 | m.end() 78 | } 79 | } 80 | 81 | pub struct ValueSerializer; 82 | 83 | impl Serializer for ValueSerializer { 84 | type Ok = IValue; 85 | type Error = Error; 86 | 87 | type SerializeSeq = SerializeArray; 88 | type SerializeTuple = SerializeArray; 89 | type SerializeTupleStruct = SerializeArray; 90 | type SerializeTupleVariant = SerializeArrayVariant; 91 | type SerializeMap = SerializeObject; 92 | type SerializeStruct = SerializeObject; 93 | type SerializeStructVariant = SerializeObjectVariant; 94 | 95 | #[inline] 96 | fn serialize_bool(self, value: bool) -> Result { 97 | Ok(value.into()) 98 | } 99 | 100 | #[inline] 101 | fn serialize_i8(self, value: i8) -> Result { 102 | Ok(value.into()) 103 | } 104 | 105 | #[inline] 106 | fn serialize_i16(self, value: i16) -> Result { 107 | Ok(value.into()) 108 | } 109 | 110 | #[inline] 111 | fn serialize_i32(self, value: i32) -> Result { 112 | Ok(value.into()) 113 | } 114 | 115 | fn serialize_i64(self, value: i64) -> Result { 116 | Ok(value.into()) 117 | } 118 | 119 | #[inline] 120 | fn serialize_u8(self, value: u8) -> Result { 121 | Ok(value.into()) 122 | } 123 | 124 | #[inline] 125 | fn serialize_u16(self, value: u16) -> Result { 126 | Ok(value.into()) 127 | } 128 | 129 | #[inline] 130 | fn serialize_u32(self, value: u32) -> Result { 131 | Ok(value.into()) 132 | } 133 | 134 | #[inline] 135 | fn serialize_u64(self, value: u64) -> Result { 136 | Ok(value.into()) 137 | } 138 | 139 | #[inline] 140 | fn serialize_f32(self, value: f32) -> Result { 141 | Ok(value.into()) 142 | } 143 | 144 | #[inline] 145 | fn serialize_f64(self, value: f64) -> Result { 146 | Ok(value.into()) 147 | } 148 | 149 | #[inline] 150 | fn serialize_char(self, value: char) -> Result { 151 | let mut buffer = [0_u8; 4]; 152 | Ok(value.encode_utf8(&mut buffer).into()) 153 | } 154 | 155 | #[inline] 156 | fn serialize_str(self, value: &str) -> Result { 157 | Ok(value.into()) 158 | } 159 | 160 | fn serialize_bytes(self, value: &[u8]) -> Result { 161 | let array: IArray = value.iter().copied().collect(); 162 | Ok(array.into()) 163 | } 164 | 165 | #[inline] 166 | fn serialize_unit(self) -> Result { 167 | Ok(IValue::NULL) 168 | } 169 | 170 | #[inline] 171 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 172 | self.serialize_unit() 173 | } 174 | 175 | #[inline] 176 | fn serialize_unit_variant( 177 | self, 178 | _name: &'static str, 179 | _variant_index: u32, 180 | variant: &'static str, 181 | ) -> Result { 182 | self.serialize_str(variant) 183 | } 184 | 185 | #[inline] 186 | fn serialize_newtype_struct( 187 | self, 188 | _name: &'static str, 189 | value: &T, 190 | ) -> Result 191 | where 192 | T: ?Sized + Serialize, 193 | { 194 | value.serialize(self) 195 | } 196 | 197 | fn serialize_newtype_variant( 198 | self, 199 | _name: &'static str, 200 | _variant_index: u32, 201 | variant: &'static str, 202 | value: &T, 203 | ) -> Result 204 | where 205 | T: ?Sized + Serialize, 206 | { 207 | let mut obj = IObject::new(); 208 | obj.insert(variant, value.serialize(self)?); 209 | Ok(obj.into()) 210 | } 211 | 212 | #[inline] 213 | fn serialize_none(self) -> Result { 214 | self.serialize_unit() 215 | } 216 | 217 | #[inline] 218 | fn serialize_some(self, value: &T) -> Result 219 | where 220 | T: ?Sized + Serialize, 221 | { 222 | value.serialize(self) 223 | } 224 | 225 | fn serialize_seq(self, len: Option) -> Result { 226 | Ok(SerializeArray { 227 | array: IArray::with_capacity(len.unwrap_or(0)), 228 | }) 229 | } 230 | 231 | fn serialize_tuple(self, len: usize) -> Result { 232 | self.serialize_seq(Some(len)) 233 | } 234 | 235 | fn serialize_tuple_struct( 236 | self, 237 | _name: &'static str, 238 | len: usize, 239 | ) -> Result { 240 | self.serialize_seq(Some(len)) 241 | } 242 | 243 | fn serialize_tuple_variant( 244 | self, 245 | _name: &'static str, 246 | _variant_index: u32, 247 | variant: &'static str, 248 | len: usize, 249 | ) -> Result { 250 | Ok(SerializeArrayVariant { 251 | name: variant.into(), 252 | array: IArray::with_capacity(len), 253 | }) 254 | } 255 | 256 | fn serialize_map(self, len: Option) -> Result { 257 | Ok(SerializeObject { 258 | object: IObject::with_capacity(len.unwrap_or(0)), 259 | next_key: None, 260 | }) 261 | } 262 | 263 | fn serialize_struct( 264 | self, 265 | _name: &'static str, 266 | len: usize, 267 | ) -> Result { 268 | self.serialize_map(Some(len)) 269 | } 270 | 271 | fn serialize_struct_variant( 272 | self, 273 | _name: &'static str, 274 | _variant_index: u32, 275 | variant: &'static str, 276 | len: usize, 277 | ) -> Result { 278 | Ok(SerializeObjectVariant { 279 | name: variant.into(), 280 | object: IObject::with_capacity(len), 281 | }) 282 | } 283 | } 284 | 285 | pub struct SerializeArray { 286 | array: IArray, 287 | } 288 | 289 | pub struct SerializeArrayVariant { 290 | name: IString, 291 | array: IArray, 292 | } 293 | 294 | pub struct SerializeObject { 295 | object: IObject, 296 | next_key: Option, 297 | } 298 | 299 | pub struct SerializeObjectVariant { 300 | name: IString, 301 | object: IObject, 302 | } 303 | 304 | impl SerializeSeq for SerializeArray { 305 | type Ok = IValue; 306 | type Error = Error; 307 | 308 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 309 | where 310 | T: ?Sized + Serialize, 311 | { 312 | self.array.push(value.serialize(ValueSerializer)?); 313 | Ok(()) 314 | } 315 | 316 | fn end(self) -> Result { 317 | Ok(self.array.into()) 318 | } 319 | } 320 | 321 | impl SerializeTuple for SerializeArray { 322 | type Ok = IValue; 323 | type Error = Error; 324 | 325 | fn serialize_element(&mut self, value: &T) -> Result<(), Self::Error> 326 | where 327 | T: ?Sized + Serialize, 328 | { 329 | SerializeSeq::serialize_element(self, value) 330 | } 331 | 332 | fn end(self) -> Result { 333 | SerializeSeq::end(self) 334 | } 335 | } 336 | 337 | impl SerializeTupleStruct for SerializeArray { 338 | type Ok = IValue; 339 | type Error = Error; 340 | 341 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 342 | where 343 | T: ?Sized + Serialize, 344 | { 345 | SerializeSeq::serialize_element(self, value) 346 | } 347 | 348 | fn end(self) -> Result { 349 | SerializeSeq::end(self) 350 | } 351 | } 352 | 353 | impl SerializeTupleVariant for SerializeArrayVariant { 354 | type Ok = IValue; 355 | type Error = Error; 356 | 357 | fn serialize_field(&mut self, value: &T) -> Result<(), Self::Error> 358 | where 359 | T: ?Sized + Serialize, 360 | { 361 | self.array.push(value.serialize(ValueSerializer)?); 362 | Ok(()) 363 | } 364 | 365 | fn end(self) -> Result { 366 | let mut object = IObject::new(); 367 | object.insert(self.name, self.array); 368 | 369 | Ok(object.into()) 370 | } 371 | } 372 | 373 | impl SerializeMap for SerializeObject { 374 | type Ok = IValue; 375 | type Error = Error; 376 | 377 | fn serialize_key(&mut self, key: &T) -> Result<(), Self::Error> 378 | where 379 | T: ?Sized + Serialize, 380 | { 381 | self.next_key = Some(key.serialize(ObjectKeySerializer)?); 382 | Ok(()) 383 | } 384 | 385 | fn serialize_value(&mut self, value: &T) -> Result<(), Self::Error> 386 | where 387 | T: ?Sized + Serialize, 388 | { 389 | // Panic because this indicates a bug in the program rather than an 390 | // expected failure. 391 | let key = self 392 | .next_key 393 | .take() 394 | .expect("serialize_value called before serialize_key"); 395 | self.object.insert(key, value.serialize(ValueSerializer)?); 396 | Ok(()) 397 | } 398 | 399 | fn end(self) -> Result { 400 | Ok(self.object.into()) 401 | } 402 | } 403 | 404 | struct ObjectKeySerializer; 405 | 406 | fn key_must_be_a_string() -> Error { 407 | Error::custom("Object key must be a string") 408 | } 409 | 410 | impl Serializer for ObjectKeySerializer { 411 | type Ok = IString; 412 | type Error = Error; 413 | 414 | type SerializeSeq = Impossible; 415 | type SerializeTuple = Impossible; 416 | type SerializeTupleStruct = Impossible; 417 | type SerializeTupleVariant = Impossible; 418 | type SerializeMap = Impossible; 419 | type SerializeStruct = Impossible; 420 | type SerializeStructVariant = Impossible; 421 | 422 | #[inline] 423 | fn serialize_unit_variant( 424 | self, 425 | _name: &'static str, 426 | _variant_index: u32, 427 | variant: &'static str, 428 | ) -> Result { 429 | Ok(variant.into()) 430 | } 431 | 432 | #[inline] 433 | fn serialize_newtype_struct( 434 | self, 435 | _name: &'static str, 436 | value: &T, 437 | ) -> Result 438 | where 439 | T: ?Sized + Serialize, 440 | { 441 | value.serialize(self) 442 | } 443 | 444 | fn serialize_bool(self, _value: bool) -> Result { 445 | Err(key_must_be_a_string()) 446 | } 447 | 448 | fn serialize_i8(self, value: i8) -> Result { 449 | Ok(value.to_string().into()) 450 | } 451 | 452 | fn serialize_i16(self, value: i16) -> Result { 453 | Ok(value.to_string().into()) 454 | } 455 | 456 | fn serialize_i32(self, value: i32) -> Result { 457 | Ok(value.to_string().into()) 458 | } 459 | 460 | fn serialize_i64(self, value: i64) -> Result { 461 | Ok(value.to_string().into()) 462 | } 463 | 464 | fn serialize_u8(self, value: u8) -> Result { 465 | Ok(value.to_string().into()) 466 | } 467 | 468 | fn serialize_u16(self, value: u16) -> Result { 469 | Ok(value.to_string().into()) 470 | } 471 | 472 | fn serialize_u32(self, value: u32) -> Result { 473 | Ok(value.to_string().into()) 474 | } 475 | 476 | fn serialize_u64(self, value: u64) -> Result { 477 | Ok(value.to_string().into()) 478 | } 479 | 480 | fn serialize_f32(self, _value: f32) -> Result { 481 | Err(key_must_be_a_string()) 482 | } 483 | 484 | fn serialize_f64(self, _value: f64) -> Result { 485 | Err(key_must_be_a_string()) 486 | } 487 | 488 | #[inline] 489 | fn serialize_char(self, value: char) -> Result { 490 | let mut buffer = [0_u8; 4]; 491 | Ok(value.encode_utf8(&mut buffer).into()) 492 | } 493 | 494 | #[inline] 495 | fn serialize_str(self, value: &str) -> Result { 496 | Ok(value.into()) 497 | } 498 | 499 | fn serialize_bytes(self, _value: &[u8]) -> Result { 500 | Err(key_must_be_a_string()) 501 | } 502 | 503 | fn serialize_unit(self) -> Result { 504 | Err(key_must_be_a_string()) 505 | } 506 | 507 | fn serialize_unit_struct(self, _name: &'static str) -> Result { 508 | Err(key_must_be_a_string()) 509 | } 510 | 511 | fn serialize_newtype_variant( 512 | self, 513 | _name: &'static str, 514 | _variant_index: u32, 515 | _variant: &'static str, 516 | _value: &T, 517 | ) -> Result 518 | where 519 | T: ?Sized + Serialize, 520 | { 521 | Err(key_must_be_a_string()) 522 | } 523 | 524 | fn serialize_none(self) -> Result { 525 | Err(key_must_be_a_string()) 526 | } 527 | 528 | fn serialize_some(self, _value: &T) -> Result 529 | where 530 | T: ?Sized + Serialize, 531 | { 532 | Err(key_must_be_a_string()) 533 | } 534 | 535 | fn serialize_seq(self, _len: Option) -> Result { 536 | Err(key_must_be_a_string()) 537 | } 538 | 539 | fn serialize_tuple(self, _len: usize) -> Result { 540 | Err(key_must_be_a_string()) 541 | } 542 | 543 | fn serialize_tuple_struct( 544 | self, 545 | _name: &'static str, 546 | _len: usize, 547 | ) -> Result { 548 | Err(key_must_be_a_string()) 549 | } 550 | 551 | fn serialize_tuple_variant( 552 | self, 553 | _name: &'static str, 554 | _variant_index: u32, 555 | _variant: &'static str, 556 | _len: usize, 557 | ) -> Result { 558 | Err(key_must_be_a_string()) 559 | } 560 | 561 | fn serialize_map(self, _len: Option) -> Result { 562 | Err(key_must_be_a_string()) 563 | } 564 | 565 | fn serialize_struct( 566 | self, 567 | _name: &'static str, 568 | _len: usize, 569 | ) -> Result { 570 | Err(key_must_be_a_string()) 571 | } 572 | 573 | fn serialize_struct_variant( 574 | self, 575 | _name: &'static str, 576 | _variant_index: u32, 577 | _variant: &'static str, 578 | _len: usize, 579 | ) -> Result { 580 | Err(key_must_be_a_string()) 581 | } 582 | } 583 | 584 | impl SerializeStruct for SerializeObject { 585 | type Ok = IValue; 586 | type Error = Error; 587 | 588 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 589 | where 590 | T: ?Sized + Serialize, 591 | { 592 | SerializeMap::serialize_entry(self, key, value) 593 | } 594 | 595 | fn end(self) -> Result { 596 | SerializeMap::end(self) 597 | } 598 | } 599 | 600 | impl SerializeStructVariant for SerializeObjectVariant { 601 | type Ok = IValue; 602 | type Error = Error; 603 | 604 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error> 605 | where 606 | T: ?Sized + Serialize, 607 | { 608 | self.object.insert(key, value.serialize(ValueSerializer)?); 609 | Ok(()) 610 | } 611 | 612 | fn end(self) -> Result { 613 | let mut object = IObject::new(); 614 | object.insert(self.name, self.object); 615 | Ok(object.into()) 616 | } 617 | } 618 | 619 | /// Converts an arbitrary type to an [`IValue`] using that type's [`serde::Serialize`] 620 | /// implementation. 621 | /// # Errors 622 | /// 623 | /// Will return `Error` if `value` fails to serialize. 624 | pub fn to_value(value: T) -> Result 625 | where 626 | T: Serialize, 627 | { 628 | value.serialize(ValueSerializer) 629 | } 630 | -------------------------------------------------------------------------------- /src/string.rs: -------------------------------------------------------------------------------- 1 | //! Functionality relating to the JSON string type 2 | 3 | use std::alloc::{alloc, dealloc, Layout, LayoutError}; 4 | use std::borrow::Borrow; 5 | use std::cmp::Ordering; 6 | use std::fmt::{self, Debug, Formatter}; 7 | use std::hash::Hash; 8 | use std::ops::Deref; 9 | use std::ptr::{copy_nonoverlapping, NonNull}; 10 | use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering}; 11 | 12 | use dashmap::{DashSet, SharedValue}; 13 | use lazy_static::lazy_static; 14 | 15 | use crate::thin::{ThinMut, ThinMutExt, ThinRef, ThinRefExt}; 16 | 17 | use super::value::{IValue, TypeTag}; 18 | 19 | #[repr(C)] 20 | #[repr(align(4))] 21 | struct Header { 22 | rc: AtomicUsize, 23 | // We use 48 bits for the length and 16 bits for the shard index. 24 | len_lower: u32, 25 | len_upper: u16, 26 | shard_index: u16, 27 | } 28 | 29 | trait HeaderRef<'a>: ThinRefExt<'a, Header> { 30 | fn len(&self) -> usize { 31 | (u64::from(self.len_lower) | (u64::from(self.len_upper) << 32)) as usize 32 | } 33 | fn shard_index(&self) -> usize { 34 | self.shard_index as usize 35 | } 36 | fn str_ptr(&self) -> *const u8 { 37 | // Safety: pointers to the end of structs are allowed 38 | unsafe { self.ptr().add(1).cast() } 39 | } 40 | fn bytes(&self) -> &'a [u8] { 41 | // Safety: Header `len` must be accurate 42 | unsafe { std::slice::from_raw_parts(self.str_ptr(), self.len()) } 43 | } 44 | fn str(&self) -> &'a str { 45 | // Safety: UTF-8 enforced on construction 46 | unsafe { std::str::from_utf8_unchecked(self.bytes()) } 47 | } 48 | } 49 | 50 | trait HeaderMut<'a>: ThinMutExt<'a, Header> { 51 | fn str_ptr_mut(mut self) -> *mut u8 { 52 | // Safety: pointers to the end of structs are allowed 53 | unsafe { self.ptr_mut().add(1).cast() } 54 | } 55 | } 56 | 57 | impl<'a, T: ThinRefExt<'a, Header>> HeaderRef<'a> for T {} 58 | impl<'a, T: ThinMutExt<'a, Header>> HeaderMut<'a> for T {} 59 | 60 | lazy_static! { 61 | static ref STRING_CACHE: DashSet = DashSet::new(); 62 | } 63 | 64 | // Eagerly initialize the string cache during tests or when the 65 | // `ctor` feature is enabled. 66 | #[cfg(any(test, feature = "ctor"))] 67 | #[ctor::ctor] 68 | fn ctor_init_cache() { 69 | lazy_static::initialize(&STRING_CACHE); 70 | } 71 | 72 | #[doc(hidden)] 73 | pub fn init_cache() { 74 | lazy_static::initialize(&STRING_CACHE); 75 | } 76 | 77 | struct WeakIString { 78 | ptr: NonNull
, 79 | } 80 | 81 | unsafe impl Send for WeakIString {} 82 | unsafe impl Sync for WeakIString {} 83 | impl PartialEq for WeakIString { 84 | fn eq(&self, other: &Self) -> bool { 85 | **self == **other 86 | } 87 | } 88 | impl Eq for WeakIString {} 89 | impl Hash for WeakIString { 90 | fn hash(&self, state: &mut H) { 91 | (**self).hash(state); 92 | } 93 | } 94 | 95 | impl Deref for WeakIString { 96 | type Target = str; 97 | fn deref(&self) -> &str { 98 | self.borrow() 99 | } 100 | } 101 | 102 | impl Borrow for WeakIString { 103 | fn borrow(&self) -> &str { 104 | self.header().str() 105 | } 106 | } 107 | impl WeakIString { 108 | fn header(&self) -> ThinRef
{ 109 | // Safety: pointer is always valid 110 | unsafe { ThinRef::new(self.ptr.as_ptr()) } 111 | } 112 | fn upgrade(&self) -> IString { 113 | unsafe { 114 | self.ptr.as_ref().rc.fetch_add(1, AtomicOrdering::Relaxed); 115 | IString(IValue::new_ptr( 116 | self.ptr.as_ptr().cast::(), 117 | TypeTag::StringOrNull, 118 | )) 119 | } 120 | } 121 | } 122 | 123 | /// The `IString` type is an interned, immutable string, and is where this crate 124 | /// gets its name. 125 | /// 126 | /// Cloning an `IString` is cheap, and it can be easily converted from `&str` or 127 | /// `String` types. Comparisons between `IString`s is a simple pointer 128 | /// comparison. 129 | /// 130 | /// The memory backing an `IString` is reference counted, so that unlike many 131 | /// string interning libraries, memory is not leaked as new strings are interned. 132 | /// Interning uses `DashSet`, an implementation of a concurrent hash-set, allowing 133 | /// many strings to be interned concurrently without becoming a bottleneck. 134 | /// 135 | /// Given the nature of `IString` it is better to intern a string once and reuse 136 | /// it, rather than continually convert from `&str` to `IString`. 137 | #[repr(transparent)] 138 | #[derive(Clone)] 139 | pub struct IString(pub(crate) IValue); 140 | 141 | value_subtype_impls!(IString, into_string, as_string, as_string_mut); 142 | 143 | static EMPTY_HEADER: Header = Header { 144 | len_lower: 0, 145 | len_upper: 0, 146 | shard_index: 0, 147 | rc: AtomicUsize::new(0), 148 | }; 149 | 150 | impl IString { 151 | fn layout(len: usize) -> Result { 152 | Ok(Layout::new::
() 153 | .extend(Layout::array::(len)?)? 154 | .0 155 | .pad_to_align()) 156 | } 157 | 158 | fn alloc(s: &str, shard_index: usize) -> *mut Header { 159 | assert!((s.len() as u64) < (1 << 48)); 160 | assert!(shard_index < (1 << 16)); 161 | unsafe { 162 | let ptr = alloc(Self::layout(s.len()).unwrap()).cast::
(); 163 | ptr.write(Header { 164 | len_lower: s.len() as u32, 165 | len_upper: ((s.len() as u64) >> 32) as u16, 166 | shard_index: shard_index as u16, 167 | rc: AtomicUsize::new(0), 168 | }); 169 | let hd = ThinMut::new(ptr); 170 | copy_nonoverlapping(s.as_ptr(), hd.str_ptr_mut(), s.len()); 171 | ptr 172 | } 173 | } 174 | 175 | fn dealloc(ptr: *mut Header) { 176 | unsafe { 177 | let hd = ThinRef::new(ptr); 178 | let layout = Self::layout(hd.len()).unwrap(); 179 | dealloc(ptr.cast::(), layout); 180 | } 181 | } 182 | 183 | /// Converts a `&str` to an `IString` by interning it in the global string cache. 184 | #[must_use] 185 | pub fn intern(s: &str) -> Self { 186 | if s.is_empty() { 187 | return Self::new(); 188 | } 189 | let cache = &*STRING_CACHE; 190 | let shard_index = cache.determine_map(s); 191 | 192 | // Safety: `determine_map` should only return valid shard indices 193 | let shard = unsafe { cache.shards().get_unchecked(shard_index) }; 194 | let mut guard = shard.write(); 195 | if let Some((k, _)) = guard.get_key_value(s) { 196 | k.upgrade() 197 | } else { 198 | let k = unsafe { 199 | WeakIString { 200 | ptr: NonNull::new_unchecked(Self::alloc(s, shard_index)), 201 | } 202 | }; 203 | let res = k.upgrade(); 204 | guard.insert(k, SharedValue::new(())); 205 | res 206 | } 207 | } 208 | 209 | fn header(&self) -> ThinRef
{ 210 | unsafe { ThinRef::new(self.0.ptr().cast()) } 211 | } 212 | 213 | /// Returns the length (in bytes) of this string. 214 | #[must_use] 215 | pub fn len(&self) -> usize { 216 | self.header().len() 217 | } 218 | 219 | /// Returns `true` if this is the empty string "". 220 | #[must_use] 221 | pub fn is_empty(&self) -> bool { 222 | self.len() == 0 223 | } 224 | 225 | /// Obtains a `&str` from this `IString`. This is a cheap operation. 226 | #[must_use] 227 | pub fn as_str(&self) -> &str { 228 | self.header().str() 229 | } 230 | 231 | /// Obtains a byte slice from this `IString`. This is a cheap operation. 232 | #[must_use] 233 | pub fn as_bytes(&self) -> &[u8] { 234 | self.header().bytes() 235 | } 236 | 237 | /// Returns the empty string. 238 | #[must_use] 239 | pub fn new() -> Self { 240 | unsafe { IString(IValue::new_ref(&EMPTY_HEADER, TypeTag::StringOrNull)) } 241 | } 242 | 243 | pub(crate) fn clone_impl(&self) -> IValue { 244 | if self.is_empty() { 245 | Self::new().0 246 | } else { 247 | self.header().rc.fetch_add(1, AtomicOrdering::Relaxed); 248 | unsafe { self.0.raw_copy() } 249 | } 250 | } 251 | pub(crate) fn drop_impl(&mut self) { 252 | if !self.is_empty() { 253 | let hd = self.header(); 254 | 255 | // If the reference count is greater than 1, we can safely decrement it without 256 | // locking the string cache. 257 | let mut rc = hd.rc.load(AtomicOrdering::Relaxed); 258 | while rc > 1 { 259 | match hd.rc.compare_exchange_weak( 260 | rc, 261 | rc - 1, 262 | AtomicOrdering::Relaxed, 263 | AtomicOrdering::Relaxed, 264 | ) { 265 | Ok(_) => return, 266 | Err(new_rc) => rc = new_rc, 267 | } 268 | } 269 | 270 | // Slow path: we observed a reference count of 1, so we need to lock the string cache 271 | let cache = &*STRING_CACHE; 272 | // Safety: the number of shards is fixed 273 | let shard = unsafe { cache.shards().get_unchecked(hd.shard_index()) }; 274 | let mut guard = shard.write(); 275 | if hd.rc.fetch_sub(1, AtomicOrdering::Relaxed) == 1 { 276 | // Reference count reached zero, free the string 277 | assert!(guard.remove(hd.str()).is_some()); 278 | 279 | // Shrink the shard if it's mostly empty. 280 | // The second condition is necessary because `HashMap` sometimes 281 | // reports a capacity of zero even when it's still backed by an 282 | // allocation. 283 | if guard.len() * 3 < guard.capacity() || guard.is_empty() { 284 | guard.shrink_to_fit(); 285 | } 286 | drop(guard); 287 | 288 | Self::dealloc(unsafe { self.0.ptr().cast() }); 289 | } 290 | } 291 | } 292 | } 293 | 294 | impl Deref for IString { 295 | type Target = str; 296 | fn deref(&self) -> &str { 297 | self.as_str() 298 | } 299 | } 300 | 301 | impl Borrow for IString { 302 | fn borrow(&self) -> &str { 303 | self.as_str() 304 | } 305 | } 306 | 307 | impl From<&str> for IString { 308 | fn from(other: &str) -> Self { 309 | Self::intern(other) 310 | } 311 | } 312 | 313 | impl From<&mut str> for IString { 314 | fn from(other: &mut str) -> Self { 315 | Self::intern(other) 316 | } 317 | } 318 | 319 | impl From for IString { 320 | fn from(other: String) -> Self { 321 | Self::intern(other.as_str()) 322 | } 323 | } 324 | 325 | impl From<&String> for IString { 326 | fn from(other: &String) -> Self { 327 | Self::intern(other.as_str()) 328 | } 329 | } 330 | 331 | impl From<&mut String> for IString { 332 | fn from(other: &mut String) -> Self { 333 | Self::intern(other.as_str()) 334 | } 335 | } 336 | 337 | impl From for String { 338 | fn from(other: IString) -> Self { 339 | other.as_str().into() 340 | } 341 | } 342 | 343 | impl PartialEq for IString { 344 | fn eq(&self, other: &Self) -> bool { 345 | self.0.raw_eq(&other.0) 346 | } 347 | } 348 | 349 | impl PartialEq for IString { 350 | fn eq(&self, other: &str) -> bool { 351 | self.as_str() == other 352 | } 353 | } 354 | 355 | impl PartialEq for str { 356 | fn eq(&self, other: &IString) -> bool { 357 | self == other.as_str() 358 | } 359 | } 360 | 361 | impl PartialEq for IString { 362 | fn eq(&self, other: &String) -> bool { 363 | self.as_str() == other 364 | } 365 | } 366 | 367 | impl PartialEq for String { 368 | fn eq(&self, other: &IString) -> bool { 369 | self == other.as_str() 370 | } 371 | } 372 | 373 | impl Default for IString { 374 | fn default() -> Self { 375 | Self::new() 376 | } 377 | } 378 | 379 | impl Eq for IString {} 380 | impl Ord for IString { 381 | fn cmp(&self, other: &Self) -> Ordering { 382 | if self == other { 383 | Ordering::Equal 384 | } else { 385 | self.as_str().cmp(other.as_str()) 386 | } 387 | } 388 | } 389 | impl PartialOrd for IString { 390 | fn partial_cmp(&self, other: &Self) -> Option { 391 | Some(self.cmp(other)) 392 | } 393 | } 394 | impl Hash for IString { 395 | fn hash(&self, state: &mut H) { 396 | self.0.raw_hash(state); 397 | } 398 | } 399 | 400 | impl Debug for IString { 401 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 402 | Debug::fmt(self.as_str(), f) 403 | } 404 | } 405 | 406 | #[cfg(test)] 407 | mod tests { 408 | use super::*; 409 | 410 | #[mockalloc::test] 411 | fn can_intern() { 412 | let x = IString::intern("foo"); 413 | let y = IString::intern("bar"); 414 | let z = IString::intern("foo"); 415 | 416 | assert_eq!(x.as_ptr(), z.as_ptr()); 417 | assert_ne!(x.as_ptr(), y.as_ptr()); 418 | assert_eq!(x.as_str(), "foo"); 419 | assert_eq!(y.as_str(), "bar"); 420 | } 421 | 422 | #[mockalloc::test] 423 | fn default_interns_string() { 424 | let x = IString::intern(""); 425 | let y = IString::new(); 426 | let z = IString::intern("foo"); 427 | 428 | assert_eq!(x.as_ptr(), y.as_ptr()); 429 | assert_ne!(x.as_ptr(), z.as_ptr()); 430 | } 431 | } 432 | -------------------------------------------------------------------------------- /src/thin.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ops::{Deref, DerefMut}; 3 | use std::ptr::NonNull; 4 | 5 | #[repr(transparent)] 6 | pub struct ThinRef<'a, T> { 7 | ptr: NonNull, 8 | phantom: PhantomData<&'a T>, 9 | } 10 | 11 | impl ThinRef<'_, T> { 12 | pub unsafe fn new(ptr: *const T) -> Self { 13 | Self { 14 | ptr: NonNull::new_unchecked(ptr as *mut T), 15 | phantom: PhantomData, 16 | } 17 | } 18 | } 19 | 20 | impl Deref for ThinRef<'_, T> { 21 | type Target = T; 22 | 23 | fn deref(&self) -> &Self::Target { 24 | unsafe { &*self.ptr() } 25 | } 26 | } 27 | 28 | impl Copy for ThinRef<'_, T> {} 29 | impl Clone for ThinRef<'_, T> { 30 | fn clone(&self) -> Self { 31 | *self 32 | } 33 | } 34 | 35 | #[repr(transparent)] 36 | pub struct ThinMut<'a, T> { 37 | ptr: NonNull, 38 | phantom: PhantomData<&'a mut T>, 39 | } 40 | 41 | impl ThinMut<'_, T> { 42 | pub unsafe fn new(ptr: *mut T) -> Self { 43 | Self { 44 | ptr: NonNull::new_unchecked(ptr), 45 | phantom: PhantomData, 46 | } 47 | } 48 | } 49 | 50 | impl Deref for ThinMut<'_, T> { 51 | type Target = T; 52 | 53 | fn deref(&self) -> &Self::Target { 54 | // Safety: `ptr` must be valid 55 | unsafe { &*self.ptr() } 56 | } 57 | } 58 | 59 | impl DerefMut for ThinMut<'_, T> { 60 | fn deref_mut(&mut self) -> &mut Self::Target { 61 | // Safety: `ptr` must be valid 62 | unsafe { &mut *self.ptr_mut() } 63 | } 64 | } 65 | 66 | pub trait ThinRefExt<'a, T>: Deref { 67 | fn ptr(&self) -> *const T; 68 | } 69 | 70 | pub trait ThinMutExt<'a, T>: DerefMut + ThinRefExt<'a, T> + Sized { 71 | fn ptr_mut(&mut self) -> *mut T; 72 | fn reborrow(&mut self) -> ThinMut; 73 | } 74 | 75 | impl<'a, T> ThinRefExt<'a, T> for ThinRef<'a, T> { 76 | fn ptr(&self) -> *const T { 77 | self.ptr.as_ptr() 78 | } 79 | } 80 | 81 | impl<'a, T> ThinRefExt<'a, T> for ThinMut<'a, T> { 82 | fn ptr(&self) -> *const T { 83 | self.ptr.as_ptr() 84 | } 85 | } 86 | 87 | impl<'a, T> ThinMutExt<'a, T> for ThinMut<'a, T> { 88 | fn ptr_mut(&mut self) -> *mut T { 89 | self.ptr.as_ptr() 90 | } 91 | fn reborrow(&mut self) -> ThinMut { 92 | Self { 93 | ptr: self.ptr, 94 | phantom: self.phantom, 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | use std::collections::{BTreeMap, HashMap}; 3 | use std::convert::TryFrom; 4 | use std::fmt::{self, Debug, Formatter}; 5 | use std::hash::Hash; 6 | use std::hint::unreachable_unchecked; 7 | use std::mem; 8 | use std::ops::{Deref, Index, IndexMut}; 9 | use std::ptr::NonNull; 10 | 11 | #[cfg(feature = "indexmap")] 12 | use indexmap::IndexMap; 13 | 14 | use super::array::IArray; 15 | use super::number::INumber; 16 | use super::object::IObject; 17 | use super::string::IString; 18 | 19 | /// Stores an arbitrary JSON value. 20 | /// 21 | /// Compared to [`serde_json::Value`] this type is a struct rather than an enum, as 22 | /// this is necessary to achieve the important size reductions. This means that 23 | /// you cannot directly `match` on an `IValue` to determine its type. 24 | /// 25 | /// Instead, an `IValue` offers several ways to get at the inner type: 26 | /// 27 | /// - Destructuring using `IValue::destructure[{_ref,_mut}]()` 28 | /// 29 | /// These methods return wrapper enums which you _can_ directly match on, so 30 | /// these methods are the most direct replacement for matching on a `Value`. 31 | /// 32 | /// - Borrowing using `IValue::as_{array,object,string,number}[_mut]()` 33 | /// 34 | /// These methods return an `Option` of the corresponding reference if the 35 | /// type matches the one expected. These methods exist for the variants 36 | /// which are not `Copy`. 37 | /// 38 | /// - Converting using `IValue::into_{array,object,string,number}()` 39 | /// 40 | /// These methods return a `Result` of the corresponding type (or the 41 | /// original `IValue` if the type is not the one expected). These methods 42 | /// also exist for the variants which are not `Copy`. 43 | /// 44 | /// - Getting using `IValue::to_{bool,{i,u,f}{32,64}}[_lossy]}()` 45 | /// 46 | /// These methods return an `Option` of the corresponding type. These 47 | /// methods exist for types where the return value would be `Copy`. 48 | /// 49 | /// You can also check the type of the inner value without specifically 50 | /// accessing it using one of these methods: 51 | /// 52 | /// - Checking using `IValue::is_{null,bool,number,string,array,object,true,false}()` 53 | /// 54 | /// These methods exist for all types. 55 | /// 56 | /// - Getting the type with [`IValue::type_`] 57 | /// 58 | /// This method returns the [`ValueType`] enum, which has a variant for each of the 59 | /// six JSON types. 60 | #[repr(transparent)] 61 | pub struct IValue { 62 | ptr: NonNull, 63 | } 64 | 65 | /// Enum returned by [`IValue::destructure`] to allow matching on the type of 66 | /// an owned [`IValue`]. 67 | #[derive(Debug, Clone, PartialEq, Eq)] 68 | pub enum Destructured { 69 | /// Null. 70 | Null, 71 | /// Boolean. 72 | Bool(bool), 73 | /// Number. 74 | Number(INumber), 75 | /// String. 76 | String(IString), 77 | /// Array. 78 | Array(IArray), 79 | /// Object. 80 | Object(IObject), 81 | } 82 | 83 | impl Destructured { 84 | /// Convert to the borrowed form of thie enum. 85 | #[must_use] 86 | pub fn as_ref(&self) -> DestructuredRef { 87 | use DestructuredRef::{Array, Bool, Null, Number, Object, String}; 88 | match self { 89 | Self::Null => Null, 90 | Self::Bool(b) => Bool(*b), 91 | Self::Number(v) => Number(v), 92 | Self::String(v) => String(v), 93 | Self::Array(v) => Array(v), 94 | Self::Object(v) => Object(v), 95 | } 96 | } 97 | } 98 | 99 | /// Enum returned by [`IValue::destructure_ref`] to allow matching on the type of 100 | /// a reference to an [`IValue`]. 101 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] 102 | pub enum DestructuredRef<'a> { 103 | /// Null. 104 | Null, 105 | /// Boolean. 106 | /// [`IValue`]s do not directly contain booleans, so the value is returned 107 | /// directly instead of as a reference. 108 | Bool(bool), 109 | /// Number. 110 | Number(&'a INumber), 111 | /// String. 112 | String(&'a IString), 113 | /// Array. 114 | Array(&'a IArray), 115 | /// Object. 116 | Object(&'a IObject), 117 | } 118 | 119 | /// Enum returned by [`IValue::destructure_mut`] to allow matching on the type of 120 | /// a mutable reference to an [`IValue`]. 121 | #[derive(Debug)] 122 | pub enum DestructuredMut<'a> { 123 | /// Null. 124 | Null, 125 | /// Boolean. 126 | /// [`IValue`]s do not directly contain booleans, so this variant contains 127 | /// a proxy type which allows getting and setting the original [`IValue`] 128 | /// as a `bool`. 129 | Bool(BoolMut<'a>), 130 | /// Number. 131 | Number(&'a mut INumber), 132 | /// String. 133 | String(&'a mut IString), 134 | /// Array. 135 | Array(&'a mut IArray), 136 | /// Object. 137 | Object(&'a mut IObject), 138 | } 139 | 140 | /// A proxy type which imitates a `&mut bool`. 141 | #[derive(Debug)] 142 | pub struct BoolMut<'a>(&'a mut IValue); 143 | 144 | impl BoolMut<'_> { 145 | /// Set the [`IValue`] referenced by this proxy type to either 146 | /// `true` or `false`. 147 | pub fn set(&mut self, value: bool) { 148 | *self.0 = value.into(); 149 | } 150 | /// Get the boolean value stored in the [`IValue`] from which 151 | /// this proxy was obtained. 152 | #[must_use] 153 | pub fn get(&self) -> bool { 154 | self.0.is_true() 155 | } 156 | } 157 | 158 | impl Deref for BoolMut<'_> { 159 | type Target = bool; 160 | fn deref(&self) -> &bool { 161 | if self.get() { 162 | &true 163 | } else { 164 | &false 165 | } 166 | } 167 | } 168 | 169 | pub(crate) const ALIGNMENT: usize = 4; 170 | 171 | #[repr(usize)] 172 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] 173 | pub(crate) enum TypeTag { 174 | Number = 0, 175 | StringOrNull = 1, 176 | ArrayOrFalse = 2, 177 | ObjectOrTrue = 3, 178 | } 179 | 180 | impl From for TypeTag { 181 | fn from(other: usize) -> Self { 182 | // Safety: `% ALIGNMENT` can only return valid variants 183 | unsafe { mem::transmute(other % ALIGNMENT) } 184 | } 185 | } 186 | 187 | /// Enum which distinguishes the six JSON types. 188 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 189 | pub enum ValueType { 190 | // Stored inline 191 | /// Null. 192 | Null, 193 | /// Boolean. 194 | Bool, 195 | 196 | // Stored behind pointer 197 | /// Number. 198 | Number, 199 | /// String. 200 | String, 201 | /// Array. 202 | Array, 203 | /// Object. 204 | Object, 205 | } 206 | 207 | unsafe impl Send for IValue {} 208 | unsafe impl Sync for IValue {} 209 | 210 | impl IValue { 211 | // Safety: Tag must not be `Number` 212 | const unsafe fn new_inline(tag: TypeTag) -> Self { 213 | Self { 214 | ptr: NonNull::new_unchecked(tag as usize as *mut u8), 215 | } 216 | } 217 | // Safety: Pointer must be non-null and aligned to at least ALIGNMENT 218 | pub(crate) unsafe fn new_ptr(p: *mut u8, tag: TypeTag) -> Self { 219 | Self { 220 | ptr: NonNull::new_unchecked(p.add(tag as usize)), 221 | } 222 | } 223 | // Safety: Reference must be aligned to at least ALIGNMENT 224 | pub(crate) unsafe fn new_ref(r: &T, tag: TypeTag) -> Self { 225 | Self::new_ptr(r as *const _ as *mut u8, tag) 226 | } 227 | 228 | /// JSON `null`. 229 | pub const NULL: Self = unsafe { Self::new_inline(TypeTag::StringOrNull) }; 230 | /// JSON `false`. 231 | pub const FALSE: Self = unsafe { Self::new_inline(TypeTag::ArrayOrFalse) }; 232 | /// JSON `true`. 233 | pub const TRUE: Self = unsafe { Self::new_inline(TypeTag::ObjectOrTrue) }; 234 | 235 | pub(crate) fn ptr_usize(&self) -> usize { 236 | self.ptr.as_ptr() as usize 237 | } 238 | // Safety: Must only be called on non-inline types 239 | pub(crate) unsafe fn ptr(&self) -> *mut u8 { 240 | self.ptr 241 | .as_ptr() 242 | .wrapping_offset(-((self.ptr_usize() % ALIGNMENT) as isize)) 243 | } 244 | // Safety: Pointer must be non-null and aligned to at least ALIGNMENT 245 | pub(crate) unsafe fn set_ptr(&mut self, ptr: *mut u8) { 246 | let tag = self.type_tag(); 247 | self.ptr = NonNull::new_unchecked(ptr.add(tag as usize)); 248 | } 249 | // Safety: Reference must be aligned to at least ALIGNMENT 250 | pub(crate) unsafe fn set_ref(&mut self, r: &T) { 251 | self.set_ptr(r as *const T as *mut u8); 252 | } 253 | pub(crate) unsafe fn raw_copy(&self) -> Self { 254 | Self { ptr: self.ptr } 255 | } 256 | pub(crate) fn raw_eq(&self, other: &Self) -> bool { 257 | self.ptr == other.ptr 258 | } 259 | pub(crate) fn raw_hash(&self, state: &mut H) { 260 | self.ptr.hash(state); 261 | } 262 | fn is_ptr(&self) -> bool { 263 | self.ptr_usize() >= ALIGNMENT 264 | } 265 | fn type_tag(&self) -> TypeTag { 266 | self.ptr_usize().into() 267 | } 268 | 269 | /// Returns the type of this value. 270 | #[must_use] 271 | pub fn type_(&self) -> ValueType { 272 | match (self.type_tag(), self.is_ptr()) { 273 | // Pointers 274 | (TypeTag::Number, true) => ValueType::Number, 275 | (TypeTag::StringOrNull, true) => ValueType::String, 276 | (TypeTag::ArrayOrFalse, true) => ValueType::Array, 277 | (TypeTag::ObjectOrTrue, true) => ValueType::Object, 278 | 279 | // Non-pointers 280 | (TypeTag::StringOrNull, false) => ValueType::Null, 281 | (TypeTag::ArrayOrFalse, false) | (TypeTag::ObjectOrTrue, false) => ValueType::Bool, 282 | 283 | // Safety: due to invariants on IValue 284 | _ => unsafe { unreachable_unchecked() }, 285 | } 286 | } 287 | 288 | /// Destructures this value into an enum which can be `match`ed on. 289 | #[must_use] 290 | pub fn destructure(self) -> Destructured { 291 | match self.type_() { 292 | ValueType::Null => Destructured::Null, 293 | ValueType::Bool => Destructured::Bool(self.is_true()), 294 | ValueType::Number => Destructured::Number(INumber(self)), 295 | ValueType::String => Destructured::String(IString(self)), 296 | ValueType::Array => Destructured::Array(IArray(self)), 297 | ValueType::Object => Destructured::Object(IObject(self)), 298 | } 299 | } 300 | 301 | /// Destructures a reference to this value into an enum which can be `match`ed on. 302 | #[must_use] 303 | pub fn destructure_ref(&self) -> DestructuredRef { 304 | // Safety: we check the type 305 | unsafe { 306 | match self.type_() { 307 | ValueType::Null => DestructuredRef::Null, 308 | ValueType::Bool => DestructuredRef::Bool(self.is_true()), 309 | ValueType::Number => DestructuredRef::Number(self.as_number_unchecked()), 310 | ValueType::String => DestructuredRef::String(self.as_string_unchecked()), 311 | ValueType::Array => DestructuredRef::Array(self.as_array_unchecked()), 312 | ValueType::Object => DestructuredRef::Object(self.as_object_unchecked()), 313 | } 314 | } 315 | } 316 | 317 | /// Destructures a mutable reference to this value into an enum which can be `match`ed on. 318 | pub fn destructure_mut(&mut self) -> DestructuredMut { 319 | // Safety: we check the type 320 | unsafe { 321 | match self.type_() { 322 | ValueType::Null => DestructuredMut::Null, 323 | ValueType::Bool => DestructuredMut::Bool(BoolMut(self)), 324 | ValueType::Number => DestructuredMut::Number(self.as_number_unchecked_mut()), 325 | ValueType::String => DestructuredMut::String(self.as_string_unchecked_mut()), 326 | ValueType::Array => DestructuredMut::Array(self.as_array_unchecked_mut()), 327 | ValueType::Object => DestructuredMut::Object(self.as_object_unchecked_mut()), 328 | } 329 | } 330 | } 331 | 332 | /// Indexes into this value with a number or string. 333 | /// Panics if the value is not an array or object. 334 | /// Panics if attempting to index an array with a string. 335 | /// Panics if attempting to index an object with a number. 336 | /// Returns `None` if the index type is correct, but there is 337 | /// no value at this index. 338 | pub fn get(&self, index: impl ValueIndex) -> Option<&IValue> { 339 | index.index_into(self) 340 | } 341 | 342 | /// Mutably indexes into this value with a number or string. 343 | /// Panics if the value is not an array or object. 344 | /// Panics if attempting to index an array with a string. 345 | /// Panics if attempting to index an object with a number. 346 | /// Returns `None` if the index type is correct, but there is 347 | /// no value at this index. 348 | pub fn get_mut(&mut self, index: impl ValueIndex) -> Option<&mut IValue> { 349 | index.index_into_mut(self) 350 | } 351 | 352 | /// Removes a value at the specified numberic or string index. 353 | /// Panics if this is not an array or object. 354 | /// Panics if attempting to index an array with a string. 355 | /// Panics if attempting to index an object with a number. 356 | /// Returns `None` if the index type is correct, but there is 357 | /// no value at this index. 358 | pub fn remove(&mut self, index: impl ValueIndex) -> Option { 359 | index.remove(self) 360 | } 361 | 362 | /// Takes this value, replacing it with [`IValue::NULL`]. 363 | pub fn take(&mut self) -> IValue { 364 | mem::replace(self, IValue::NULL) 365 | } 366 | 367 | /// Returns the length of this value if it is an array or object. 368 | /// Returns `None` for other types. 369 | #[must_use] 370 | pub fn len(&self) -> Option { 371 | match self.type_() { 372 | // Safety: checked type 373 | ValueType::Array => Some(unsafe { self.as_array_unchecked().len() }), 374 | // Safety: checked type 375 | ValueType::Object => Some(unsafe { self.as_object_unchecked().len() }), 376 | _ => None, 377 | } 378 | } 379 | 380 | /// Returns whether this value is empty if it is an array or object. 381 | /// Returns `None` for other types. 382 | #[must_use] 383 | pub fn is_empty(&self) -> Option { 384 | match self.type_() { 385 | // Safety: checked type 386 | ValueType::Array => Some(unsafe { self.as_array_unchecked().is_empty() }), 387 | // Safety: checked type 388 | ValueType::Object => Some(unsafe { self.as_object_unchecked().is_empty() }), 389 | _ => None, 390 | } 391 | } 392 | 393 | // # Null methods 394 | /// Returns `true` if this is the `null` value. 395 | #[must_use] 396 | pub fn is_null(&self) -> bool { 397 | self.ptr == Self::NULL.ptr 398 | } 399 | 400 | // # Bool methods 401 | /// Returns `true` if this is a boolean. 402 | #[must_use] 403 | pub fn is_bool(&self) -> bool { 404 | self.ptr == Self::TRUE.ptr || self.ptr == Self::FALSE.ptr 405 | } 406 | 407 | /// Returns `true` if this is the `true` value. 408 | #[must_use] 409 | pub fn is_true(&self) -> bool { 410 | self.ptr == Self::TRUE.ptr 411 | } 412 | 413 | /// Returns `true` if this is the `false` value. 414 | #[must_use] 415 | pub fn is_false(&self) -> bool { 416 | self.ptr == Self::FALSE.ptr 417 | } 418 | 419 | /// Converts this value to a `bool`. 420 | /// Returns `None` if it's not a boolean. 421 | #[must_use] 422 | pub fn to_bool(&self) -> Option { 423 | if self.is_bool() { 424 | Some(self.is_true()) 425 | } else { 426 | None 427 | } 428 | } 429 | 430 | // # Number methods 431 | /// Returns `true` if this is a number. 432 | #[must_use] 433 | pub fn is_number(&self) -> bool { 434 | self.type_tag() == TypeTag::Number 435 | } 436 | 437 | unsafe fn unchecked_cast_ref(&self) -> &T { 438 | &*(self as *const Self).cast::() 439 | } 440 | 441 | unsafe fn unchecked_cast_mut(&mut self) -> &mut T { 442 | &mut *(self as *mut Self).cast::() 443 | } 444 | 445 | // Safety: Must be a string 446 | unsafe fn as_number_unchecked(&self) -> &INumber { 447 | self.unchecked_cast_ref() 448 | } 449 | 450 | // Safety: Must be a string 451 | unsafe fn as_number_unchecked_mut(&mut self) -> &mut INumber { 452 | self.unchecked_cast_mut() 453 | } 454 | 455 | /// Gets a reference to this value as an [`INumber`]. 456 | /// Returns `None` if it's not a number. 457 | #[must_use] 458 | pub fn as_number(&self) -> Option<&INumber> { 459 | if self.is_number() { 460 | // Safety: INumber is a `#[repr(transparent)]` wrapper around IValue 461 | Some(unsafe { self.as_number_unchecked() }) 462 | } else { 463 | None 464 | } 465 | } 466 | 467 | /// Gets a mutable reference to this value as an [`INumber`]. 468 | /// Returns `None` if it's not a number. 469 | pub fn as_number_mut(&mut self) -> Option<&mut INumber> { 470 | if self.is_number() { 471 | // Safety: INumber is a `#[repr(transparent)]` wrapper around IValue 472 | Some(unsafe { self.as_number_unchecked_mut() }) 473 | } else { 474 | None 475 | } 476 | } 477 | 478 | /// Converts this value to an [`INumber`]. 479 | /// 480 | /// # Errors 481 | /// 482 | /// Returns `Err(self)` if it's not a number. 483 | pub fn into_number(self) -> Result { 484 | if self.is_number() { 485 | Ok(INumber(self)) 486 | } else { 487 | Err(self) 488 | } 489 | } 490 | 491 | /// Converts this value to an i64 if it is a number that can be represented exactly. 492 | #[must_use] 493 | pub fn to_i64(&self) -> Option { 494 | self.as_number()?.to_i64() 495 | } 496 | /// Converts this value to a u64 if it is a number that can be represented exactly. 497 | #[must_use] 498 | pub fn to_u64(&self) -> Option { 499 | self.as_number()?.to_u64() 500 | } 501 | /// Converts this value to an f64 if it is a number that can be represented exactly. 502 | #[must_use] 503 | pub fn to_f64(&self) -> Option { 504 | self.as_number()?.to_f64() 505 | } 506 | /// Converts this value to an f32 if it is a number that can be represented exactly. 507 | #[must_use] 508 | pub fn to_f32(&self) -> Option { 509 | self.as_number()?.to_f32() 510 | } 511 | /// Converts this value to an i32 if it is a number that can be represented exactly. 512 | #[must_use] 513 | pub fn to_i32(&self) -> Option { 514 | self.as_number()?.to_i32() 515 | } 516 | /// Converts this value to a u32 if it is a number that can be represented exactly. 517 | #[must_use] 518 | pub fn to_u32(&self) -> Option { 519 | self.as_number()?.to_u32() 520 | } 521 | /// Converts this value to an isize if it is a number that can be represented exactly. 522 | #[must_use] 523 | pub fn to_isize(&self) -> Option { 524 | self.as_number()?.to_isize() 525 | } 526 | /// Converts this value to a usize if it is a number that can be represented exactly. 527 | #[must_use] 528 | pub fn to_usize(&self) -> Option { 529 | self.as_number()?.to_usize() 530 | } 531 | /// Converts this value to an f64 if it is a number, potentially losing precision 532 | /// in the process. 533 | #[must_use] 534 | pub fn to_f64_lossy(&self) -> Option { 535 | Some(self.as_number()?.to_f64_lossy()) 536 | } 537 | /// Converts this value to an f32 if it is a number, potentially losing precision 538 | /// in the process. 539 | #[must_use] 540 | pub fn to_f32_lossy(&self) -> Option { 541 | Some(self.as_number()?.to_f32_lossy()) 542 | } 543 | 544 | // # String methods 545 | /// Returns `true` if this is a string. 546 | #[must_use] 547 | pub fn is_string(&self) -> bool { 548 | self.type_tag() == TypeTag::StringOrNull && self.is_ptr() 549 | } 550 | 551 | // Safety: Must be a string 552 | unsafe fn as_string_unchecked(&self) -> &IString { 553 | self.unchecked_cast_ref() 554 | } 555 | 556 | // Safety: Must be a string 557 | unsafe fn as_string_unchecked_mut(&mut self) -> &mut IString { 558 | self.unchecked_cast_mut() 559 | } 560 | 561 | /// Gets a reference to this value as an [`IString`]. 562 | /// Returns `None` if it's not a string. 563 | #[must_use] 564 | pub fn as_string(&self) -> Option<&IString> { 565 | if self.is_string() { 566 | // Safety: IString is a `#[repr(transparent)]` wrapper around IValue 567 | Some(unsafe { self.as_string_unchecked() }) 568 | } else { 569 | None 570 | } 571 | } 572 | 573 | /// Gets a mutable reference to this value as an [`IString`]. 574 | /// Returns `None` if it's not a string. 575 | pub fn as_string_mut(&mut self) -> Option<&mut IString> { 576 | if self.is_string() { 577 | // Safety: IString is a `#[repr(transparent)]` wrapper around IValue 578 | Some(unsafe { self.as_string_unchecked_mut() }) 579 | } else { 580 | None 581 | } 582 | } 583 | 584 | /// Converts this value to an [`IString`]. 585 | /// 586 | /// # Errors 587 | /// 588 | /// Returns `Err(self)` if it's not a string. 589 | pub fn into_string(self) -> Result { 590 | if self.is_string() { 591 | Ok(IString(self)) 592 | } else { 593 | Err(self) 594 | } 595 | } 596 | 597 | // # Array methods 598 | /// Returns `true` if this is an array. 599 | #[must_use] 600 | pub fn is_array(&self) -> bool { 601 | self.type_tag() == TypeTag::ArrayOrFalse && self.is_ptr() 602 | } 603 | 604 | // Safety: Must be an array 605 | unsafe fn as_array_unchecked(&self) -> &IArray { 606 | self.unchecked_cast_ref() 607 | } 608 | 609 | // Safety: Must be an array 610 | unsafe fn as_array_unchecked_mut(&mut self) -> &mut IArray { 611 | self.unchecked_cast_mut() 612 | } 613 | 614 | /// Gets a reference to this value as an [`IArray`]. 615 | /// Returns `None` if it's not an array. 616 | #[must_use] 617 | pub fn as_array(&self) -> Option<&IArray> { 618 | if self.is_array() { 619 | // Safety: IArray is a `#[repr(transparent)]` wrapper around IValue 620 | Some(unsafe { self.as_array_unchecked() }) 621 | } else { 622 | None 623 | } 624 | } 625 | 626 | /// Gets a mutable reference to this value as an [`IArray`]. 627 | /// Returns `None` if it's not an array. 628 | pub fn as_array_mut(&mut self) -> Option<&mut IArray> { 629 | if self.is_array() { 630 | // Safety: IArray is a `#[repr(transparent)]` wrapper around IValue 631 | Some(unsafe { self.as_array_unchecked_mut() }) 632 | } else { 633 | None 634 | } 635 | } 636 | 637 | /// Converts this value to an [`IArray`]. 638 | /// 639 | /// # Errors 640 | /// 641 | /// Returns `Err(self)` if it's not an array. 642 | pub fn into_array(self) -> Result { 643 | if self.is_array() { 644 | Ok(IArray(self)) 645 | } else { 646 | Err(self) 647 | } 648 | } 649 | 650 | // # Object methods 651 | /// Returns `true` if this is an object. 652 | #[must_use] 653 | pub fn is_object(&self) -> bool { 654 | self.type_tag() == TypeTag::ObjectOrTrue && self.is_ptr() 655 | } 656 | 657 | // Safety: Must be an array 658 | unsafe fn as_object_unchecked(&self) -> &IObject { 659 | self.unchecked_cast_ref() 660 | } 661 | 662 | // Safety: Must be an array 663 | unsafe fn as_object_unchecked_mut(&mut self) -> &mut IObject { 664 | self.unchecked_cast_mut() 665 | } 666 | 667 | /// Gets a reference to this value as an [`IObject`]. 668 | /// Returns `None` if it's not an object. 669 | #[must_use] 670 | pub fn as_object(&self) -> Option<&IObject> { 671 | if self.is_object() { 672 | // Safety: IObject is a `#[repr(transparent)]` wrapper around IValue 673 | Some(unsafe { self.as_object_unchecked() }) 674 | } else { 675 | None 676 | } 677 | } 678 | 679 | /// Gets a mutable reference to this value as an [`IObject`]. 680 | /// Returns `None` if it's not an object. 681 | pub fn as_object_mut(&mut self) -> Option<&mut IObject> { 682 | if self.is_object() { 683 | // Safety: IObject is a `#[repr(transparent)]` wrapper around IValue 684 | Some(unsafe { self.as_object_unchecked_mut() }) 685 | } else { 686 | None 687 | } 688 | } 689 | 690 | /// Converts this value to an [`IObject`]. 691 | /// 692 | /// # Errors 693 | /// 694 | /// Returns `Err(self)` if it's not an object. 695 | pub fn into_object(self) -> Result { 696 | if self.is_object() { 697 | Ok(IObject(self)) 698 | } else { 699 | Err(self) 700 | } 701 | } 702 | } 703 | 704 | impl Clone for IValue { 705 | fn clone(&self) -> Self { 706 | match self.type_() { 707 | // Inline types can be trivially copied 708 | ValueType::Null | ValueType::Bool => Self { ptr: self.ptr }, 709 | // Safety: We checked the type 710 | ValueType::Array => unsafe { self.as_array_unchecked() }.clone_impl(), 711 | ValueType::Object => unsafe { self.as_object_unchecked() }.clone_impl(), 712 | ValueType::String => unsafe { self.as_string_unchecked() }.clone_impl(), 713 | ValueType::Number => unsafe { self.as_number_unchecked() }.clone_impl(), 714 | } 715 | } 716 | } 717 | 718 | impl Drop for IValue { 719 | fn drop(&mut self) { 720 | match self.type_() { 721 | // Inline types can be trivially dropped 722 | ValueType::Null | ValueType::Bool => {} 723 | // Safety: We checked the type 724 | ValueType::Array => unsafe { self.as_array_unchecked_mut() }.drop_impl(), 725 | ValueType::Object => unsafe { self.as_object_unchecked_mut() }.drop_impl(), 726 | ValueType::String => unsafe { self.as_string_unchecked_mut() }.drop_impl(), 727 | ValueType::Number => unsafe { self.as_number_unchecked_mut() }.drop_impl(), 728 | } 729 | } 730 | } 731 | 732 | impl Hash for IValue { 733 | fn hash(&self, state: &mut H) { 734 | match self.type_() { 735 | // Inline and interned types can be trivially hashed 736 | ValueType::Null | ValueType::Bool | ValueType::String => self.ptr.hash(state), 737 | // Safety: We checked the type 738 | ValueType::Array => unsafe { self.as_array_unchecked() }.hash(state), 739 | // Safety: We checked the type 740 | ValueType::Object => unsafe { self.as_object_unchecked() }.hash(state), 741 | // Safety: We checked the type 742 | ValueType::Number => unsafe { self.as_number_unchecked() }.hash(state), 743 | } 744 | } 745 | } 746 | 747 | impl PartialEq for IValue { 748 | fn eq(&self, other: &Self) -> bool { 749 | let (t1, t2) = (self.type_(), other.type_()); 750 | if t1 == t2 { 751 | // Safety: Only methods for the appropriate type are called 752 | unsafe { 753 | match t1 { 754 | // Inline and interned types can be trivially compared 755 | ValueType::Null | ValueType::Bool | ValueType::String => self.ptr == other.ptr, 756 | ValueType::Number => self.as_number_unchecked() == other.as_number_unchecked(), 757 | ValueType::Array => self.as_array_unchecked() == other.as_array_unchecked(), 758 | ValueType::Object => self.as_object_unchecked() == other.as_object_unchecked(), 759 | } 760 | } 761 | } else { 762 | false 763 | } 764 | } 765 | } 766 | 767 | impl Eq for IValue {} 768 | impl PartialOrd for IValue { 769 | fn partial_cmp(&self, other: &Self) -> Option { 770 | let (t1, t2) = (self.type_(), other.type_()); 771 | if t1 == t2 { 772 | // Safety: Only methods for the appropriate type are called 773 | unsafe { 774 | match t1 { 775 | // Inline and interned types can be trivially compared 776 | ValueType::Null => Some(Ordering::Equal), 777 | ValueType::Bool => self.is_true().partial_cmp(&other.is_true()), 778 | ValueType::String => self 779 | .as_string_unchecked() 780 | .partial_cmp(other.as_string_unchecked()), 781 | ValueType::Number => self 782 | .as_number_unchecked() 783 | .partial_cmp(other.as_number_unchecked()), 784 | ValueType::Array => self 785 | .as_array_unchecked() 786 | .partial_cmp(other.as_array_unchecked()), 787 | ValueType::Object => None, 788 | } 789 | } 790 | } else { 791 | t1.partial_cmp(&t2) 792 | } 793 | } 794 | } 795 | 796 | mod private { 797 | #[doc(hidden)] 798 | pub trait Sealed {} 799 | impl Sealed for usize {} 800 | impl Sealed for &str {} 801 | impl Sealed for &super::IString {} 802 | impl Sealed for &T {} 803 | } 804 | 805 | /// Trait which abstracts over the various number and string types 806 | /// which can be used to index into an [`IValue`]. 807 | pub trait ValueIndex: private::Sealed + Copy { 808 | #[doc(hidden)] 809 | fn index_into(self, v: &IValue) -> Option<&IValue>; 810 | 811 | #[doc(hidden)] 812 | fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue>; 813 | 814 | #[doc(hidden)] 815 | fn index_or_insert(self, v: &mut IValue) -> &mut IValue; 816 | 817 | #[doc(hidden)] 818 | fn remove(self, v: &mut IValue) -> Option; 819 | } 820 | 821 | impl ValueIndex for usize { 822 | fn index_into(self, v: &IValue) -> Option<&IValue> { 823 | v.as_array().unwrap().get(self) 824 | } 825 | 826 | fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> { 827 | v.as_array_mut().unwrap().get_mut(self) 828 | } 829 | 830 | fn index_or_insert(self, v: &mut IValue) -> &mut IValue { 831 | self.index_into_mut(v).unwrap() 832 | } 833 | 834 | fn remove(self, v: &mut IValue) -> Option { 835 | v.as_array_mut().unwrap().remove(self) 836 | } 837 | } 838 | 839 | impl ValueIndex for &str { 840 | fn index_into(self, v: &IValue) -> Option<&IValue> { 841 | v.as_object().unwrap().get(&IString::intern(self)) 842 | } 843 | 844 | fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> { 845 | v.as_object_mut().unwrap().get_mut(&IString::intern(self)) 846 | } 847 | 848 | fn index_or_insert(self, v: &mut IValue) -> &mut IValue { 849 | &mut v.as_object_mut().unwrap()[self] 850 | } 851 | 852 | fn remove(self, v: &mut IValue) -> Option { 853 | v.as_object_mut().unwrap().remove(self) 854 | } 855 | } 856 | 857 | impl ValueIndex for &IString { 858 | fn index_into(self, v: &IValue) -> Option<&IValue> { 859 | v.as_object().unwrap().get(self) 860 | } 861 | 862 | fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> { 863 | v.as_object_mut().unwrap().get_mut(self) 864 | } 865 | 866 | fn index_or_insert(self, v: &mut IValue) -> &mut IValue { 867 | &mut v.as_object_mut().unwrap()[self] 868 | } 869 | 870 | fn remove(self, v: &mut IValue) -> Option { 871 | v.as_object_mut().unwrap().remove(self) 872 | } 873 | } 874 | 875 | impl ValueIndex for &T { 876 | fn index_into(self, v: &IValue) -> Option<&IValue> { 877 | (*self).index_into(v) 878 | } 879 | 880 | fn index_into_mut(self, v: &mut IValue) -> Option<&mut IValue> { 881 | (*self).index_into_mut(v) 882 | } 883 | 884 | fn index_or_insert(self, v: &mut IValue) -> &mut IValue { 885 | (*self).index_or_insert(v) 886 | } 887 | 888 | fn remove(self, v: &mut IValue) -> Option { 889 | (*self).remove(v) 890 | } 891 | } 892 | 893 | impl Index for IValue { 894 | type Output = IValue; 895 | 896 | #[inline] 897 | fn index(&self, index: I) -> &IValue { 898 | index.index_into(self).unwrap() 899 | } 900 | } 901 | 902 | impl IndexMut for IValue { 903 | #[inline] 904 | fn index_mut(&mut self, index: I) -> &mut IValue { 905 | index.index_or_insert(self) 906 | } 907 | } 908 | 909 | impl Debug for IValue { 910 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 911 | unsafe { 912 | match self.type_() { 913 | // Inline and interned types can be trivially hashed 914 | ValueType::Null => f.write_str("null"), 915 | ValueType::Bool => Debug::fmt(&self.is_true(), f), 916 | // Safety: We checked the type 917 | ValueType::String => Debug::fmt(self.as_string_unchecked(), f), 918 | // Safety: We checked the type 919 | ValueType::Array => Debug::fmt(self.as_array_unchecked(), f), 920 | // Safety: We checked the type 921 | ValueType::Object => Debug::fmt(self.as_object_unchecked(), f), 922 | // Safety: We checked the type 923 | ValueType::Number => Debug::fmt(self.as_number_unchecked(), f), 924 | } 925 | } 926 | } 927 | } 928 | 929 | impl> From> for IValue { 930 | fn from(other: Option) -> Self { 931 | if let Some(v) = other { 932 | v.into() 933 | } else { 934 | Self::NULL 935 | } 936 | } 937 | } 938 | 939 | impl From for IValue { 940 | fn from(other: bool) -> Self { 941 | if other { 942 | Self::TRUE 943 | } else { 944 | Self::FALSE 945 | } 946 | } 947 | } 948 | 949 | typed_conversions! { 950 | INumber: i8, u8, i16, u16, i32, u32, i64, u64, isize, usize; 951 | IString: String, &String, &mut String, &str, &mut str; 952 | IArray: 953 | Vec where (T: Into), 954 | &[T] where (T: Into + Clone); 955 | IObject: 956 | HashMap where (K: Into, V: Into), 957 | BTreeMap where (K: Into, V: Into); 958 | } 959 | 960 | #[cfg(feature = "indexmap")] 961 | typed_conversions! { 962 | IObject: 963 | IndexMap where (K: Into, V: Into); 964 | } 965 | 966 | impl From for IValue { 967 | fn from(v: f32) -> Self { 968 | INumber::try_from(v).map(Into::into).unwrap_or(IValue::NULL) 969 | } 970 | } 971 | 972 | impl From for IValue { 973 | fn from(v: f64) -> Self { 974 | INumber::try_from(v).map(Into::into).unwrap_or(IValue::NULL) 975 | } 976 | } 977 | 978 | impl Default for IValue { 979 | fn default() -> Self { 980 | Self::NULL 981 | } 982 | } 983 | 984 | #[cfg(test)] 985 | mod tests { 986 | use super::*; 987 | 988 | #[mockalloc::test] 989 | fn can_use_literal() { 990 | let x: IValue = ijson!({ 991 | "foo": "bar", 992 | "x": [], 993 | "y": ["hi", "there", 1, 2, null, false, true, 63.5], 994 | "z": [false, { 995 | "a": null 996 | }, {}] 997 | }); 998 | let y: IValue = serde_json::from_str( 999 | r#"{ 1000 | "foo": "bar", 1001 | "x": [], 1002 | "y": ["hi", "there", 1, 2, null, false, true, 63.5], 1003 | "z": [false, { 1004 | "a": null 1005 | }, {}] 1006 | }"#, 1007 | ) 1008 | .unwrap(); 1009 | assert_eq!(x, y); 1010 | } 1011 | 1012 | #[test] 1013 | #[allow(clippy::redundant_clone)] 1014 | fn test_null() { 1015 | let x: IValue = IValue::NULL; 1016 | assert!(x.is_null()); 1017 | assert_eq!(x.type_(), ValueType::Null); 1018 | assert!(matches!(x.clone().destructure(), Destructured::Null)); 1019 | assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Null)); 1020 | assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Null)); 1021 | } 1022 | 1023 | #[test] 1024 | fn test_bool() { 1025 | for v in [true, false].iter().copied() { 1026 | let mut x = IValue::from(v); 1027 | assert!(x.is_bool()); 1028 | assert_eq!(x.type_(), ValueType::Bool); 1029 | assert_eq!(x.to_bool(), Some(v)); 1030 | assert!(matches!(x.clone().destructure(), Destructured::Bool(u) if u == v)); 1031 | assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Bool(u) if u == v)); 1032 | assert!( 1033 | matches!(x.clone().destructure_mut(), DestructuredMut::Bool(u) if u.get() == v) 1034 | ); 1035 | 1036 | if let DestructuredMut::Bool(mut b) = x.destructure_mut() { 1037 | b.set(!v); 1038 | } 1039 | 1040 | assert_eq!(x.to_bool(), Some(!v)); 1041 | } 1042 | } 1043 | 1044 | #[mockalloc::test] 1045 | fn test_number() { 1046 | for v in 300..400 { 1047 | let mut x = IValue::from(v); 1048 | assert!(x.is_number()); 1049 | assert_eq!(x.type_(), ValueType::Number); 1050 | assert_eq!(x.to_i32(), Some(v)); 1051 | assert_eq!(x.to_u32(), Some(v as u32)); 1052 | assert_eq!(x.to_i64(), Some(i64::from(v))); 1053 | assert_eq!(x.to_u64(), Some(v as u64)); 1054 | assert_eq!(x.to_isize(), Some(v as isize)); 1055 | assert_eq!(x.to_usize(), Some(v as usize)); 1056 | assert_eq!(x.as_number(), Some(&v.into())); 1057 | assert_eq!(x.as_number_mut(), Some(&mut v.into())); 1058 | assert!(matches!(x.clone().destructure(), Destructured::Number(u) if u == v.into())); 1059 | assert!( 1060 | matches!(x.clone().destructure_ref(), DestructuredRef::Number(u) if *u == v.into()) 1061 | ); 1062 | assert!( 1063 | matches!(x.clone().destructure_mut(), DestructuredMut::Number(u) if *u == v.into()) 1064 | ); 1065 | } 1066 | } 1067 | 1068 | #[mockalloc::test] 1069 | fn test_string() { 1070 | for v in 0..10 { 1071 | let s = v.to_string(); 1072 | let mut x = IValue::from(&s); 1073 | assert!(x.is_string()); 1074 | assert_eq!(x.type_(), ValueType::String); 1075 | assert_eq!(x.as_string(), Some(&IString::intern(&s))); 1076 | assert_eq!(x.as_string_mut(), Some(&mut IString::intern(&s))); 1077 | assert!(matches!(x.clone().destructure(), Destructured::String(u) if u == s)); 1078 | assert!(matches!(x.clone().destructure_ref(), DestructuredRef::String(u) if *u == s)); 1079 | assert!(matches!(x.clone().destructure_mut(), DestructuredMut::String(u) if *u == s)); 1080 | } 1081 | } 1082 | 1083 | #[mockalloc::test] 1084 | fn test_array() { 1085 | for v in 0..10 { 1086 | let mut a: IArray = (0..v).collect(); 1087 | let mut x = IValue::from(a.clone()); 1088 | assert!(x.is_array()); 1089 | assert_eq!(x.type_(), ValueType::Array); 1090 | assert_eq!(x.as_array(), Some(&a)); 1091 | assert_eq!(x.as_array_mut(), Some(&mut a)); 1092 | assert!(matches!(x.clone().destructure(), Destructured::Array(u) if u == a)); 1093 | assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Array(u) if *u == a)); 1094 | assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Array(u) if *u == a)); 1095 | } 1096 | } 1097 | 1098 | #[mockalloc::test] 1099 | fn test_object() { 1100 | for v in 0..10 { 1101 | let mut o: IObject = (0..v).map(|i| (i.to_string(), i)).collect(); 1102 | let mut x = IValue::from(o.clone()); 1103 | assert!(x.is_object()); 1104 | assert_eq!(x.type_(), ValueType::Object); 1105 | assert_eq!(x.as_object(), Some(&o)); 1106 | assert_eq!(x.as_object_mut(), Some(&mut o)); 1107 | assert!(matches!(x.clone().destructure(), Destructured::Object(u) if u == o)); 1108 | assert!(matches!(x.clone().destructure_ref(), DestructuredRef::Object(u) if *u == o)); 1109 | assert!(matches!(x.clone().destructure_mut(), DestructuredMut::Object(u) if *u == o)); 1110 | } 1111 | } 1112 | 1113 | #[mockalloc::test] 1114 | fn test_into_object_for_object() { 1115 | let o: IObject = (0..10).map(|i| (i.to_string(), i)).collect(); 1116 | let x = IValue::from(o.clone()); 1117 | 1118 | assert_eq!(x.into_object(), Ok(o)); 1119 | } 1120 | } 1121 | -------------------------------------------------------------------------------- /test_data/template.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "users": [ 3 | {{#repeat min=1 max = 10000}} 4 | { 5 | "id": {{@index}}, 6 | "name": "{{firstName}} {{lastName}}", 7 | "work": "{{company}}", 8 | "email": "{{email}}", 9 | "dob": "{{date '1900' '2000' 'YYYY'}}", 10 | "address": "{{int 1 100}} {{street}}", 11 | "city": "{{city}}", 12 | "optedin": {{boolean}} 13 | } 14 | {{/repeat}} 15 | ], 16 | "images": [ 17 | {{#repeat min=0 max=5}} 18 | "img{{@index}}.png" 19 | {{/repeat}} 20 | ], 21 | "coordinates": { 22 | "x": {{float -50 50 '0.00'}}, 23 | "y": {{float -25 25 '0.00'}} 24 | }, 25 | "price": "${{int 0 99999 '0,0'}}" 26 | } --------------------------------------------------------------------------------