diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d05f283d..111861161 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,15 @@ name: CI on: push: pull_request: + workflow_dispatch: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + +env: + RUSTFLAGS: -Dwarnings + jobs: test: name: Rust nightly ${{matrix.os == 'windows' && '(windows)' || ''}} @@ -13,8 +20,9 @@ jobs: fail-fast: false matrix: os: [ubuntu, windows] + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@nightly - run: cargo test - run: cargo test --features preserve_order --tests -- --skip ui --exact @@ -23,6 +31,12 @@ jobs: - run: cargo test --features float_roundtrip,arbitrary_precision --tests -- --skip ui --exact - run: cargo test --features raw_value --tests -- --skip ui --exact - run: cargo test --features unbounded_depth --tests -- --skip ui --exact + - uses: actions/upload-artifact@v4 + if: matrix.os == 'ubuntu' && always() + with: + name: Cargo.lock + path: Cargo.lock + continue-on-error: true build: name: Rust ${{matrix.rust}} ${{matrix.os == 'windows' && '(windows)' || ''}} @@ -30,55 +44,113 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.40.0, 1.36.0, 1.31.0] + rust: [beta, 1.65.0, 1.56.1] os: [ubuntu] include: + - rust: stable + os: ubuntu + target: aarch64-unknown-none - rust: stable os: windows + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} - - run: cargo check - - run: cargo check --features preserve_order - if: matrix.rust != '1.31.0' - - run: cargo check --features float_roundtrip - - run: cargo check --features arbitrary_precision - - run: cargo check --features raw_value - - run: cargo check --features unbounded_depth + targets: ${{matrix.target}} + - run: cargo check --manifest-path tests/crate/Cargo.toml + - run: cargo check --manifest-path tests/crate/Cargo.toml --features float_roundtrip + - run: cargo check --manifest-path tests/crate/Cargo.toml --features arbitrary_precision + - run: cargo check --manifest-path tests/crate/Cargo.toml --features raw_value + - run: cargo check --manifest-path tests/crate/Cargo.toml --features unbounded_depth + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,arbitrary_precision + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,raw_value + - run: cargo check --manifest-path tests/crate/Cargo.toml --features serde_json/preserve_order + if: matrix.rust != '1.56.1' + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,serde_json/preserve_order + if: matrix.rust != '1.56.1' - name: Build without std - run: | - rustup target add aarch64-unknown-none - cargo check \ - --manifest-path tests/crate/Cargo.toml \ - --target aarch64-unknown-none \ - --no-default-features \ - --features alloc - if: matrix.rust == 'stable' && matrix.os == 'ubuntu' + run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc + if: matrix.target - nostd: - name: Rust 1.36.0 + minimal: + name: Minimal versions runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.36.0 - - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc - - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo generate-lockfile -Z minimal-versions + - run: cargo check --locked + + miri: + name: Miri (${{matrix.name}}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: 64-bit little endian + target: x86_64-unknown-linux-gnu + - name: 64-bit big endian + target: powerpc64-unknown-linux-gnu + - name: 32-bit little endian + target: i686-unknown-linux-gnu + - name: 32-bit big endian + target: mips-unknown-linux-gnu + env: + MIRIFLAGS: -Zmiri-strict-provenance + timeout-minutes: 45 + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@miri + - run: cargo miri setup + - run: cargo miri test --target ${{matrix.target}} + - run: cargo miri test --target ${{matrix.target}} --features preserve_order,float_roundtrip,arbitrary_precision,raw_value clippy: name: Clippy runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic + + doc: + name: Documentation + runs-on: ubuntu-latest + timeout-minutes: 45 + env: + RUSTDOCFLAGS: -Dwarnings + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@nightly + - uses: dtolnay/install@cargo-docs-rs + - run: cargo docs-rs fuzz: name: Fuzz runs-on: ubuntu-latest + timeout-minutes: 45 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@nightly - - run: cargo install cargo-fuzz --debug - - run: cargo fuzz build -O + - uses: dtolnay/install@cargo-fuzz + - run: cargo fuzz check + + outdated: + name: Outdated + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + timeout-minutes: 45 + steps: + - uses: actions/checkout@v5 + - uses: dtolnay/rust-toolchain@stable + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --exit-code 1 + - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 diff --git a/.gitignore b/.gitignore index 165eb22d0..e9e21997b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,2 @@ -target/ -**/*.rs.bk -*.sw[po] -Cargo.lock +/target/ +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 587fb206f..4e7a02723 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,40 +1,46 @@ [package] name = "serde_json" -version = "1.0.60" # remember to update html_root_url +version = "1.0.143" authors = ["Erick Tryzelaar ", "David Tolnay "] -license = "MIT OR Apache-2.0" +categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" -repository = "https://github.com/serde-rs/json" -documentation = "https://docs.serde.rs/serde_json/" +documentation = "https://docs.rs/serde_json" +edition = "2021" keywords = ["json", "serde", "serialization"] -categories = ["encoding"] -readme = "README.md" -include = ["build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] -edition = "2018" +license = "MIT OR Apache-2.0" +repository = "https://github.com/serde-rs/json" +rust-version = "1.56" [dependencies] -serde = { version = "1.0.100", default-features = false } -indexmap = { version = "1.5", optional = true } -itoa = { version = "0.4.3", default-features = false } +indexmap = { version = "2.2.3", optional = true } +itoa = "1.0" +memchr = { version = "2", default-features = false } ryu = "1.0" +serde = { version = "1.0.194", default-features = false } [dev-dependencies] -automod = "1.0" -rustversion = "1.0" -serde_bytes = "0.11" -serde_derive = "1.0" -serde_stacker = "0.1" -trybuild = { version = "1.0.19", features = ["diff"] } - -[workspace] -members = ["tests/crate"] +automod = "1.0.11" +indoc = "2.0.2" +ref-cast = "1.0.18" +rustversion = "1.0.13" +serde = { version = "1.0.194", features = ["derive"] } +serde_bytes = "0.11.10" +serde_derive = "1.0.166" +serde_stacker = "0.1.8" +trybuild = { version = "1.0.81", features = ["diff"] } [package.metadata.docs.rs] -features = ["raw_value", "unbounded_depth"] +features = ["preserve_order", "raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = [ + "--generate-link-to-definition", + "--extern-html-root-url=core=https://doc.rust-lang.org", + "--extern-html-root-url=alloc=https://doc.rust-lang.org", + "--extern-html-root-url=std=https://doc.rust-lang.org", +] [package.metadata.playground] -features = ["raw_value"] +features = ["float_roundtrip", "raw_value", "unbounded_depth"] ### FEATURES ################################################################# @@ -42,18 +48,17 @@ features = ["raw_value"] [features] default = ["std"] -std = ["serde/std"] +std = ["memchr/std", "serde/std"] # Provide integration for heap-allocated collections without depending on the # rest of the Rust standard library. # NOTE: Disabling both `std` *and* `alloc` features is not supported yet. -# Available on Rust 1.36+. alloc = ["serde/alloc"] # Make serde_json::Map use a representation which maintains insertion order. # This allows data to be read into a Value and written back to a JSON string # while preserving the order of map keys in the input. -preserve_order = ["indexmap"] +preserve_order = ["indexmap", "std"] # Use sufficient precision when parsing fixed precision floats from JSON to # ensure that they maintain accuracy when round-tripped through JSON. This comes diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 16fe87b06..1b5ec8b78 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -174,28 +174,3 @@ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/README.md b/README.md index f2db0de47..be70b7b06 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.31+]][rustc] +# Serde JSON   [![Build Status]][actions] [![Latest Version]][crates.io] [![Rustc Version 1.36+]][rustc] -[Build Status]: https://img.shields.io/github/workflow/status/serde-rs/json/CI/master -[travis]: https://github.com/serde-rs/json/actions?query=branch%3Amaster +[Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/json/ci.yml?branch=master +[actions]: https://github.com/serde-rs/json/actions?query=branch%3Amaster [Latest Version]: https://img.shields.io/crates/v/serde_json.svg [crates.io]: https://crates.io/crates/serde\_json -[Rustc Version 1.31+]: https://img.shields.io/badge/rustc-1.31+-lightgray.svg -[rustc]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html +[Rustc Version 1.36+]: https://img.shields.io/badge/rustc-1.36+-lightgray.svg +[rustc]: https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html **Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.** @@ -18,8 +18,8 @@ serde_json = "1.0" You may be looking for: -- [JSON API documentation](https://docs.serde.rs/serde_json/) -- [Serde API documentation](https://docs.serde.rs/serde/) +- [JSON API documentation](https://docs.rs/serde_json) +- [Serde API documentation](https://docs.rs/serde) - [Detailed documentation about Serde](https://serde.rs/) - [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/derive.html) - [Release notes](https://github.com/serde-rs/json/releases) @@ -42,22 +42,21 @@ transmit data objects consisting of key-value pairs. } ``` -There are three common ways that you might find yourself needing to work -with JSON data in Rust. +There are three common ways that you might find yourself needing to work with +JSON data in Rust. - - **As text data.** An unprocessed string of JSON data that you receive on - an HTTP endpoint, read from a file, or prepare to send to a remote - server. - - **As an untyped or loosely typed representation.** Maybe you want to - check that some JSON data is valid before passing it on, but without - knowing the structure of what it contains. Or you want to do very basic - manipulations like insert a key in a particular spot. - - **As a strongly typed Rust data structure.** When you expect all or most - of your data to conform to a particular structure and want to get real - work done without JSON's loosey-goosey nature tripping you up. + - **As text data.** An unprocessed string of JSON data that you receive on an + HTTP endpoint, read from a file, or prepare to send to a remote server. + - **As an untyped or loosely typed representation.** Maybe you want to check + that some JSON data is valid before passing it on, but without knowing the + structure of what it contains. Or you want to do very basic manipulations + like insert a key in a particular spot. + - **As a strongly typed Rust data structure.** When you expect all or most of + your data to conform to a particular structure and want to get real work done + without JSON's loosey-goosey nature tripping you up. -Serde JSON provides efficient, flexible, safe ways of converting data -between each of these representations. +Serde JSON provides efficient, flexible, safe ways of converting data between +each of these representations. ## Operating on untyped JSON values @@ -77,13 +76,15 @@ enum Value { A string of JSON data can be parsed into a `serde_json::Value` by the [`serde_json::from_str`][from_str] function. There is also -[`from_slice`][from_slice] for parsing from a byte slice &[u8] and -[`from_reader`][from_reader] for parsing from any `io::Read` like a File or -a TCP stream. +[`from_slice`][from_slice] for parsing from a byte slice `&[u8]` and +[`from_reader`][from_reader] for parsing from any `io::Read` like a File or a +TCP stream. +
- + +
```rust use serde_json::{Result, Value}; @@ -125,7 +126,7 @@ without quotation marks involves converting from a JSON string to a Rust string with [`as_str()`] or avoiding the use of `Value` as described in the following section. -[`as_str()`]: https://docs.serde.rs/serde_json/enum.Value.html#method.as_str +[`as_str()`]: https://docs.rs/serde_json/1/serde_json/enum.Value.html#method.as_str The `Value` representation is sufficient for very basic tasks but can be tedious to work with for anything more significant. Error handling is verbose to @@ -139,9 +140,11 @@ in one of the dozens of places it is used in your code. Serde provides a powerful way of mapping JSON data into Rust data structures largely automatically. +
- + +
```rust use serde::{Deserialize, Serialize}; @@ -181,20 +184,20 @@ fn typed_example() -> Result<()> { This is the same `serde_json::from_str` function as before, but this time we assign the return value to a variable of type `Person` so Serde will automatically interpret the input data as a `Person` and produce informative -error messages if the layout does not conform to what a `Person` is expected -to look like. +error messages if the layout does not conform to what a `Person` is expected to +look like. -Any type that implements Serde's `Deserialize` trait can be deserialized -this way. This includes built-in Rust standard library types like `Vec` -and `HashMap`, as well as any structs or enums annotated with +Any type that implements Serde's `Deserialize` trait can be deserialized this +way. This includes built-in Rust standard library types like `Vec` and +`HashMap`, as well as any structs or enums annotated with `#[derive(Deserialize)]`. -Once we have `p` of type `Person`, our IDE and the Rust compiler can help us -use it correctly like they do for any other Rust code. The IDE can -autocomplete field names to prevent typos, which was impossible in the -`serde_json::Value` representation. And the Rust compiler can check that -when we write `p.phones[0]`, then `p.phones` is guaranteed to be a -`Vec` so indexing into it makes sense and produces a `String`. +Once we have `p` of type `Person`, our IDE and the Rust compiler can help us use +it correctly like they do for any other Rust code. The IDE can autocomplete +field names to prevent typos, which was impossible in the `serde_json::Value` +representation. And the Rust compiler can check that when we write +`p.phones[0]`, then `p.phones` is guaranteed to be a `Vec` so indexing +into it makes sense and produces a `String`. The necessary setup for using Serde's derive macros is explained on the *[Using derive]* page of the Serde site. @@ -206,9 +209,11 @@ derive]* page of the Serde site. Serde JSON provides a [`json!` macro][macro] to build `serde_json::Value` objects with very natural JSON syntax. + ```rust use serde_json::json; @@ -231,17 +236,19 @@ fn main() { } ``` -The `Value::to_string()` function converts a `serde_json::Value` into a -`String` of JSON text. +The `Value::to_string()` function converts a `serde_json::Value` into a `String` +of JSON text. -One neat thing about the `json!` macro is that variables and expressions can -be interpolated directly into the JSON value as you are building it. Serde -will check at compile time that the value you are interpolating is able to -be represented as JSON. +One neat thing about the `json!` macro is that variables and expressions can be +interpolated directly into the JSON value as you are building it. Serde will +check at compile time that the value you are interpolating is able to be +represented as JSON. + ```rust let full_name = "John Doe"; @@ -257,10 +264,10 @@ let john = json!({ }); ``` -This is amazingly convenient but we have the problem we had before with -`Value` which is that the IDE and Rust compiler cannot help us if we get it -wrong. Serde JSON provides a better way of serializing strongly-typed data -structures into JSON text. +This is amazingly convenient, but we have the problem we had before with +`Value`: the IDE and Rust compiler cannot help us if we get it wrong. Serde JSON +provides a better way of serializing strongly-typed data structures into JSON +text. ## Creating JSON by serializing data structures @@ -270,9 +277,11 @@ A data structure can be converted to a JSON string by [`serde_json::to_writer`][to_writer] which serializes to any `io::Write` such as a File or a TCP stream. + ```rust use serde::{Deserialize, Serialize}; @@ -301,10 +310,9 @@ fn print_an_address() -> Result<()> { } ``` -Any type that implements Serde's `Serialize` trait can be serialized this -way. This includes built-in Rust standard library types like `Vec` and -`HashMap`, as well as any structs or enums annotated with -`#[derive(Serialize)]`. +Any type that implements Serde's `Serialize` trait can be serialized this way. +This includes built-in Rust standard library types like `Vec` and `HashMap`, as well as any structs or enums annotated with `#[derive(Serialize)]`. ## Performance @@ -318,19 +326,22 @@ Benchmarks live in the [serde-rs/json-benchmark] repo. ## Getting help -Serde is one of the most widely used Rust libraries so any place that Rustaceans -congregate will be able to help you out. For chat, consider trying the -[#general] or [#beginners] channels of the unofficial community Discord, the -[#rust-usage] channel of the official Rust Project Discord, or the -[#general][zulip] stream in Zulip. For asynchronous, consider the [\[rust\] tag -on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned -weekly easy questions post, or the Rust [Discourse forum][discourse]. It's -acceptable to file a support issue in this repo but they tend not to get as many -eyes as any of the above and may get closed without a response after some time. - -[#general]: https://discord.com/channels/273534239310479360/274215136414400513 -[#beginners]: https://discord.com/channels/273534239310479360/273541522815713281 +Serde is one of the most widely used Rust libraries, so any place that +Rustaceans congregate will be able to help you out. For chat, consider trying +the [#rust-questions] or [#rust-beginners] channels of the unofficial community +Discord (invite: ), the [#rust-usage] or +[#beginners] channels of the official Rust Project Discord (invite: +), or the [#general][zulip] stream in Zulip. For +asynchronous, consider the [\[rust\] tag on StackOverflow][stackoverflow], the +[/r/rust] subreddit which has a pinned weekly easy questions post, or the Rust +[Discourse forum][discourse]. It's acceptable to file a support issue in this +repo, but they tend not to get as many eyes as any of the above and may get +closed without a response after some time. + +[#rust-questions]: https://discord.com/channels/273534239310479360/274215136414400513 +[#rust-beginners]: https://discord.com/channels/273534239310479360/273541522815713281 [#rust-usage]: https://discord.com/channels/442252698964721669/443150878111694848 +[#beginners]: https://discord.com/channels/442252698964721669/448238009733742612 [zulip]: https://rust-lang.zulipchat.com/#narrow/stream/122651-general [stackoverflow]: https://stackoverflow.com/questions/tagged/rust [/r/rust]: https://www.reddit.com/r/rust @@ -339,8 +350,8 @@ eyes as any of the above and may get closed without a response after some time. ## No-std support As long as there is a memory allocator, it is possible to use serde_json without -the rest of the Rust standard library. This is supported on Rust 1.36+. Disable -the default "std" feature and enable the "alloc" feature: +the rest of the Rust standard library. Disable the default "std" feature and +enable the "alloc" feature: ```toml [dependencies] @@ -350,16 +361,16 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] } For JSON support in Serde without a memory allocator, please see the [`serde-json-core`] crate. -[`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/ +[`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -[value]: https://docs.serde.rs/serde_json/value/enum.Value.html -[from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html -[from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html -[from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html -[to_string]: https://docs.serde.rs/serde_json/ser/fn.to_string.html -[to_vec]: https://docs.serde.rs/serde_json/ser/fn.to_vec.html -[to_writer]: https://docs.serde.rs/serde_json/ser/fn.to_writer.html -[macro]: https://docs.serde.rs/serde_json/macro.json.html +[value]: https://docs.rs/serde_json/1/serde_json/value/enum.Value.html +[from_str]: https://docs.rs/serde_json/1/serde_json/de/fn.from_str.html +[from_slice]: https://docs.rs/serde_json/1/serde_json/de/fn.from_slice.html +[from_reader]: https://docs.rs/serde_json/1/serde_json/de/fn.from_reader.html +[to_string]: https://docs.rs/serde_json/1/serde_json/ser/fn.to_string.html +[to_vec]: https://docs.rs/serde_json/1/serde_json/ser/fn.to_vec.html +[to_writer]: https://docs.rs/serde_json/1/serde_json/ser/fn.to_writer.html +[macro]: https://docs.rs/serde_json/1/serde_json/macro.json.html
diff --git a/build.rs b/build.rs index 04ff4a0a1..29907eaf7 100644 --- a/build.rs +++ b/build.rs @@ -1,46 +1,29 @@ use std::env; -use std::process::Command; -use std::str::{self, FromStr}; fn main() { - // Decide ideal limb width for arithmetic in the float parser. Refer to - // src/lexical/math.rs for where this has an effect. - let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); - match target_arch.as_str() { - "aarch64" | "mips64" | "powerpc64" | "x86_64" => { - println!("cargo:rustc-cfg=limb_width_64"); - } - _ => { - println!("cargo:rustc-cfg=limb_width_32"); - } - } - - let minor = match rustc_minor_version() { - Some(minor) => minor, - None => return, - }; - - // BTreeMap::get_key_value - // https://blog.rust-lang.org/2019/12/19/Rust-1.40.0.html#additions-to-the-standard-library - if minor < 40 { - println!("cargo:rustc-cfg=no_btreemap_get_key_value"); - } + println!("cargo:rerun-if-changed=build.rs"); - // BTreeMap::remove_entry - // https://blog.rust-lang.org/2020/07/16/Rust-1.45.0.html#library-changes - if minor < 45 { - println!("cargo:rustc-cfg=no_btreemap_remove_entry"); - } -} + println!("cargo:rustc-check-cfg=cfg(fast_arithmetic, values(\"32\", \"64\"))"); -fn rustc_minor_version() -> Option { - let rustc = env::var_os("RUSTC")?; - let output = Command::new(rustc).arg("--version").output().ok()?; - let version = str::from_utf8(&output.stdout).ok()?; - let mut pieces = version.split('.'); - if pieces.next() != Some("rustc 1") { - return None; + // Decide ideal limb width for arithmetic in the float parser and string + // parser. + let target_arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_pointer_width = env::var_os("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(); + if target_arch == "aarch64" + || target_arch == "loongarch64" + || target_arch == "mips64" + || target_arch == "powerpc64" + || target_arch == "wasm32" + || target_arch == "x86_64" + || target_pointer_width == "64" + { + // The above list of architectures are ones that have native support for + // 64-bit arithmetic, but which have some targets using a smaller + // pointer width. Examples include aarch64-unknown-linux-gnu_ilp32 and + // x86_64-unknown-linux-gnux32. So our choice of limb width is not + // equivalent to using usize everywhere. + println!("cargo:rustc-cfg=fast_arithmetic=\"64\""); + } else { + println!("cargo:rustc-cfg=fast_arithmetic=\"32\""); } - let next = pieces.next()?; - u32::from_str(next).ok() } diff --git a/fuzz/.gitignore b/fuzz/.gitignore index 188f19609..4bc31dc39 100644 --- a/fuzz/.gitignore +++ b/fuzz/.gitignore @@ -1,3 +1,5 @@ -artifacts/ -corpus/ -target/ +/artifacts/ +/corpus/ +/coverage/ +/target/ +/Cargo.lock diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 4823b077b..5fe38b679 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,20 +1,19 @@ [package] name = "serde_json-fuzz" version = "0.0.0" -authors = ["David Korczynski "] +authors = ["David Tolnay "] +edition = "2021" publish = false -edition = "2018" [package.metadata] cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.3" +libfuzzer-sys = "0.4" serde_json = { path = ".." } [[bin]] name = "from_slice" path = "fuzz_targets/from_slice.rs" - -# Prevent this from interfering with workspaces -[workspace] +test = false +doc = false diff --git a/fuzz/fuzz_targets/from_slice.rs b/fuzz/fuzz_targets/from_slice.rs index e071faff8..28bb3ee01 100644 --- a/fuzz/fuzz_targets/from_slice.rs +++ b/fuzz/fuzz_targets/from_slice.rs @@ -4,5 +4,5 @@ use libfuzzer_sys::fuzz_target; use serde_json::{from_slice, Value}; fuzz_target!(|data: &[u8]| { - let _ = from_slice::(data); + _ = from_slice::(data); }); diff --git a/src/de.rs b/src/de.rs index 4f406bd7c..4080c54ac 100644 --- a/src/de.rs +++ b/src/de.rs @@ -3,12 +3,18 @@ use crate::error::{Error, ErrorCode, Result}; #[cfg(feature = "float_roundtrip")] use crate::lexical; -use crate::lib::str::FromStr; -use crate::lib::*; use crate::number::Number; use crate::read::{self, Fused, Reference}; +use alloc::string::String; +use alloc::vec::Vec; +#[cfg(feature = "float_roundtrip")] +use core::iter; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::result; +use core::str::FromStr; use serde::de::{self, Expected, Unexpected}; -use serde::{forward_to_deserialize_any, serde_if_integer128}; +use serde::forward_to_deserialize_any; #[cfg(feature = "arbitrary_precision")] use crate::number::NumberDeserializer; @@ -16,6 +22,7 @@ use crate::number::NumberDeserializer; pub use crate::read::{Read, SliceRead, StrRead}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::read::IoRead; ////////////////////////////////////////////////////////////////////////////// @@ -38,11 +45,17 @@ where /// Create a JSON deserializer from one of the possible serde_json input /// sources. /// + /// When reading from a source against which short reads are not efficient, such + /// as a [`File`], you will want to apply your own buffering because serde_json + /// will not buffer the input. See [`std::io::BufReader`]. + /// /// Typically it is more convenient to use one of these methods instead: /// /// - Deserializer::from_str - /// - Deserializer::from_bytes + /// - Deserializer::from_slice /// - Deserializer::from_reader + /// + /// [`File`]: std::fs::File pub fn new(read: R) -> Self { Deserializer { read, @@ -87,7 +100,9 @@ impl<'a> Deserializer> { macro_rules! overflow { ($a:ident * 10 + $b:ident, $c:expr) => { - $a >= $c / 10 && ($a > $c / 10 || $b > $c % 10) + match $c { + c => $a >= c / 10 && ($a > c / 10 || $b > c % 10), + } }; } @@ -196,11 +211,12 @@ impl<'de, R: Read<'de>> Deserializer { /// } /// ``` #[cfg(feature = "unbounded_depth")] + #[cfg_attr(docsrs, doc(cfg(feature = "unbounded_depth")))] pub fn disable_recursion_limit(&mut self) { self.disable_recursion_limit = true; } - fn peek(&mut self) -> Result> { + pub(crate) fn peek(&mut self) -> Result> { self.read.peek() } @@ -239,7 +255,7 @@ impl<'de, R: Read<'de>> Deserializer { fn parse_whitespace(&mut self) -> Result> { loop { match tri!(self.peek()) { - Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => { + Some(b' ' | b'\n' | b'\t' | b'\r') => { self.eat_char(); } other => { @@ -300,9 +316,9 @@ impl<'de, R: Read<'de>> Deserializer { self.fix_position(err) } - fn deserialize_number(&mut self, visitor: V) -> Result + pub(crate) fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result where - V: de::Visitor<'de>, + V: de::Visitor<'any>, { let peek = match tri!(self.parse_whitespace()) { Some(b) => b, @@ -326,31 +342,98 @@ impl<'de, R: Read<'de>> Deserializer { } } - serde_if_integer128! { - fn scan_integer128(&mut self, buf: &mut String) -> Result<()> { - match tri!(self.next_char_or_null()) { - b'0' => { - buf.push('0'); - // There can be only one leading '0'. - match tri!(self.peek_or_null()) { - b'0'..=b'9' => { - Err(self.peek_error(ErrorCode::InvalidNumber)) - } - _ => Ok(()), - } + #[cfg(feature = "float_roundtrip")] + pub(crate) fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + self.single_precision = true; + let val = self.deserialize_number(visitor); + self.single_precision = false; + val + } + + pub(crate) fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + let mut buf = String::new(); + + match tri!(self.parse_whitespace()) { + Some(b'-') => { + self.eat_char(); + buf.push('-'); + } + Some(_) => {} + None => { + return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); + } + } + + tri!(self.scan_integer128(&mut buf)); + + let value = match buf.parse() { + Ok(int) => visitor.visit_i128(int), + Err(_) => { + return Err(self.error(ErrorCode::NumberOutOfRange)); + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.fix_position(err)), + } + } + + pub(crate) fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result + where + V: de::Visitor<'any>, + { + match tri!(self.parse_whitespace()) { + Some(b'-') => { + return Err(self.peek_error(ErrorCode::NumberOutOfRange)); + } + Some(_) => {} + None => { + return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); + } + } + + let mut buf = String::new(); + tri!(self.scan_integer128(&mut buf)); + + let value = match buf.parse() { + Ok(int) => visitor.visit_u128(int), + Err(_) => { + return Err(self.error(ErrorCode::NumberOutOfRange)); + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.fix_position(err)), + } + } + + fn scan_integer128(&mut self, buf: &mut String) -> Result<()> { + match tri!(self.next_char_or_null()) { + b'0' => { + buf.push('0'); + // There can be only one leading '0'. + match tri!(self.peek_or_null()) { + b'0'..=b'9' => Err(self.peek_error(ErrorCode::InvalidNumber)), + _ => Ok(()), } - c @ b'1'..=b'9' => { + } + c @ b'1'..=b'9' => { + buf.push(c as char); + while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { + self.eat_char(); buf.push(c as char); - while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { - self.eat_char(); - buf.push(c as char); - } - Ok(()) - } - _ => { - Err(self.error(ErrorCode::InvalidNumber)) } + Ok(()) } + _ => Err(self.error(ErrorCode::InvalidNumber)), } } @@ -404,7 +487,7 @@ impl<'de, R: Read<'de>> Deserializer { // try to keep the number as a `u64` until we grow // too large. At that point, switch to parsing the // value as a `f64`. - if overflow!(significand * 10 + digit, u64::max_value()) { + if overflow!(significand * 10 + digit, u64::MAX) { return Ok(ParserNumber::F64(tri!( self.parse_long_integer(positive, significand), ))); @@ -433,8 +516,8 @@ impl<'de, R: Read<'de>> Deserializer { } else { let neg = (significand as i64).wrapping_neg(); - // Convert into a float if we underflow. - if neg > 0 { + // Convert into a float if we underflow, or on `-0`. + if neg >= 0 { ParserNumber::F64(-(significand as f64)) } else { ParserNumber::I64(neg) @@ -448,30 +531,33 @@ impl<'de, R: Read<'de>> Deserializer { &mut self, positive: bool, mut significand: u64, - mut exponent: i32, + exponent_before_decimal_point: i32, ) -> Result { self.eat_char(); + let mut exponent_after_decimal_point = 0; while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { let digit = (c - b'0') as u64; - if overflow!(significand * 10 + digit, u64::max_value()) { + if overflow!(significand * 10 + digit, u64::MAX) { + let exponent = exponent_before_decimal_point + exponent_after_decimal_point; return self.parse_decimal_overflow(positive, significand, exponent); } self.eat_char(); significand = significand * 10 + digit; - exponent -= 1; + exponent_after_decimal_point -= 1; } // Error if there is not at least one digit after the decimal point. - if exponent == 0 { + if exponent_after_decimal_point == 0 { match tri!(self.peek()) { Some(_) => return Err(self.peek_error(ErrorCode::InvalidNumber)), None => return Err(self.peek_error(ErrorCode::EofWhileParsingValue)), } } + let exponent = exponent_before_decimal_point + exponent_after_decimal_point; match tri!(self.peek_or_null()) { b'e' | b'E' => self.parse_exponent(positive, significand, exponent), _ => self.f64_from_parts(positive, significand, exponent), @@ -517,7 +603,7 @@ impl<'de, R: Read<'de>> Deserializer { self.eat_char(); let digit = (c - b'0') as i32; - if overflow!(exp * 10 + digit, i32::max_value()) { + if overflow!(exp * 10 + digit, i32::MAX) { let zero_significand = significand == 0; return self.parse_exponent_overflow(positive, zero_significand, positive_exp); } @@ -709,7 +795,7 @@ impl<'de, R: Read<'de>> Deserializer { self.eat_char(); let digit = (c - b'0') as i32; - if overflow!(exp * 10 + digit, i32::max_value()) { + if overflow!(exp * 10 + digit, i32::MAX) { let zero_significand = self.scratch.iter().all(|&digit| digit == b'0'); return self.parse_exponent_overflow(positive, zero_significand, positive_exp); } @@ -854,7 +940,16 @@ impl<'de, R: Read<'de>> Deserializer { if !positive { buf.push('-'); } - self.scan_integer(&mut buf)?; + tri!(self.scan_integer(&mut buf)); + if positive { + if let Ok(unsigned) = buf.parse() { + return Ok(ParserNumber::U64(unsigned)); + } + } else { + if let Ok(signed) = buf.parse() { + return Ok(ParserNumber::I64(signed)); + } + } Ok(ParserNumber::String(buf)) } @@ -898,7 +993,7 @@ impl<'de, R: Read<'de>> Deserializer { fn scan_number(&mut self, buf: &mut String) -> Result<()> { match tri!(self.peek_or_null()) { b'.' => self.scan_decimal(buf), - b'e' | b'E' => self.scan_exponent(buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -923,19 +1018,20 @@ impl<'de, R: Read<'de>> Deserializer { } match tri!(self.peek_or_null()) { - b'e' | b'E' => self.scan_exponent(buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } #[cfg(feature = "arbitrary_precision")] - fn scan_exponent(&mut self, buf: &mut String) -> Result<()> { + fn scan_exponent(&mut self, e: char, buf: &mut String) -> Result<()> { self.eat_char(); - buf.push('e'); + buf.push(e); match tri!(self.peek_or_null()) { b'+' => { self.eat_char(); + buf.push('+'); } b'-' => { self.eat_char(); @@ -1043,7 +1139,7 @@ impl<'de, R: Read<'de>> Deserializer { tri!(self.read.ignore_str()); None } - frame @ b'[' | frame @ b'{' => { + frame @ (b'[' | b'{') => { self.scratch.extend(enclosing.take()); self.eat_char(); Some(frame) @@ -1188,9 +1284,9 @@ impl<'de, R: Read<'de>> Deserializer { where V: de::Visitor<'de>, { - self.parse_whitespace()?; + tri!(self.parse_whitespace()); self.read.begin_raw_buffering(); - self.ignore_value()?; + tri!(self.ignore_value()); self.read.end_raw_buffering(visitor) } } @@ -1242,11 +1338,15 @@ static POW10: [f64; 309] = [ macro_rules! deserialize_number { ($method:ident) => { + deserialize_number!($method, deserialize_number); + }; + + ($method:ident, $using:ident) => { fn $method(self, visitor: V) -> Result where V: de::Visitor<'de>, { - self.deserialize_number(visitor) + self.$using(visitor) } }; } @@ -1284,7 +1384,7 @@ macro_rules! check_recursion { }; } -impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { +impl<'de, R: Read<'de>> de::Deserializer<'de> for &mut Deserializer { type Error = Error; #[inline] @@ -1408,79 +1508,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { deserialize_number!(deserialize_f64); #[cfg(feature = "float_roundtrip")] - fn deserialize_f32(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - self.single_precision = true; - let val = self.deserialize_number(visitor); - self.single_precision = false; - val - } - - serde_if_integer128! { - fn deserialize_i128(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - let mut buf = String::new(); - - match tri!(self.parse_whitespace()) { - Some(b'-') => { - self.eat_char(); - buf.push('-'); - } - Some(_) => {} - None => { - return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); - } - }; - - tri!(self.scan_integer128(&mut buf)); - - let value = match buf.parse() { - Ok(int) => visitor.visit_i128(int), - Err(_) => { - return Err(self.error(ErrorCode::NumberOutOfRange)); - } - }; - - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), - } - } - - fn deserialize_u128(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - match tri!(self.parse_whitespace()) { - Some(b'-') => { - return Err(self.peek_error(ErrorCode::NumberOutOfRange)); - } - Some(_) => {} - None => { - return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); - } - } - - let mut buf = String::new(); - tri!(self.scan_integer128(&mut buf)); - - let value = match buf.parse() { - Ok(int) => visitor.visit_u128(int), - Err(_) => { - return Err(self.error(ErrorCode::NumberOutOfRange)); - } - }; - - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), - } - } - } + deserialize_number!(deserialize_f32, do_deserialize_f32); + deserialize_number!(deserialize_i128, do_deserialize_i128); + deserialize_number!(deserialize_u128, do_deserialize_u128); fn deserialize_char(self, visitor: V) -> Result where @@ -1551,14 +1581,18 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// /// The behavior of serde_json is specified to fail on non-UTF-8 strings /// when deserializing into Rust UTF-8 string types such as String, and - /// succeed with non-UTF-8 bytes when deserializing using this method. + /// succeed with the bytes representing the [WTF-8] encoding of code points + /// when deserializing using this method. + /// + /// [WTF-8]: https://simonsapin.github.io/wtf-8 /// /// Escape sequences are processed as usual, and for `\uXXXX` escapes it is /// still checked if the hex number represents a valid Unicode code point. /// /// # Examples /// - /// You can use this to parse JSON strings containing invalid UTF-8 bytes. + /// You can use this to parse JSON strings containing invalid UTF-8 bytes, + /// or unpaired surrogates. /// /// ``` /// use serde_bytes::ByteBuf; @@ -1578,20 +1612,18 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// ``` /// /// Backslash escape sequences like `\n` are still interpreted and required - /// to be valid, and `\u` escape sequences are required to represent valid - /// Unicode code points. + /// to be valid. `\u` escape sequences are required to represent a valid + /// Unicode code point or lone surrogate. /// /// ``` /// use serde_bytes::ByteBuf; /// - /// fn look_at_bytes() { - /// let json_data = b"\"invalid unicode surrogate: \\uD801\""; - /// let parsed: Result = serde_json::from_slice(json_data); - /// - /// assert!(parsed.is_err()); - /// - /// let expected_msg = "unexpected end of hex escape at line 1 column 35"; - /// assert_eq!(expected_msg, parsed.unwrap_err().to_string()); + /// fn look_at_bytes() -> Result<(), serde_json::Error> { + /// let json_data = b"\"lone surrogate: \\uD801\""; + /// let bytes: ByteBuf = serde_json::from_slice(json_data)?; + /// let expected = b"lone surrogate: \xED\xA0\x81"; + /// assert_eq!(expected, bytes.as_slice()); + /// Ok(()) /// } /// # /// # look_at_bytes(); @@ -1847,8 +1879,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { Some(b'{') => { check_recursion! { self.eat_char(); - let value = tri!(visitor.visit_enum(VariantAccess::new(self))); + let ret = visitor.visit_enum(VariantAccess::new(self)); } + let value = tri!(ret); match tri!(self.parse_whitespace()) { Some(b'}') => { @@ -1899,31 +1932,37 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { where T: de::DeserializeSeed<'de>, { - let peek = match tri!(self.de.parse_whitespace()) { - Some(b']') => { - return Ok(None); - } - Some(b',') if !self.first => { - self.de.eat_char(); - tri!(self.de.parse_whitespace()) - } - Some(b) => { - if self.first { - self.first = false; - Some(b) - } else { - return Err(self.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)); + fn has_next_element<'de, 'a, R: Read<'de> + 'a>( + seq: &mut SeqAccess<'a, R>, + ) -> Result { + let peek = match tri!(seq.de.parse_whitespace()) { + Some(b) => b, + None => { + return Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)); } + }; + + if peek == b']' { + Ok(false) + } else if seq.first { + seq.first = false; + Ok(true) + } else if peek == b',' { + seq.de.eat_char(); + match tri!(seq.de.parse_whitespace()) { + Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Ok(true), + None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), + } + } else { + Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)) } - None => { - return Err(self.de.peek_error(ErrorCode::EofWhileParsingList)); - } - }; + } - match peek { - Some(b']') => Err(self.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Ok(Some(tri!(seed.deserialize(&mut *self.de)))), - None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)), + if tri!(has_next_element(self)) { + Ok(Some(tri!(seed.deserialize(&mut *self.de)))) + } else { + Ok(None) } } } @@ -1946,32 +1985,40 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { where K: de::DeserializeSeed<'de>, { - let peek = match tri!(self.de.parse_whitespace()) { - Some(b'}') => { - return Ok(None); - } - Some(b',') if !self.first => { - self.de.eat_char(); - tri!(self.de.parse_whitespace()) - } - Some(b) => { - if self.first { - self.first = false; - Some(b) + fn has_next_key<'de, 'a, R: Read<'de> + 'a>(map: &mut MapAccess<'a, R>) -> Result { + let peek = match tri!(map.de.parse_whitespace()) { + Some(b) => b, + None => { + return Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)); + } + }; + + if peek == b'}' { + Ok(false) + } else if map.first { + map.first = false; + if peek == b'"' { + Ok(true) } else { - return Err(self.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)); + Err(map.de.peek_error(ErrorCode::KeyMustBeAString)) } + } else if peek == b',' { + map.de.eat_char(); + match tri!(map.de.parse_whitespace()) { + Some(b'"') => Ok(true), + Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), + None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), + } + } else { + Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)) } - None => { - return Err(self.de.peek_error(ErrorCode::EofWhileParsingObject)); - } - }; + } - match peek { - Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some), - Some(b'}') => Err(self.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)), - None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)), + if tri!(has_next_key(self)) { + Ok(Some(tri!(seed.deserialize(MapKey { de: &mut *self.de })))) + } else { + Ok(None) } } @@ -2105,24 +2152,47 @@ struct MapKey<'a, R: 'a> { de: &'a mut Deserializer, } -macro_rules! deserialize_integer_key { - ($method:ident => $visit:ident) => { +macro_rules! deserialize_numeric_key { + ($method:ident) => { + fn $method(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.deserialize_number(visitor) + } + }; + + ($method:ident, $delegate:ident) => { fn $method(self, visitor: V) -> Result where V: de::Visitor<'de>, { self.de.eat_char(); - self.de.scratch.clear(); - let string = tri!(self.de.read.parse_str(&mut self.de.scratch)); - match (string.parse(), string) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Reference::Borrowed(s)) => visitor.visit_borrowed_str(s), - (Err(_), Reference::Copied(s)) => visitor.visit_str(s), + + match tri!(self.de.peek()) { + Some(b'0'..=b'9' | b'-') => {} + _ => return Err(self.de.error(ErrorCode::ExpectedNumericKey)), } + + let value = tri!(self.de.$delegate(visitor)); + + match tri!(self.de.peek()) { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), + } + + Ok(value) } }; } +impl<'de, 'a, R> MapKey<'a, R> +where + R: Read<'de>, +{ + deserialize_numeric_key!(deserialize_number, deserialize_number); +} + impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R> where R: Read<'de>, @@ -2142,18 +2212,55 @@ where } } - deserialize_integer_key!(deserialize_i8 => visit_i8); - deserialize_integer_key!(deserialize_i16 => visit_i16); - deserialize_integer_key!(deserialize_i32 => visit_i32); - deserialize_integer_key!(deserialize_i64 => visit_i64); - deserialize_integer_key!(deserialize_u8 => visit_u8); - deserialize_integer_key!(deserialize_u16 => visit_u16); - deserialize_integer_key!(deserialize_u32 => visit_u32); - deserialize_integer_key!(deserialize_u64 => visit_u64); + deserialize_numeric_key!(deserialize_i8); + deserialize_numeric_key!(deserialize_i16); + deserialize_numeric_key!(deserialize_i32); + deserialize_numeric_key!(deserialize_i64); + deserialize_numeric_key!(deserialize_i128, deserialize_i128); + deserialize_numeric_key!(deserialize_u8); + deserialize_numeric_key!(deserialize_u16); + deserialize_numeric_key!(deserialize_u32); + deserialize_numeric_key!(deserialize_u64); + deserialize_numeric_key!(deserialize_u128, deserialize_u128); + #[cfg(not(feature = "float_roundtrip"))] + deserialize_numeric_key!(deserialize_f32); + #[cfg(feature = "float_roundtrip")] + deserialize_numeric_key!(deserialize_f32, deserialize_f32); + deserialize_numeric_key!(deserialize_f64); - serde_if_integer128! { - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u128 => visit_u128); + fn deserialize_bool(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.de.eat_char(); + + let peek = match tri!(self.de.next_char()) { + Some(b) => b, + None => { + return Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)); + } + }; + + let value = match peek { + b't' => { + tri!(self.de.parse_ident(b"rue\"")); + visitor.visit_bool(true) + } + b'f' => { + tri!(self.de.parse_ident(b"alse\"")); + visitor.visit_bool(false) + } + _ => { + self.de.scratch.clear(); + let s = tri!(self.de.read.parse_str(&mut self.de.scratch)); + Err(de::Error::invalid_type(Unexpected::Str(&s), &visitor)) + } + }; + + match value { + Ok(value) => Ok(value), + Err(err) => Err(self.de.fix_position(err)), + } } #[inline] @@ -2166,10 +2273,18 @@ where } #[inline] - fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + fn deserialize_newtype_struct(self, name: &'static str, visitor: V) -> Result where V: de::Visitor<'de>, { + #[cfg(feature = "raw_value")] + { + if name == crate::raw::TOKEN { + return self.de.deserialize_raw_value(visitor); + } + } + + let _ = name; visitor.visit_newtype_struct(self) } @@ -2203,8 +2318,8 @@ where } forward_to_deserialize_any! { - bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map - struct identifier ignored_any + char str string unit unit_struct seq tuple tuple_struct map struct + identifier ignored_any } } @@ -2250,7 +2365,7 @@ where /// Typically it is more convenient to use one of these methods instead: /// /// - Deserializer::from_str(...).into_iter() - /// - Deserializer::from_bytes(...).into_iter() + /// - Deserializer::from_slice(...).into_iter() /// - Deserializer::from_reader(...).into_iter() pub fn new(read: R) -> Self { let offset = read.byte_offset(); @@ -2300,8 +2415,8 @@ where fn peek_end_of_value(&mut self) -> Result<()> { match tri!(self.de.peek()) { - Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') | Some(b'"') | Some(b'[') - | Some(b']') | Some(b'{') | Some(b'}') | Some(b',') | Some(b':') | None => Ok(()), + Some(b' ' | b'\n' | b'\t' | b'\r' | b'"' | b'[' | b']' | b'{' | b'}' | b',' | b':') + | None => Ok(()), Some(_) => { let position = self.de.read.peek_position(); Err(Error::syntax( @@ -2351,7 +2466,7 @@ where if self_delineated_value { Ok(value) } else { - self.peek_end_of_value().map(|_| value) + self.peek_end_of_value().map(|()| value) } } Err(e) => { @@ -2390,9 +2505,9 @@ where Ok(value) } -/// Deserialize an instance of type `T` from an IO stream of JSON. +/// Deserialize an instance of type `T` from an I/O stream of JSON. /// -/// The content of the IO stream is deserialized directly from the stream +/// The content of the I/O stream is deserialized directly from the stream /// without being buffered in memory by serde_json. /// /// When reading from a source against which short reads are not efficient, such @@ -2408,10 +2523,7 @@ where /// reading a file completely into memory and then applying [`from_str`] /// or [`from_slice`] on it. See [issue #160]. /// -/// [`File`]: https://doc.rust-lang.org/std/fs/struct.File.html -/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html -/// [`from_str`]: ./fn.from_str.html -/// [`from_slice`]: ./fn.from_slice.html +/// [`File`]: std::fs::File /// [issue #160]: https://github.com/serde-rs/json/issues/160 /// /// # Example @@ -2432,7 +2544,7 @@ where /// location: String, /// } /// -/// fn read_user_from_file>(path: P) -> Result> { +/// fn read_user_from_file>(path: P) -> Result> { /// // Open the file in read-only mode with buffer. /// let file = File::open(path)?; /// let reader = BufReader::new(file); @@ -2458,6 +2570,7 @@ where /// use serde::Deserialize; /// /// use std::error::Error; +/// use std::io::BufReader; /// use std::net::{TcpListener, TcpStream}; /// /// #[derive(Deserialize, Debug)] @@ -2466,8 +2579,8 @@ where /// location: String, /// } /// -/// fn read_user_from_stream(tcp_stream: TcpStream) -> Result> { -/// let mut de = serde_json::Deserializer::from_reader(tcp_stream); +/// fn read_user_from_stream(stream: &mut BufReader) -> Result> { +/// let mut de = serde_json::Deserializer::from_reader(stream); /// let u = User::deserialize(&mut de)?; /// /// Ok(u) @@ -2478,8 +2591,9 @@ where /// # fn fake_main() { /// let listener = TcpListener::bind("127.0.0.1:4000").unwrap(); /// -/// for stream in listener.incoming() { -/// println!("{:#?}", read_user_from_stream(stream.unwrap())); +/// for tcp_stream in listener.incoming() { +/// let mut buffered = BufReader::new(tcp_stream.unwrap()); +/// println!("{:#?}", read_user_from_stream(&mut buffered)); /// } /// } /// ``` @@ -2494,6 +2608,7 @@ where /// the JSON map or some number is too big to fit in the expected primitive /// type. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_reader(rdr: R) -> Result where R: crate::io::Read, diff --git a/src/error.rs b/src/error.rs index 7dcdd81c9..fbf9eb14e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,16 @@ //! When serializing or deserializing JSON goes wrong. use crate::io; -use crate::lib::str::FromStr; -use crate::lib::*; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use core::fmt::{self, Debug, Display}; +use core::result; +use core::str::FromStr; use serde::{de, ser}; +#[cfg(feature = "std")] +use std::error; +#[cfg(feature = "std")] +use std::io::ErrorKind; /// This type represents all possible errors that can occur when serializing or /// deserializing JSON data. @@ -31,15 +38,16 @@ impl Error { /// The first character in the input and any characters immediately /// following a newline character are in column 1. /// - /// Note that errors may occur in column 0, for example if a read from an IO - /// stream fails immediately following a previously read newline character. + /// Note that errors may occur in column 0, for example if a read from an + /// I/O stream fails immediately following a previously read newline + /// character. pub fn column(&self) -> usize { self.err.column } /// Categorizes the cause of this error. /// - /// - `Category::Io` - failure to read or write bytes on an IO stream + /// - `Category::Io` - failure to read or write bytes on an I/O stream /// - `Category::Syntax` - input that is not syntactically valid JSON /// - `Category::Data` - input data that is semantically incorrect /// - `Category::Eof` - unexpected end of the input data @@ -56,12 +64,15 @@ impl Error { | ErrorCode::ExpectedObjectCommaOrEnd | ErrorCode::ExpectedSomeIdent | ErrorCode::ExpectedSomeValue + | ErrorCode::ExpectedDoubleQuote | ErrorCode::InvalidEscape | ErrorCode::InvalidNumber | ErrorCode::NumberOutOfRange | ErrorCode::InvalidUnicodeCodePoint | ErrorCode::ControlCharacterWhileParsingString | ErrorCode::KeyMustBeAString + | ErrorCode::ExpectedNumericKey + | ErrorCode::FloatKeyMustBeFinite | ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::TrailingComma | ErrorCode::TrailingCharacters @@ -71,7 +82,7 @@ impl Error { } /// Returns true if this error was caused by a failure to read or write - /// bytes on an IO stream. + /// bytes on an I/O stream. pub fn is_io(&self) -> bool { self.classify() == Category::Io } @@ -99,12 +110,61 @@ impl Error { pub fn is_eof(&self) -> bool { self.classify() == Category::Eof } + + /// The kind reported by the underlying standard library I/O error, if this + /// error was caused by a failure to read or write bytes on an I/O stream. + /// + /// # Example + /// + /// ``` + /// use serde_json::Value; + /// use std::io::{self, ErrorKind, Read}; + /// use std::process; + /// + /// struct ReaderThatWillTimeOut<'a>(&'a [u8]); + /// + /// impl<'a> Read for ReaderThatWillTimeOut<'a> { + /// fn read(&mut self, buf: &mut [u8]) -> io::Result { + /// if self.0.is_empty() { + /// Err(io::Error::new(ErrorKind::TimedOut, "timed out")) + /// } else { + /// self.0.read(buf) + /// } + /// } + /// } + /// + /// fn main() { + /// let reader = ReaderThatWillTimeOut(br#" {"k": "#); + /// + /// let _: Value = match serde_json::from_reader(reader) { + /// Ok(value) => value, + /// Err(error) => { + /// if error.io_error_kind() == Some(ErrorKind::TimedOut) { + /// // Maybe this application needs to retry certain kinds of errors. + /// + /// # return; + /// } else { + /// eprintln!("error: {}", error); + /// process::exit(1); + /// } + /// } + /// }; + /// } + /// ``` + #[cfg(feature = "std")] + pub fn io_error_kind(&self) -> Option { + if let ErrorCode::Io(io_error) = &self.err.code { + Some(io_error.kind()) + } else { + None + } + } } /// Categorizes the cause of a `serde_json::Error`. #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Category { - /// The error was caused by a failure to read or write bytes on an IO + /// The error was caused by a failure to read or write bytes on an I/O /// stream. Io, @@ -129,8 +189,8 @@ pub enum Category { impl From for io::Error { /// Convert a `serde_json::Error` into an `io::Error`. /// - /// JSON syntax and data errors are turned into `InvalidData` IO errors. - /// EOF errors are turned into `UnexpectedEof` IO errors. + /// JSON syntax and data errors are turned into `InvalidData` I/O errors. + /// EOF errors are turned into `UnexpectedEof` I/O errors. /// /// ``` /// use std::io; @@ -160,8 +220,8 @@ impl From for io::Error { } else { match j.classify() { Category::Io => unreachable!(), - Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j), - Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j), + Category::Syntax | Category::Data => io::Error::new(ErrorKind::InvalidData, j), + Category::Eof => io::Error::new(ErrorKind::UnexpectedEof, j), } } } @@ -177,7 +237,7 @@ pub(crate) enum ErrorCode { /// Catchall for syntax error messages Message(Box), - /// Some IO error occurred while serializing or deserializing. + /// Some I/O error occurred while serializing or deserializing. Io(io::Error), /// EOF while parsing a list. @@ -207,6 +267,9 @@ pub(crate) enum ErrorCode { /// Expected this character to start a JSON value. ExpectedSomeValue, + /// Expected this character to be a `"`. + ExpectedDoubleQuote, + /// Invalid hex escape code. InvalidEscape, @@ -225,6 +288,12 @@ pub(crate) enum ErrorCode { /// Object key is not a string. KeyMustBeAString, + /// Contents of key were supposed to be a number. + ExpectedNumericKey, + + /// Object key is a non-finite float value. + FloatKeyMustBeFinite, + /// Lone leading surrogate in hex escape. LoneLeadingSurrogateInHexEscape, @@ -234,7 +303,7 @@ pub(crate) enum ErrorCode { /// JSON has non-whitespace trailing characters after the value. TrailingCharacters, - /// Unexpected end of hex excape. + /// Unexpected end of hex escape. UnexpectedEndOfHexEscape, /// Encountered nesting of JSON maps and arrays more than 128 layers deep. @@ -279,9 +348,9 @@ impl Error { impl Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ErrorCode::Message(ref msg) => f.write_str(msg), - ErrorCode::Io(ref err) => Display::fmt(err, f), + match self { + ErrorCode::Message(msg) => f.write_str(msg), + ErrorCode::Io(err) => Display::fmt(err, f), ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"), ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"), ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"), @@ -291,6 +360,7 @@ impl Display for ErrorCode { ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"), ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"), ErrorCode::ExpectedSomeValue => f.write_str("expected value"), + ErrorCode::ExpectedDoubleQuote => f.write_str("expected `\"`"), ErrorCode::InvalidEscape => f.write_str("invalid escape"), ErrorCode::InvalidNumber => f.write_str("invalid number"), ErrorCode::NumberOutOfRange => f.write_str("number out of range"), @@ -299,6 +369,12 @@ impl Display for ErrorCode { f.write_str("control character (\\u0000-\\u001F) found while parsing a string") } ErrorCode::KeyMustBeAString => f.write_str("key must be a string"), + ErrorCode::ExpectedNumericKey => { + f.write_str("invalid value: expected key to be a number in quotes") + } + ErrorCode::FloatKeyMustBeFinite => { + f.write_str("float key must be finite (got NaN or +/-inf)") + } ErrorCode::LoneLeadingSurrogateInHexEscape => { f.write_str("lone leading surrogate in hex escape") } @@ -313,8 +389,8 @@ impl Display for ErrorCode { impl serde::de::StdError for Error { #[cfg(feature = "std")] fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.err.code { - ErrorCode::Io(ref err) => Some(err), + match &self.err.code { + ErrorCode::Io(err) => err.source(), _ => None, } } @@ -362,11 +438,20 @@ impl de::Error for Error { #[cold] fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { - if let de::Unexpected::Unit = unexp { - Error::custom(format_args!("invalid type: null, expected {}", exp)) - } else { - Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) - } + Error::custom(format_args!( + "invalid type: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) + } + + #[cold] + fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { + Error::custom(format_args!( + "invalid value: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) } } @@ -377,6 +462,22 @@ impl ser::Error for Error { } } +struct JsonUnexpected<'a>(de::Unexpected<'a>); + +impl<'a> Display for JsonUnexpected<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + match self.0 { + de::Unexpected::Unit => formatter.write_str("null"), + de::Unexpected::Float(value) => write!( + formatter, + "floating point `{}`", + ryu::Buffer::new().format(value), + ), + unexp => Display::fmt(&unexp, formatter), + } + } +} + // Parse our own error message that looks like "{} at line {} column {}" to work // around erased-serde round-tripping the error through de::Error::custom. fn make_error(mut msg: String) -> Error { @@ -433,7 +534,7 @@ fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> { } fn starts_with_digit(slice: &str) -> bool { - match slice.as_bytes().get(0) { + match slice.as_bytes().first() { None => false, Some(&byte) => byte >= b'0' && byte <= b'9', } diff --git a/src/features_check/error.rs b/src/features_check/error.rs deleted file mode 100644 index 22e58235c..000000000 --- a/src/features_check/error.rs +++ /dev/null @@ -1 +0,0 @@ -"serde_json requires that either `std` (default) or `alloc` feature is enabled" diff --git a/src/features_check/mod.rs b/src/features_check/mod.rs deleted file mode 100644 index d12032cef..000000000 --- a/src/features_check/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Shows a user-friendly compiler error on incompatible selected features. - -#[allow(unused_macros)] -macro_rules! hide_from_rustfmt { - ($mod:item) => { - $mod - }; -} - -#[cfg(not(any(feature = "std", feature = "alloc")))] -hide_from_rustfmt! { - mod error; -} diff --git a/src/io/core.rs b/src/io/core.rs index 97354eb30..54c8ddfda 100644 --- a/src/io/core.rs +++ b/src/io/core.rs @@ -1,13 +1,15 @@ //! Reimplements core logic and types from `std::io` in an `alloc`-friendly //! fashion. -use crate::lib::*; +use alloc::vec::Vec; +use core::fmt::{self, Display}; +use core::result; pub enum ErrorKind { Other, } -// IO errors can never occur in no-std mode. All our no-std IO implementations +// I/O errors can never occur in no-std mode. All our no-std I/O implementations // are infallible. pub struct Error; diff --git a/src/iter.rs b/src/iter.rs index c14026f73..9792916dc 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -12,7 +12,7 @@ pub struct LineColIterator { /// The column is 0 immediately after a newline character has been read. col: usize, - /// Byte offset of the start of the current line. This is the sum of lenghts + /// Byte offset of the start of the current line. This is the sum of lengths /// of all previous lines. Keeping track of things this way allows efficient /// computation of the current line, column, and byte offset while only /// updating one of the counters in `next()` in the common case. diff --git a/src/lexical/algorithm.rs b/src/lexical/algorithm.rs index d01e5b950..eaa5e7ebc 100644 --- a/src/lexical/algorithm.rs +++ b/src/lexical/algorithm.rs @@ -51,7 +51,10 @@ where // Compute the product of the power, if it overflows, // prematurely return early, otherwise, if we didn't overshoot, // we can get an exact value. - let value = mantissa.checked_mul(power)?; + let value = match mantissa.checked_mul(power) { + None => return None, + Some(value) => value, + }; if value >> mantissa_size != 0 { None } else { @@ -133,7 +136,7 @@ where let shift = fp.normalize(); errors <<= shift; - u64::error_is_accurate::(errors, &fp) + u64::error_is_accurate::(errors, fp) } } diff --git a/src/lexical/bhcomp.rs b/src/lexical/bhcomp.rs index 6d76c5944..e52b1c9b9 100644 --- a/src/lexical/bhcomp.rs +++ b/src/lexical/bhcomp.rs @@ -12,7 +12,7 @@ use super::float::*; use super::math::*; use super::num::*; use super::rounding::*; -use crate::lib::{cmp, mem}; +use core::{cmp, mem}; // MANTISSA @@ -75,13 +75,13 @@ where // FLOAT OPS -/// Calculate `b` from a a representation of `b` as a float. +/// Calculate `b` from a representation of `b` as a float. #[inline] pub(super) fn b_extended(f: F) -> ExtendedFloat { ExtendedFloat::from_float(f) } -/// Calculate `b+h` from a a representation of `b` as a float. +/// Calculate `b+h` from a representation of `b` as a float. #[inline] pub(super) fn bh_extended(f: F) -> ExtendedFloat { // None of these can overflow. diff --git a/src/lexical/bignum.rs b/src/lexical/bignum.rs index dee4e688b..4fa7eed6d 100644 --- a/src/lexical/bignum.rs +++ b/src/lexical/bignum.rs @@ -3,7 +3,8 @@ //! Big integer type definition. use super::math::*; -use crate::lib::Vec; +#[allow(unused_imports)] +use alloc::vec::Vec; /// Storage for a big integer type. #[derive(Clone, PartialEq, Eq)] diff --git a/src/lexical/digit.rs b/src/lexical/digit.rs index 882aa9eef..3d150a1af 100644 --- a/src/lexical/digit.rs +++ b/src/lexical/digit.rs @@ -11,5 +11,8 @@ pub(crate) fn to_digit(c: u8) -> Option { // Add digit to mantissa. #[inline] pub(crate) fn add_digit(value: u64, digit: u32) -> Option { - value.checked_mul(10)?.checked_add(digit as u64) + match value.checked_mul(10) { + None => None, + Some(n) => n.checked_add(digit as u64), + } } diff --git a/src/lexical/errors.rs b/src/lexical/errors.rs index cad4bd3d5..f4f41cdc5 100644 --- a/src/lexical/errors.rs +++ b/src/lexical/errors.rs @@ -5,8 +5,7 @@ //! This estimates the error in a floating-point representation. //! //! This implementation is loosely based off the Golang implementation, -//! found here: -//! https://golang.org/src/strconv/atof.go +//! found here: use super::float::*; use super::num::*; diff --git a/src/lexical/exponent.rs b/src/lexical/exponent.rs index 6fc51977e..5e27de893 100644 --- a/src/lexical/exponent.rs +++ b/src/lexical/exponent.rs @@ -8,8 +8,8 @@ /// the mantissa we do not overflow for comically-long exponents. #[inline] fn into_i32(value: usize) -> i32 { - if value > i32::max_value() as usize { - i32::max_value() + if value > i32::MAX as usize { + i32::MAX } else { value as i32 } diff --git a/src/lexical/float.rs b/src/lexical/float.rs index e320ead16..2d434a29c 100644 --- a/src/lexical/float.rs +++ b/src/lexical/float.rs @@ -108,7 +108,7 @@ impl ExtendedFloat { F: Float, Algorithm: FnOnce(&mut ExtendedFloat, i32), { - round_to_native::(self, algorithm) + round_to_native::(self, algorithm); } // FROM diff --git a/src/lexical/large_powers.rs b/src/lexical/large_powers.rs index c63ce1cf2..af2c8a6a9 100644 --- a/src/lexical/large_powers.rs +++ b/src/lexical/large_powers.rs @@ -2,8 +2,8 @@ //! Precalculated large powers for limbs. -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub(crate) use super::large_powers32::*; -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub(crate) use super::large_powers64::*; diff --git a/src/lexical/large_powers32.rs b/src/lexical/large_powers32.rs index 799119726..eb8582f5f 100644 --- a/src/lexical/large_powers32.rs +++ b/src/lexical/large_powers32.rs @@ -2,7 +2,7 @@ //! Precalculated large powers for 32-bit limbs. -/// Large powers (&[u32]) for base5 operations. +/// Large powers (`&[u32]`) for base5 operations. const POW5_1: [u32; 1] = [5]; const POW5_2: [u32; 1] = [25]; const POW5_3: [u32; 1] = [625]; diff --git a/src/lexical/large_powers64.rs b/src/lexical/large_powers64.rs index ee3656108..96554eac1 100644 --- a/src/lexical/large_powers64.rs +++ b/src/lexical/large_powers64.rs @@ -2,7 +2,7 @@ //! Precalculated large powers for 64-bit limbs. -/// Large powers (&[u64]) for base5 operations. +/// Large powers (`&[u64]`) for base5 operations. const POW5_1: [u64; 1] = [5]; const POW5_2: [u64; 1] = [25]; const POW5_3: [u64; 1] = [625]; diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 4890c0fa3..2e900f1a1 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -9,7 +9,8 @@ use super::large_powers; use super::num::*; use super::small_powers::*; -use crate::lib::{cmp, iter, mem, Vec}; +use alloc::vec::Vec; +use core::{cmp, iter, mem}; // ALIASES // ------- @@ -36,29 +37,29 @@ use crate::lib::{cmp, iter, mem, Vec}; // sparc64 (`UMUL` only supported double-word arguments). // 32-BIT LIMB -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub type Limb = u32; -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub const POW5_LIMB: &[Limb] = &POW5_32; -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub const POW10_LIMB: &[Limb] = &POW10_32; -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] type Wide = u64; // 64-BIT LIMB -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub type Limb = u64; -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub const POW5_LIMB: &[Limb] = &POW5_64; -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub const POW10_LIMB: &[Limb] = &POW10_64; -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] type Wide = u128; /// Cast to limb type. @@ -78,14 +79,14 @@ fn as_wide(t: T) -> Wide { /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] fn split_u64(x: u64) -> [Limb; 2] { [as_limb(x), as_limb(x >> 32)] } /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] fn split_u64(x: u64) -> [Limb; 1] { [as_limb(x)] } @@ -271,9 +272,7 @@ mod scalar { mod small { use super::*; - // MULTIPLICATIION - - /// ADDITION + // ADDITION /// Implied AddAssign implementation for adding a small integer to bigint. /// @@ -335,7 +334,7 @@ mod small { pub fn imul(x: &mut Vec, y: Limb) { // Multiply iteratively over all elements, adding the carry each time. let mut carry: Limb = 0; - for xi in x.iter_mut() { + for xi in &mut *x { carry = scalar::imul(xi, y, carry); } @@ -481,7 +480,7 @@ mod small { let rshift = bits - n; let lshift = n; let mut prev: Limb = 0; - for xi in x.iter_mut() { + for xi in &mut *x { let tmp = *xi; *xi <<= lshift; *xi |= prev >> rshift; @@ -593,7 +592,7 @@ mod large { // Iteratively add elements from y to x. let mut carry = false; - for (xi, yi) in (&mut x[xstart..]).iter_mut().zip(y.iter()) { + for (xi, yi) in x[xstart..].iter_mut().zip(y.iter()) { // Only one op of the two can overflow, since we added at max // Limb::max_value() + Limb::max_value(). Add the previous carry, // and store the current carry for the next. @@ -613,7 +612,7 @@ mod large { /// AddAssign bigint to bigint. #[inline] pub fn iadd(x: &mut Vec, y: &[Limb]) { - iadd_impl(x, y, 0) + iadd_impl(x, y, 0); } /// Add bigint to bigint. @@ -859,13 +858,13 @@ pub(crate) trait Math: Clone + Sized + Default { /// Multiply by a power of 2. #[inline] fn imul_pow2(&mut self, n: u32) { - self.ishl(n as usize) + self.ishl(n as usize); } /// Multiply by a power of 5. #[inline] fn imul_pow5(&mut self, n: u32) { - small::imul_pow5(self.data_mut(), n) + small::imul_pow5(self.data_mut(), n); } /// MulAssign by a power of 10. diff --git a/src/lexical/mod.rs b/src/lexical/mod.rs index b1a45e218..aeed4065e 100644 --- a/src/lexical/mod.rs +++ b/src/lexical/mod.rs @@ -28,10 +28,10 @@ pub(crate) mod rounding; mod shift; mod small_powers; -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] mod large_powers32; -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] mod large_powers64; // API diff --git a/src/lexical/num.rs b/src/lexical/num.rs index 27c78edf9..3f3914021 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -2,7 +2,7 @@ //! Utilities for Rust numbers. -use crate::lib::ops; +use core::ops; /// Precalculated values of radix**i for i in range [0, arr.len()-1]. /// Each value can be **exactly** represented as that type. @@ -206,8 +206,6 @@ pub trait Float: Number { // MASKS - /// Bitmask for the sign bit. - const SIGN_MASK: Self::Unsigned; /// Bitmask for the exponent, including the hidden bit. const EXPONENT_MASK: Self::Unsigned; /// Bitmask for the hidden bit in exponent, which is an implicit 1 in the fraction. @@ -219,11 +217,9 @@ pub trait Float: Number { /// Positive infinity as bits. const INFINITY_BITS: Self::Unsigned; - /// Positive infinity as bits. - const NEGATIVE_INFINITY_BITS: Self::Unsigned; /// Size of the significand (mantissa) without hidden bit. const MANTISSA_SIZE: i32; - /// Bias of the exponet + /// Bias of the exponent const EXPONENT_BIAS: i32; /// Exponent portion of a denormal float. const DENORMAL_EXPONENT: i32; @@ -248,7 +244,6 @@ pub trait Float: Number { fn from_bits(u: Self::Unsigned) -> Self; fn to_bits(self) -> Self::Unsigned; fn is_sign_positive(self) -> bool; - fn is_sign_negative(self) -> bool; /// Returns true if the float is a denormal. #[inline] @@ -316,12 +311,10 @@ impl Float for f32 { const ZERO: f32 = 0.0; const MAX_DIGITS: usize = 114; - const SIGN_MASK: u32 = 0x80000000; const EXPONENT_MASK: u32 = 0x7F800000; const HIDDEN_BIT_MASK: u32 = 0x00800000; const MANTISSA_MASK: u32 = 0x007FFFFF; const INFINITY_BITS: u32 = 0x7F800000; - const NEGATIVE_INFINITY_BITS: u32 = Self::INFINITY_BITS | Self::SIGN_MASK; const MANTISSA_SIZE: i32 = 23; const EXPONENT_BIAS: i32 = 127 + Self::MANTISSA_SIZE; const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; @@ -368,11 +361,6 @@ impl Float for f32 { fn is_sign_positive(self) -> bool { f32::is_sign_positive(self) } - - #[inline] - fn is_sign_negative(self) -> bool { - f32::is_sign_negative(self) - } } impl Float for f64 { @@ -380,12 +368,10 @@ impl Float for f64 { const ZERO: f64 = 0.0; const MAX_DIGITS: usize = 769; - const SIGN_MASK: u64 = 0x8000000000000000; const EXPONENT_MASK: u64 = 0x7FF0000000000000; const HIDDEN_BIT_MASK: u64 = 0x0010000000000000; const MANTISSA_MASK: u64 = 0x000FFFFFFFFFFFFF; const INFINITY_BITS: u64 = 0x7FF0000000000000; - const NEGATIVE_INFINITY_BITS: u64 = Self::INFINITY_BITS | Self::SIGN_MASK; const MANTISSA_SIZE: i32 = 52; const EXPONENT_BIAS: i32 = 1023 + Self::MANTISSA_SIZE; const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; @@ -432,9 +418,4 @@ impl Float for f64 { fn is_sign_positive(self) -> bool { f64::is_sign_positive(self) } - - #[inline] - fn is_sign_negative(self) -> bool { - f64::is_sign_negative(self) - } } diff --git a/src/lexical/rounding.rs b/src/lexical/rounding.rs index d2704c92c..634487522 100644 --- a/src/lexical/rounding.rs +++ b/src/lexical/rounding.rs @@ -5,7 +5,7 @@ use super::float::ExtendedFloat; use super::num::*; use super::shift::*; -use crate::lib::mem; +use core::mem; // MASKS @@ -25,7 +25,7 @@ pub(crate) fn lower_n_mask(n: u64) -> u64 { debug_assert!(n <= bits, "lower_n_mask() overflow in shl."); if n == bits { - u64::max_value() + u64::MAX } else { (1 << n) - 1 } diff --git a/src/lexical/shift.rs b/src/lexical/shift.rs index b0bd469f4..a0bae01e0 100644 --- a/src/lexical/shift.rs +++ b/src/lexical/shift.rs @@ -3,7 +3,7 @@ //! Bit-shift helpers. use super::float::ExtendedFloat; -use crate::lib::mem; +use core::mem; // Shift extended-precision float right `shift` bytes. #[inline] diff --git a/src/lexical/small_powers.rs b/src/lexical/small_powers.rs index 219d82611..29b83a159 100644 --- a/src/lexical/small_powers.rs +++ b/src/lexical/small_powers.rs @@ -3,19 +3,19 @@ //! Pre-computed small powers. // 32 BIT -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub(crate) const POW5_32: [u32; 14] = [ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, ]; -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub(crate) const POW10_32: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // 64 BIT -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub(crate) const POW5_64: [u64; 28] = [ 1, 5, diff --git a/src/lib.rs b/src/lib.rs index 33612c616..f77fa73f4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,10 +55,9 @@ //! ``` //! //! A string of JSON data can be parsed into a `serde_json::Value` by the -//! [`serde_json::from_str`][from_str] function. There is also -//! [`from_slice`][from_slice] for parsing from a byte slice &[u8] and -//! [`from_reader`][from_reader] for parsing from any `io::Read` like a File or -//! a TCP stream. +//! [`serde_json::from_str`][from_str] function. There is also [`from_slice`] +//! for parsing from a byte slice `&[u8]` and [`from_reader`] for parsing from +//! any `io::Read` like a File or a TCP stream. //! //! ``` //! use serde_json::{Result, Value}; @@ -104,7 +103,7 @@ //! a JSON string to a Rust string with [`as_str()`] or avoiding the use of //! `Value` as described in the following section. //! -//! [`as_str()`]: https://docs.serde.rs/serde_json/enum.Value.html#method.as_str +//! [`as_str()`]: crate::Value::as_str //! //! The `Value` representation is sufficient for very basic tasks but can be //! tedious to work with for anything more significant. Error handling is @@ -227,10 +226,10 @@ //! }); //! ``` //! -//! This is amazingly convenient but we have the problem we had before with -//! `Value` which is that the IDE and Rust compiler cannot help us if we get it -//! wrong. Serde JSON provides a better way of serializing strongly-typed data -//! structures into JSON text. +//! This is amazingly convenient, but we have the problem we had before with +//! `Value`: the IDE and Rust compiler cannot help us if we get it wrong. Serde +//! JSON provides a better way of serializing strongly-typed data structures +//! into JSON text. //! //! # Creating JSON by serializing data structures //! @@ -279,8 +278,8 @@ //! # No-std support //! //! As long as there is a memory allocator, it is possible to use serde_json -//! without the rest of the Rust standard library. This is supported on Rust -//! 1.36+. Disable the default "std" feature and enable the "alloc" feature: +//! without the rest of the Rust standard library. Disable the default "std" +//! feature and enable the "alloc" feature: //! //! ```toml //! [dependencies] @@ -290,33 +289,38 @@ //! For JSON support in Serde without a memory allocator, please see the //! [`serde-json-core`] crate. //! -//! [value]: https://docs.serde.rs/serde_json/value/enum.Value.html -//! [from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html -//! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html -//! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html -//! [to_string]: https://docs.serde.rs/serde_json/ser/fn.to_string.html -//! [to_vec]: https://docs.serde.rs/serde_json/ser/fn.to_vec.html -//! [to_writer]: https://docs.serde.rs/serde_json/ser/fn.to_writer.html -//! [macro]: https://docs.serde.rs/serde_json/macro.json.html -//! [`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/ +//! [value]: crate::value::Value +//! [from_str]: crate::de::from_str +//! [from_slice]: crate::de::from_slice +//! [from_reader]: crate::de::from_reader +//! [to_string]: crate::ser::to_string +//! [to_vec]: crate::ser::to_vec +//! [to_writer]: crate::ser::to_writer +//! [macro]: crate::json +//! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.60")] -#![deny(clippy::all, clippy::pedantic)] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.143")] // Ignored clippy lints #![allow( + clippy::collapsible_else_if, clippy::comparison_chain, clippy::deprecated_cfg_attr, clippy::doc_markdown, + clippy::elidable_lifetime_names, clippy::excessive_precision, + clippy::explicit_auto_deref, clippy::float_cmp, clippy::manual_range_contains, clippy::match_like_matches_macro, clippy::match_single_binding, clippy::needless_doctest_main, + clippy::needless_late_init, + clippy::needless_lifetimes, + clippy::return_self_not_must_use, clippy::transmute_ptr_to_ptr, - clippy::unnecessary_wraps, - // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 - clippy::unnested_or_patterns, + clippy::unbuffered_bytes, + clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133 + clippy::unnecessary_wraps )] // Ignored clippy_pedantic lints #![allow( @@ -331,6 +335,7 @@ clippy::enum_glob_use, clippy::if_not_else, clippy::integer_division, + clippy::let_underscore_untyped, clippy::map_err_ignore, clippy::match_same_arms, clippy::similar_names, @@ -338,7 +343,9 @@ clippy::wildcard_imports, // things are often more readable this way clippy::cast_lossless, + clippy::items_after_statements, clippy::module_name_repetitions, + clippy::redundant_else, clippy::shadow_unrelated, clippy::single_match_else, clippy::too_many_lines, @@ -353,70 +360,33 @@ clippy::missing_errors_doc, clippy::must_use_candidate, )] +// Restrictions +#![deny(clippy::question_mark_used)] #![allow(non_upper_case_globals)] #![deny(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![allow(unknown_lints, mismatched_lifetime_syntaxes)] -//////////////////////////////////////////////////////////////////////////////// +#[cfg(not(any(feature = "std", feature = "alloc")))] +compile_error! { + "serde_json requires that either `std` (default) or `alloc` feature is enabled" +} -#[cfg(not(feature = "std"))] extern crate alloc; -/// A facade around all the types we need from the `std`, `core`, and `alloc` -/// crates. This avoids elaborate import wrangling having to happen in every -/// module. -mod lib { - mod core { - #[cfg(not(feature = "std"))] - pub use core::*; - #[cfg(feature = "std")] - pub use std::*; - } - - pub use self::core::cell::{Cell, RefCell}; - pub use self::core::clone::{self, Clone}; - pub use self::core::convert::{self, From, Into}; - pub use self::core::default::{self, Default}; - pub use self::core::fmt::{self, Debug, Display}; - pub use self::core::hash::{self, Hash}; - pub use self::core::iter::FusedIterator; - pub use self::core::marker::{self, PhantomData}; - pub use self::core::ops::{Bound, RangeBounds}; - pub use self::core::result::{self, Result}; - pub use self::core::{borrow, char, cmp, iter, mem, num, ops, slice, str}; - - #[cfg(not(feature = "std"))] - pub use alloc::borrow::{Cow, ToOwned}; - #[cfg(feature = "std")] - pub use std::borrow::{Cow, ToOwned}; - - #[cfg(not(feature = "std"))] - pub use alloc::string::{String, ToString}; - #[cfg(feature = "std")] - pub use std::string::{String, ToString}; - - #[cfg(not(feature = "std"))] - pub use alloc::vec::{self, Vec}; - #[cfg(feature = "std")] - pub use std::vec::{self, Vec}; - - #[cfg(not(feature = "std"))] - pub use alloc::boxed::Box; - #[cfg(feature = "std")] - pub use std::boxed::Box; - - #[cfg(not(feature = "std"))] - pub use alloc::collections::{btree_map, BTreeMap}; - #[cfg(feature = "std")] - pub use std::collections::{btree_map, BTreeMap}; +#[cfg(feature = "std")] +extern crate std; - #[cfg(feature = "std")] - pub use std::error; +// Not public API. Used from macro-generated code. +#[doc(hidden)] +pub mod __private { + #[doc(hidden)] + pub use alloc::vec; } -//////////////////////////////////////////////////////////////////////////////// - #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::de::from_reader; #[doc(inline)] @@ -426,6 +396,7 @@ pub use crate::error::{Error, Result}; #[doc(inline)] pub use crate::ser::{to_string, to_string_pretty, to_vec, to_vec_pretty}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::ser::{to_writer, to_writer_pretty, Serializer}; #[doc(inline)] @@ -434,15 +405,12 @@ pub use crate::value::{from_value, to_value, Map, Number, Value}; // We only use our own error type; no need for From conversions provided by the // standard library's try! macro. This reduces lines of LLVM IR by 4%. macro_rules! tri { - ($e:expr) => { + ($e:expr $(,)?) => { match $e { - crate::lib::Result::Ok(val) => val, - crate::lib::Result::Err(err) => return crate::lib::Result::Err(err), + core::result::Result::Ok(val) => val, + core::result::Result::Err(err) => return core::result::Result::Err(err), } }; - ($e:expr,) => { - tri!($e) - }; } #[macro_use] @@ -452,13 +420,12 @@ pub mod de; pub mod error; pub mod map; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub mod ser; #[cfg(not(feature = "std"))] mod ser; pub mod value; -mod features_check; - mod io; #[cfg(feature = "std")] mod iter; diff --git a/src/macros.rs b/src/macros.rs index 5287998b4..c47bdf929 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -10,7 +10,8 @@ /// "features": [ /// "serde", /// "json" -/// ] +/// ], +/// "homepage": null /// } /// }); /// ``` @@ -49,11 +50,11 @@ /// "comma -->", /// ]); /// ``` -#[macro_export(local_inner_macros)] +#[macro_export] macro_rules! json { // Hide distracting implementation details from the generated rustdoc. ($($json:tt)+) => { - json_internal!($($json)+) + $crate::json_internal!($($json)+) }; } @@ -64,7 +65,7 @@ macro_rules! json { // // Changes are fine as long as `json_internal!` does not call any new helper // macros and can still be invoked as `json_internal!($($json)+)`. -#[macro_export(local_inner_macros)] +#[macro_export] #[doc(hidden)] macro_rules! json_internal { ////////////////////////////////////////////////////////////////////////// @@ -76,57 +77,57 @@ macro_rules! json_internal { // Done with trailing comma. (@array [$($elems:expr,)*]) => { - json_internal_vec![$($elems,)*] + $crate::__private::vec![$($elems,)*] }; // Done without trailing comma. (@array [$($elems:expr),*]) => { - json_internal_vec![$($elems),*] + $crate::__private::vec![$($elems),*] }; // Next element is `null`. (@array [$($elems:expr,)*] null $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(null)] $($rest)*) }; // Next element is `true`. (@array [$($elems:expr,)*] true $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(true)] $($rest)*) }; // Next element is `false`. (@array [$($elems:expr,)*] false $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(false)] $($rest)*) }; // Next element is an array. (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!([$($array)*])] $($rest)*) }; // Next element is a map. (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!({$($map)*})] $($rest)*) }; // Next element is an expression followed by comma. (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!($next),] $($rest)*) }; // Last element is an expression with no trailing comma. (@array [$($elems:expr,)*] $last:expr) => { - json_internal!(@array [$($elems,)* json_internal!($last)]) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!($last)]) }; // Comma after the most recent element. (@array [$($elems:expr),*] , $($rest:tt)*) => { - json_internal!(@array [$($elems,)*] $($rest)*) + $crate::json_internal!(@array [$($elems,)*] $($rest)*) }; // Unexpected token after most recent element. (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { - json_unexpected!($unexpected) + $crate::json_unexpected!($unexpected) }; ////////////////////////////////////////////////////////////////////////// @@ -145,12 +146,12 @@ macro_rules! json_internal { // Insert the current entry followed by trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { let _ = $object.insert(($($key)+).into(), $value); - json_internal!(@object $object () ($($rest)*) ($($rest)*)); + $crate::json_internal!(@object $object () ($($rest)*) ($($rest)*)); }; // Current entry followed by unexpected token. (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { - json_unexpected!($unexpected); + $crate::json_unexpected!($unexpected); }; // Insert the last entry without trailing comma. @@ -160,78 +161,78 @@ macro_rules! json_internal { // Next value is `null`. (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(null)) $($rest)*); }; // Next value is `true`. (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(true)) $($rest)*); }; // Next value is `false`. (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(false)) $($rest)*); }; // Next value is an array. (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!([$($array)*])) $($rest)*); }; // Next value is a map. (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!({$($map)*})) $($rest)*); }; // Next value is an expression followed by comma. (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!($value)) , $($rest)*); }; // Last value is an expression with no trailing comma. (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!($value))); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!($value))); }; // Missing value for last entry. Trigger a reasonable error message. (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { // "unexpected end of macro invocation" - json_internal!(); + $crate::json_internal!(); }; // Missing colon and value for last entry. Trigger a reasonable error // message. (@object $object:ident ($($key:tt)+) () $copy:tt) => { // "unexpected end of macro invocation" - json_internal!(); + $crate::json_internal!(); }; // Misplaced colon. Trigger a reasonable error message. (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `:`". - json_unexpected!($colon); + $crate::json_unexpected!($colon); }; // Found a comma inside a key. Trigger a reasonable error message. (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `,`". - json_unexpected!($comma); + $crate::json_unexpected!($comma); }; // Key is fully parenthesized. This avoids clippy double_parens false // positives because the parenthesization may be necessary here. (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); + $crate::json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); }; // Refuse to absorb colon token into key expression. (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { - json_expect_expr_comma!($($unexpected)+); + $crate::json_expect_expr_comma!($($unexpected)+); }; // Munch a token into the current key. (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); + $crate::json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); }; ////////////////////////////////////////////////////////////////////////// @@ -253,11 +254,11 @@ macro_rules! json_internal { }; ([]) => { - $crate::Value::Array(json_internal_vec![]) + $crate::Value::Array($crate::__private::vec![]) }; ([ $($tt:tt)+ ]) => { - $crate::Value::Array(json_internal!(@array [] $($tt)+)) + $crate::Value::Array($crate::json_internal!(@array [] $($tt)+)) }; ({}) => { @@ -267,7 +268,7 @@ macro_rules! json_internal { ({ $($tt:tt)+ }) => { $crate::Value::Object({ let mut object = $crate::Map::new(); - json_internal!(@object object () ($($tt)+) ($($tt)+)); + $crate::json_internal!(@object object () ($($tt)+) ($($tt)+)); object }) }; @@ -279,9 +280,8 @@ macro_rules! json_internal { }; } -// The json_internal macro above cannot invoke vec directly because it uses -// local_inner_macros. A vec invocation there would resolve to $crate::vec. -// Instead invoke vec here outside of local_inner_macros. +// Used by old versions of Rocket. +// Unused since https://github.com/rwf2/Rocket/commit/c74bcfd40a47b35330db6cafb88e4f3da83e0d17 #[macro_export] #[doc(hidden)] macro_rules! json_internal_vec { diff --git a/src/map.rs b/src/map.rs index f09d84059..f91e70b24 100644 --- a/src/map.rs +++ b/src/map.rs @@ -3,17 +3,27 @@ //! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order` //! feature of serde_json to use [`IndexMap`] instead. //! -//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html -//! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html +//! [`BTreeMap`]: std::collections::BTreeMap +//! [`IndexMap`]: indexmap::IndexMap -use crate::lib::borrow::Borrow; -use crate::lib::iter::FromIterator; -use crate::lib::*; +use crate::error::Error; use crate::value::Value; +use alloc::string::String; +#[cfg(feature = "preserve_order")] +use alloc::vec::Vec; +use core::borrow::Borrow; +use core::fmt::{self, Debug}; +use core::hash::{Hash, Hasher}; +use core::iter::FusedIterator; +#[cfg(feature = "preserve_order")] +use core::mem; +use core::ops; use serde::de; +#[cfg(not(feature = "preserve_order"))] +use alloc::collections::{btree_map, BTreeMap}; #[cfg(feature = "preserve_order")] -use indexmap::{self, IndexMap}; +use indexmap::IndexMap; /// Represents a JSON key/value type. pub struct Map { @@ -52,7 +62,7 @@ impl Map { /// Clears the map, removing all values. #[inline] pub fn clear(&mut self) { - self.map.clear() + self.map.clear(); } /// Returns a reference to the value corresponding to the key. @@ -94,6 +104,19 @@ impl Map { self.map.get_mut(key) } + /// Returns the key-value pair matching the given key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + #[inline] + pub fn get_key_value(&self, key: &Q) -> Option<(&String, &Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.get_key_value(key) + } + /// Inserts a key-value pair into the map. /// /// If the map did not have this key present, `None` is returned. @@ -105,11 +128,30 @@ impl Map { self.map.insert(k, v) } + /// Insert a key-value pair in the map at the given index. + /// + /// If the map did not have this key present, `None` is returned. + /// + /// If the map did have this key present, the key is moved to the new + /// position, the value is updated, and the old value is returned. + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_insert(&mut self, index: usize, k: String, v: Value) -> Option { + self.map.shift_insert(index, k, v) + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove(key)` is + /// equivalent to [`.swap_remove(key)`][Self::swap_remove], replacing this + /// entry's position with the last element. If you need to preserve the + /// relative order of the keys in the map, use + /// [`.shift_remove(key)`][Self::shift_remove] instead. #[inline] pub fn remove(&mut self, key: &Q) -> Option where @@ -117,7 +159,7 @@ impl Map { Q: ?Sized + Ord + Eq + Hash, { #[cfg(feature = "preserve_order")] - return self.map.swap_remove(key); + return self.swap_remove(key); #[cfg(not(feature = "preserve_order"))] return self.map.remove(key); } @@ -127,56 +169,102 @@ impl Map { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove_entry(key)` is + /// equivalent to [`.swap_remove_entry(key)`][Self::swap_remove_entry], + /// replacing this entry's position with the last element. If you need to + /// preserve the relative order of the keys in the map, use + /// [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead. + #[inline] pub fn remove_entry(&mut self, key: &Q) -> Option<(String, Value)> where String: Borrow, Q: ?Sized + Ord + Eq + Hash, { - #[cfg(any(feature = "preserve_order", not(no_btreemap_remove_entry)))] + #[cfg(feature = "preserve_order")] + return self.swap_remove_entry(key); + #[cfg(not(feature = "preserve_order"))] return self.map.remove_entry(key); - #[cfg(all( - not(feature = "preserve_order"), - no_btreemap_remove_entry, - not(no_btreemap_get_key_value), - ))] - { - let (key, _value) = self.map.get_key_value(key)?; - let key = key.clone(); - let value = self.map.remove::(&key)?; - Some((key, value)) - } - #[cfg(all( - not(feature = "preserve_order"), - no_btreemap_remove_entry, - no_btreemap_get_key_value, - ))] - { - struct Key<'a, Q: ?Sized>(&'a Q); + } - impl<'a, Q: ?Sized> RangeBounds for Key<'a, Q> { - fn start_bound(&self) -> Bound<&Q> { - Bound::Included(self.0) - } - fn end_bound(&self) -> Bound<&Q> { - Bound::Included(self.0) - } - } + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove(key) + } - let mut range = self.map.range(Key(key)); - let (key, _value) = range.next()?; - let key = key.clone(); - let value = self.map.remove::(&key)?; - Some((key, value)) - } + /// Remove and return the key-value pair. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove_entry(key) + } + + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove(key) } - /// Moves all elements from other into Self, leaving other empty. + /// Remove and return the key-value pair. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove_entry(key) + } + + /// Moves all elements from other into self, leaving other empty. #[inline] pub fn append(&mut self, other: &mut Self) { #[cfg(feature = "preserve_order")] - for (k, v) in mem::replace(&mut other.map, MapImpl::default()) { - self.map.insert(k, v); - } + self.map + .extend(mem::replace(&mut other.map, MapImpl::default())); #[cfg(not(feature = "preserve_order"))] self.map.append(&mut other.map); } @@ -188,7 +276,7 @@ impl Map { S: Into, { #[cfg(not(feature = "preserve_order"))] - use crate::lib::btree_map::Entry as EntryImpl; + use alloc::collections::btree_map::Entry as EntryImpl; #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; @@ -249,8 +337,52 @@ impl Map { iter: self.map.values_mut(), } } + + /// Gets an iterator over the values of the map. + #[inline] + pub fn into_values(self) -> IntoValues { + IntoValues { + iter: self.map.into_values(), + } + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` + /// returns `false`. + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&String, &mut Value) -> bool, + { + self.map.retain(f); + } + + /// Sorts this map's entries in-place using `str`'s usual ordering. + /// + /// If serde_json's "preserve_order" feature is not enabled, this method + /// does no work because all JSON maps are always kept in a sorted state. + /// + /// If serde_json's "preserve_order" feature is enabled, this method + /// destroys the original source order or insertion order of this map in + /// favor of an alphanumerical order that matches how a BTreeMap with the + /// same contents would be ordered. This takes **O(n log n + c)** time where + /// _n_ is the length of the map and _c_ is the capacity. + /// + /// Other maps nested within the values of this map are not sorted. If you + /// need the entire data structure to be sorted at all levels, you must also + /// call + /// map.[values_mut]\().for_each([Value::sort_all_objects]). + /// + /// [values_mut]: Map::values_mut + #[inline] + pub fn sort_keys(&mut self) { + #[cfg(feature = "preserve_order")] + self.map.sort_unstable_keys(); + } } +#[allow(clippy::derivable_impls)] // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7655 impl Default for Map { #[inline] fn default() -> Self { @@ -267,6 +399,11 @@ impl Clone for Map { map: self.map.clone(), } } + + #[inline] + fn clone_from(&mut self, source: &Self) { + self.map.clone_from(&source.map); + } } impl PartialEq for Map { @@ -278,6 +415,22 @@ impl PartialEq for Map { impl Eq for Map {} +impl Hash for Map { + fn hash(&self, state: &mut H) { + #[cfg(not(feature = "preserve_order"))] + { + self.map.hash(state); + } + + #[cfg(feature = "preserve_order")] + { + let mut kv = Vec::from_iter(&self.map); + kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); + kv.hash(state); + } + } +} + /// Access an element of this map. Panics if the given key is not present in the /// map. /// @@ -286,15 +439,15 @@ impl Eq for Map {} /// # /// # let val = &Value::String("".to_owned()); /// # let _ = -/// match *val { -/// Value::String(ref s) => Some(s.as_str()), -/// Value::Array(ref arr) => arr[0].as_str(), -/// Value::Object(ref map) => map["type"].as_str(), +/// match val { +/// Value::String(s) => Some(s.as_str()), +/// Value::Array(arr) => arr[0].as_str(), +/// Value::Object(map) => map["type"].as_str(), /// _ => None, /// } /// # ; /// ``` -impl<'a, Q> ops::Index<&'a Q> for Map +impl ops::Index<&Q> for Map where String: Borrow, Q: ?Sized + Ord + Eq + Hash, @@ -317,7 +470,7 @@ where /// # /// map["key"] = json!("value"); /// ``` -impl<'a, Q> ops::IndexMut<&'a Q> for Map +impl ops::IndexMut<&Q> for Map where String: Borrow, Q: ?Sized + Ord + Eq + Hash, @@ -445,13 +598,28 @@ macro_rules! delegate_iterator { } } +impl<'de> de::IntoDeserializer<'de, Error> for Map { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> de::IntoDeserializer<'de, Error> for &'de Map { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + ////////////////////////////////////////////////////////////////////////////// /// A view into a single entry in a map, which may either be vacant or occupied. /// This enum is constructed from the [`entry`] method on [`Map`]. /// -/// [`entry`]: struct.Map.html#method.entry -/// [`Map`]: struct.Map.html +/// [`entry`]: Map::entry pub enum Entry<'a> { /// A vacant Entry. Vacant(VacantEntry<'a>), @@ -460,15 +628,11 @@ pub enum Entry<'a> { } /// A vacant Entry. It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html pub struct VacantEntry<'a> { vacant: VacantEntryImpl<'a>, } /// An occupied Entry. It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html pub struct OccupiedEntry<'a> { occupied: OccupiedEntryImpl<'a>, } @@ -493,9 +657,9 @@ impl<'a> Entry<'a> { /// assert_eq!(map.entry("serde").key(), &"serde"); /// ``` pub fn key(&self) -> &String { - match *self { - Entry::Vacant(ref e) => e.key(), - Entry::Occupied(ref e) => e.key(), + match self { + Entry::Vacant(e) => e.key(), + Entry::Occupied(e) => e.key(), } } @@ -542,6 +706,40 @@ impl<'a> Entry<'a> { Entry::Occupied(entry) => entry.into_mut(), } } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// # use serde_json::json; + /// # + /// let mut map = serde_json::Map::new(); + /// map.entry("serde") + /// .and_modify(|e| *e = json!("rust")) + /// .or_insert(json!("cpp")); + /// + /// assert_eq!(map["serde"], "cpp"); + /// + /// map.entry("serde") + /// .and_modify(|e| *e = json!("rust")) + /// .or_insert(json!("cpp")); + /// + /// assert_eq!(map["serde"], "rust"); + /// ``` + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut Value), + { + match self { + Entry::Occupied(mut entry) => { + f(entry.get_mut()); + Entry::Occupied(entry) + } + Entry::Vacant(entry) => Entry::Vacant(entry), + } + } } impl<'a> VacantEntry<'a> { @@ -721,6 +919,12 @@ impl<'a> OccupiedEntry<'a> { /// Takes the value of the entry out of the map, and returns it. /// + /// If serde_json's "preserve_order" is enabled, `.remove()` is + /// equivalent to [`.swap_remove()`][Self::swap_remove], replacing this + /// entry's position with the last element. If you need to preserve the + /// relative order of the keys in the map, use + /// [`.shift_remove()`][Self::shift_remove] instead. + /// /// # Examples /// /// ``` @@ -741,10 +945,101 @@ impl<'a> OccupiedEntry<'a> { #[inline] pub fn remove(self) -> Value { #[cfg(feature = "preserve_order")] - return self.occupied.swap_remove(); + return self.swap_remove(); #[cfg(not(feature = "preserve_order"))] return self.occupied.remove(); } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove(self) -> Value { + self.occupied.swap_remove() + } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove(self) -> Value { + self.occupied.shift_remove() + } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// If serde_json's "preserve_order" is enabled, `.remove_entry()` is + /// equivalent to [`.swap_remove_entry()`][Self::swap_remove_entry], + /// replacing this entry's position with the last element. If you need to + /// preserve the relative order of the keys in the map, use + /// [`.shift_remove_entry()`][Self::shift_remove_entry] instead. + /// + /// # Examples + /// + /// ``` + /// # use serde_json::json; + /// # + /// use serde_json::map::Entry; + /// + /// let mut map = serde_json::Map::new(); + /// map.insert("serde".to_owned(), json!(12)); + /// + /// match map.entry("serde") { + /// Entry::Occupied(occupied) => { + /// let (key, value) = occupied.remove_entry(); + /// assert_eq!(key, "serde"); + /// assert_eq!(value, 12); + /// } + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn remove_entry(self) -> (String, Value) { + #[cfg(feature = "preserve_order")] + return self.swap_remove_entry(); + #[cfg(not(feature = "preserve_order"))] + return self.occupied.remove_entry(); + } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove_entry(self) -> (String, Value) { + self.occupied.swap_remove_entry() + } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove_entry(self) -> (String, Value) { + self.occupied.shift_remove_entry() + } } ////////////////////////////////////////////////////////////////////////////// @@ -761,6 +1056,7 @@ impl<'a> IntoIterator for &'a Map { } /// An iterator over a serde_json::Map's entries. +#[derive(Clone, Debug)] pub struct Iter<'a> { iter: IterImpl<'a>, } @@ -786,6 +1082,7 @@ impl<'a> IntoIterator for &'a mut Map { } /// A mutable iterator over a serde_json::Map's entries. +#[derive(Debug)] pub struct IterMut<'a> { iter: IterMutImpl<'a>, } @@ -811,6 +1108,7 @@ impl IntoIterator for Map { } /// An owning iterator over a serde_json::Map's entries. +#[derive(Debug)] pub struct IntoIter { iter: IntoIterImpl, } @@ -825,6 +1123,7 @@ delegate_iterator!((IntoIter) => (String, Value)); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a serde_json::Map's keys. +#[derive(Clone, Debug)] pub struct Keys<'a> { iter: KeysImpl<'a>, } @@ -839,6 +1138,7 @@ delegate_iterator!((Keys<'a>) => &'a String); ////////////////////////////////////////////////////////////////////////////// /// An iterator over a serde_json::Map's values. +#[derive(Clone, Debug)] pub struct Values<'a> { iter: ValuesImpl<'a>, } @@ -853,6 +1153,7 @@ delegate_iterator!((Values<'a>) => &'a Value); ////////////////////////////////////////////////////////////////////////////// /// A mutable iterator over a serde_json::Map's values. +#[derive(Debug)] pub struct ValuesMut<'a> { iter: ValuesMutImpl<'a>, } @@ -863,3 +1164,18 @@ type ValuesMutImpl<'a> = btree_map::ValuesMut<'a, String, Value>; type ValuesMutImpl<'a> = indexmap::map::ValuesMut<'a, String, Value>; delegate_iterator!((ValuesMut<'a>) => &'a mut Value); + +////////////////////////////////////////////////////////////////////////////// + +/// An owning iterator over a serde_json::Map's values. +#[derive(Debug)] +pub struct IntoValues { + iter: IntoValuesImpl, +} + +#[cfg(not(feature = "preserve_order"))] +type IntoValuesImpl = btree_map::IntoValues; +#[cfg(feature = "preserve_order")] +type IntoValuesImpl = indexmap::map::IntoValues; + +delegate_iterator!((IntoValues) => Value); diff --git a/src/number.rs b/src/number.rs index c1476189b..8cb0c3b7b 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,28 +1,30 @@ use crate::de::ParserNumber; use crate::error::Error; -use crate::lib::*; -use serde::de::{self, Unexpected, Visitor}; -use serde::{ - forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, - Serializer, -}; - #[cfg(feature = "arbitrary_precision")] use crate::error::ErrorCode; #[cfg(feature = "arbitrary_precision")] +use alloc::borrow::ToOwned; +#[cfg(feature = "arbitrary_precision")] +use alloc::string::{String, ToString}; +use core::fmt::{self, Debug, Display}; +#[cfg(not(feature = "arbitrary_precision"))] +use core::hash::{Hash, Hasher}; +use serde::de::{self, Unexpected, Visitor}; +#[cfg(feature = "arbitrary_precision")] use serde::de::{IntoDeserializer, MapAccess}; +use serde::{forward_to_deserialize_any, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; /// Represents a JSON number, whether integer or floating point. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Number { n: N, } #[cfg(not(feature = "arbitrary_precision"))] -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone)] enum N { PosInt(u64), /// Always less than zero. @@ -31,10 +33,42 @@ enum N { Float(f64), } +#[cfg(not(feature = "arbitrary_precision"))] +impl PartialEq for N { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (N::PosInt(a), N::PosInt(b)) => a == b, + (N::NegInt(a), N::NegInt(b)) => a == b, + (N::Float(a), N::Float(b)) => a == b, + _ => false, + } + } +} + // Implementing Eq is fine since any float values are always finite. #[cfg(not(feature = "arbitrary_precision"))] impl Eq for N {} +#[cfg(not(feature = "arbitrary_precision"))] +impl Hash for N { + fn hash(&self, h: &mut H) { + match *self { + N::PosInt(i) => i.hash(h), + N::NegInt(i) => i.hash(h), + N::Float(f) => { + if f == 0.0f64 { + // There are 2 zero representations, +0 and -0, which + // compare equal but have different bits. We use the +0 hash + // for both so that hash(+0) == hash(-0). + 0.0f64.to_bits().hash(h); + } else { + f.to_bits().hash(h); + } + } + } + } +} + #[cfg(feature = "arbitrary_precision")] type N = String; @@ -44,26 +78,10 @@ impl Number { /// /// For any Number on which `is_i64` returns true, `as_i64` is guaranteed to /// return the integer value. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let big = i64::max_value() as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); - /// - /// assert!(v["a"].is_i64()); - /// - /// // Greater than i64::MAX. - /// assert!(!v["b"].is_i64()); - /// - /// // Numbers with a decimal point are not considered integers. - /// assert!(!v["c"].is_i64()); - /// ``` - #[inline] pub fn is_i64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { - N::PosInt(v) => v <= i64::max_value() as u64, + N::PosInt(v) => v <= i64::MAX as u64, N::NegInt(_) => true, N::Float(_) => false, } @@ -75,21 +93,6 @@ impl Number { /// /// For any Number on which `is_u64` returns true, `as_u64` is guaranteed to /// return the integer value. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": -64, "c": 256.0 }); - /// - /// assert!(v["a"].is_u64()); - /// - /// // Negative integer. - /// assert!(!v["b"].is_u64()); - /// - /// // Numbers with a decimal point are not considered integers. - /// assert!(!v["c"].is_u64()); - /// ``` - #[inline] pub fn is_u64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -107,19 +110,6 @@ impl Number { /// /// Currently this function returns true if and only if both `is_i64` and /// `is_u64` return false but this is not a guarantee in the future. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 256.0, "b": 64, "c": -64 }); - /// - /// assert!(v["a"].is_f64()); - /// - /// // Integers. - /// assert!(!v["b"].is_f64()); - /// assert!(!v["c"].is_f64()); - /// ``` - #[inline] pub fn is_f64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -130,7 +120,7 @@ impl Number { { for c in self.n.chars() { if c == '.' || c == 'e' || c == 'E' { - return self.n.parse::().ok().map_or(false, |f| f.is_finite()); + return self.n.parse::().ok().map_or(false, f64::is_finite); } } false @@ -139,23 +129,11 @@ impl Number { /// If the `Number` is an integer, represent it as i64 if possible. Returns /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let big = i64::max_value() as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); - /// - /// assert_eq!(v["a"].as_i64(), Some(64)); - /// assert_eq!(v["b"].as_i64(), None); - /// assert_eq!(v["c"].as_i64(), None); - /// ``` - #[inline] pub fn as_i64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { N::PosInt(n) => { - if n <= i64::max_value() as u64 { + if n <= i64::MAX as u64 { Some(n as i64) } else { None @@ -170,17 +148,6 @@ impl Number { /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": -64, "c": 256.0 }); - /// - /// assert_eq!(v["a"].as_u64(), Some(64)); - /// assert_eq!(v["b"].as_u64(), None); - /// assert_eq!(v["c"].as_u64(), None); - /// ``` - #[inline] pub fn as_u64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -192,17 +159,6 @@ impl Number { } /// Represents the number as f64 if possible. Returns None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 256.0, "b": 64, "c": -64 }); - /// - /// assert_eq!(v["a"].as_f64(), Some(256.0)); - /// assert_eq!(v["b"].as_f64(), Some(64.0)); - /// assert_eq!(v["c"].as_f64(), Some(-64.0)); - /// ``` - #[inline] pub fn as_f64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -218,15 +174,12 @@ impl Number { /// numbers. /// /// ``` - /// # use std::f64; - /// # /// # use serde_json::Number; /// # /// assert!(Number::from_f64(256.0).is_some()); /// /// assert!(Number::from_f64(f64::NAN).is_none()); /// ``` - #[inline] pub fn from_f64(f: f64) -> Option { if f.is_finite() { let n = { @@ -245,6 +198,143 @@ impl Number { } } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + pub fn as_i128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as i128), + N::NegInt(n) => Some(n as i128), + N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + + /// If the `Number` is an integer, represent it as u128 if possible. Returns + /// None otherwise. + pub fn as_u128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as u128), + N::NegInt(_) | N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + + /// Converts an `i128` to a `Number`. Numbers smaller than i64::MIN or + /// larger than u64::MAX can only be represented in `Number` if serde_json's + /// "arbitrary_precision" feature is enabled. + /// + /// ``` + /// # use serde_json::Number; + /// # + /// assert!(Number::from_i128(256).is_some()); + /// ``` + pub fn from_i128(i: i128) -> Option { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(u) = u64::try_from(i) { + N::PosInt(u) + } else if let Ok(i) = i64::try_from(i) { + N::NegInt(i) + } else { + return None; + } + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) + } + + /// Converts a `u128` to a `Number`. Numbers greater than u64::MAX can only + /// be represented in `Number` if serde_json's "arbitrary_precision" feature + /// is enabled. + /// + /// ``` + /// # use serde_json::Number; + /// # + /// assert!(Number::from_u128(256).is_some()); + /// ``` + pub fn from_u128(i: u128) -> Option { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(u) = u64::try_from(i) { + N::PosInt(u) + } else { + return None; + } + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) + } + + /// Returns the exact original JSON representation that this Number was + /// parsed from. + /// + /// For numbers constructed not via parsing, such as by `From`, returns + /// the JSON representation that serde\_json would serialize for this + /// number. + /// + /// ``` + /// # use serde_json::Number; + /// for value in [ + /// "7", + /// "12.34", + /// "34e-56789", + /// "0.0123456789000000012345678900000001234567890000123456789", + /// "343412345678910111213141516171819202122232425262728293034", + /// "-343412345678910111213141516171819202122232425262728293031", + /// ] { + /// let number: Number = serde_json::from_str(value).unwrap(); + /// assert_eq!(number.as_str(), value); + /// } + /// ``` + #[cfg(feature = "arbitrary_precision")] + #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary_precision")))] + pub fn as_str(&self) -> &str { + &self.n + } + + pub(crate) fn as_f32(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as f32), + N::NegInt(n) => Some(n as f32), + N::Float(n) => Some(n as f32), + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse::().ok().filter(|float| float.is_finite()) + } + + pub(crate) fn from_f32(f: f32) -> Option { + if f.is_finite() { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::Float(f as f64) + } + #[cfg(feature = "arbitrary_precision")] + { + ryu::Buffer::new().format_finite(f).to_owned() + } + }; + Some(Number { n }) + } else { + None + } + } + #[cfg(feature = "arbitrary_precision")] /// Not public API. Only tests use this. #[doc(hidden)] @@ -254,13 +344,13 @@ impl Number { } } -impl fmt::Display for Number { +impl Display for Number { #[cfg(not(feature = "arbitrary_precision"))] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.n { - N::PosInt(u) => Display::fmt(&u, formatter), - N::NegInt(i) => Display::fmt(&i, formatter), - N::Float(f) => Display::fmt(&f, formatter), + N::PosInt(u) => formatter.write_str(itoa::Buffer::new().format(u)), + N::NegInt(i) => formatter.write_str(itoa::Buffer::new().format(i)), + N::Float(f) => formatter.write_str(ryu::Buffer::new().format_finite(f)), } } @@ -271,35 +361,13 @@ impl fmt::Display for Number { } impl Debug for Number { - #[cfg(not(feature = "arbitrary_precision"))] - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let mut debug = formatter.debug_tuple("Number"); - match self.n { - N::PosInt(i) => { - debug.field(&i); - } - N::NegInt(i) => { - debug.field(&i); - } - N::Float(f) => { - debug.field(&f); - } - } - debug.finish() - } - - #[cfg(feature = "arbitrary_precision")] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter - .debug_tuple("Number") - .field(&format_args!("{}", self.n)) - .finish() + write!(formatter, "Number({})", self) } } impl Serialize for Number { #[cfg(not(feature = "arbitrary_precision"))] - #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -312,15 +380,14 @@ impl Serialize for Number { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, { use serde::ser::SerializeStruct; - let mut s = serializer.serialize_struct(TOKEN, 1)?; - s.serialize_field(TOKEN, &self.n)?; + let mut s = tri!(serializer.serialize_struct(TOKEN, 1)); + tri!(s.serialize_field(TOKEN, &self.n)); s.end() } } @@ -340,17 +407,30 @@ impl<'de> Deserialize<'de> for Number { formatter.write_str("a JSON number") } - #[inline] fn visit_i64(self, value: i64) -> Result { Ok(value.into()) } - #[inline] + fn visit_i128(self, value: i128) -> Result + where + E: de::Error, + { + Number::from_i128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) + } + fn visit_u64(self, value: u64) -> Result { Ok(value.into()) } - #[inline] + fn visit_u128(self, value: u128) -> Result + where + E: de::Error, + { + Number::from_u128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) + } + fn visit_f64(self, value: f64) -> Result where E: de::Error, @@ -359,16 +439,15 @@ impl<'de> Deserialize<'de> for Number { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn visit_map(self, mut visitor: V) -> Result where V: de::MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } - let v: NumberFromString = visitor.next_value()?; + let v: NumberFromString = tri!(visitor.next_value()); Ok(v.value) } } @@ -407,7 +486,7 @@ impl<'de> de::Deserialize<'de> for NumberKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(NumberKey) } } @@ -453,7 +532,6 @@ fn invalid_number() -> Error { macro_rules! deserialize_any { (@expand [$($num_string:tt)*]) => { #[cfg(not(feature = "arbitrary_precision"))] - #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -466,7 +544,6 @@ macro_rules! deserialize_any { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de> { @@ -474,6 +551,10 @@ macro_rules! deserialize_any { return visitor.visit_u64(u); } else if let Some(i) = self.as_i64() { return visitor.visit_i64(i); + } else if let Some(u) = self.as_u128() { + return visitor.visit_u128(u); + } else if let Some(i) = self.as_i128() { + return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { if ryu::Buffer::new().format_finite(f) == self.n || f.to_string() == self.n { return visitor.visit_f64(f); @@ -510,7 +591,7 @@ macro_rules! deserialize_number { where V: de::Visitor<'de>, { - visitor.$visit(self.n.parse().map_err(|_| invalid_number())?) + visitor.$visit(tri!(self.n.parse().map_err(|_| invalid_number()))) } }; } @@ -524,18 +605,15 @@ impl<'de> Deserializer<'de> for Number { deserialize_number!(deserialize_i16 => visit_i16); deserialize_number!(deserialize_i32 => visit_i32); deserialize_number!(deserialize_i64 => visit_i64); + deserialize_number!(deserialize_i128 => visit_i128); deserialize_number!(deserialize_u8 => visit_u8); deserialize_number!(deserialize_u16 => visit_u16); deserialize_number!(deserialize_u32 => visit_u32); deserialize_number!(deserialize_u64 => visit_u64); + deserialize_number!(deserialize_u128 => visit_u128); deserialize_number!(deserialize_f32 => visit_f32); deserialize_number!(deserialize_f64 => visit_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128 => visit_i128); - deserialize_number!(deserialize_u128 => visit_u128); - } - forward_to_deserialize_any! { bool char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier @@ -543,7 +621,7 @@ impl<'de> Deserializer<'de> for Number { } } -impl<'de, 'a> Deserializer<'de> for &'a Number { +impl<'de> Deserializer<'de> for &Number { type Error = Error; deserialize_any!(ref); @@ -552,18 +630,15 @@ impl<'de, 'a> Deserializer<'de> for &'a Number { deserialize_number!(deserialize_i16 => visit_i16); deserialize_number!(deserialize_i32 => visit_i32); deserialize_number!(deserialize_i64 => visit_i64); + deserialize_number!(deserialize_i128 => visit_i128); deserialize_number!(deserialize_u8 => visit_u8); deserialize_number!(deserialize_u16 => visit_u16); deserialize_number!(deserialize_u32 => visit_u32); deserialize_number!(deserialize_u64 => visit_u64); + deserialize_number!(deserialize_u128 => visit_u128); deserialize_number!(deserialize_f32 => visit_f32); deserialize_number!(deserialize_f64 => visit_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128 => visit_i128); - deserialize_number!(deserialize_u128 => visit_u128); - } - forward_to_deserialize_any! { bool char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier @@ -629,7 +704,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - f.to_string() + ryu::Buffer::new().format_finite(f).to_owned() } } ParserNumber::U64(u) => { @@ -639,7 +714,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - u.to_string() + itoa::Buffer::new().format(u).to_owned() } } ParserNumber::I64(i) => { @@ -649,7 +724,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - i.to_string() + itoa::Buffer::new().format(i).to_owned() } } #[cfg(feature = "arbitrary_precision")] @@ -665,7 +740,6 @@ macro_rules! impl_from_unsigned { ) => { $( impl From<$ty> for Number { - #[inline] fn from(u: $ty) -> Self { let n = { #[cfg(not(feature = "arbitrary_precision"))] @@ -688,7 +762,6 @@ macro_rules! impl_from_signed { ) => { $( impl From<$ty> for Number { - #[inline] fn from(i: $ty) -> Self { let n = { #[cfg(not(feature = "arbitrary_precision"))] @@ -715,19 +788,9 @@ impl_from_unsigned!(u8, u16, u32, u64, usize); impl_from_signed!(i8, i16, i32, i64, isize); #[cfg(feature = "arbitrary_precision")] -serde_if_integer128! { - impl From for Number { - fn from(i: i128) -> Self { - Number { n: i.to_string() } - } - } - - impl From for Number { - fn from(u: u128) -> Self { - Number { n: u.to_string() } - } - } -} +impl_from_unsigned!(u128); +#[cfg(feature = "arbitrary_precision")] +impl_from_signed!(i128); impl Number { #[cfg(not(feature = "arbitrary_precision"))] diff --git a/src/raw.rs b/src/raw.rs index c373b4ded..be4dad44d 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,5 +1,9 @@ use crate::error::Error; -use crate::lib::*; +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::string::String; +use core::fmt::{self, Debug, Display}; +use core::mem; use serde::de::value::BorrowedStrDeserializer; use serde::de::{ self, Deserialize, DeserializeSeed, Deserializer, IntoDeserializer, MapAccess, Unexpected, @@ -95,9 +99,9 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// the boxed form of `RawValue` instead. This is almost as efficient but /// involves buffering the raw value from the I/O stream into memory. /// -/// [`serde_json::from_str`]: ../fn.from_str.html -/// [`serde_json::from_slice`]: ../fn.from_slice.html -/// [`serde_json::from_reader`]: ../fn.from_reader.html +/// [`serde_json::from_str`]: crate::from_str +/// [`serde_json::from_slice`]: crate::from_slice +/// [`serde_json::from_reader`]: crate::from_reader /// /// ``` /// # use serde::Deserialize; @@ -108,19 +112,24 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// raw_value: Box, /// } /// ``` -#[repr(C)] +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] +#[repr(transparent)] pub struct RawValue { json: str, } impl RawValue { - fn from_borrowed(json: &str) -> &Self { + const fn from_borrowed(json: &str) -> &Self { unsafe { mem::transmute::<&str, &RawValue>(json) } } fn from_owned(json: Box) -> Box { unsafe { mem::transmute::, Box>(json) } } + + fn into_owned(raw_value: Box) -> Box { + unsafe { mem::transmute::, Box>(raw_value) } + } } impl Clone for Box { @@ -139,7 +148,7 @@ impl ToOwned for RawValue { impl Default for Box { fn default() -> Self { - RawValue::from_borrowed("null").to_owned() + RawValue::NULL.to_owned() } } @@ -159,6 +168,13 @@ impl Display for RawValue { } impl RawValue { + /// A constant RawValue with the JSON value `null`. + pub const NULL: &'static RawValue = RawValue::from_borrowed("null"); + /// A constant RawValue with the JSON value `true`. + pub const TRUE: &'static RawValue = RawValue::from_borrowed("true"); + /// A constant RawValue with the JSON value `false`. + pub const FALSE: &'static RawValue = RawValue::from_borrowed("false"); + /// Convert an owned `String` of JSON data to an owned `RawValue`. /// /// This function is equivalent to `serde_json::from_str::>` @@ -168,11 +184,9 @@ impl RawValue { /// - the input has no leading or trailing whitespace, and /// - the input has capacity equal to its length. pub fn from_string(json: String) -> Result, Error> { - { - let borrowed = crate::from_str::<&Self>(&json)?; - if borrowed.json.len() < json.len() { - return Ok(borrowed.to_owned()); - } + let borrowed = tri!(crate::from_str::<&Self>(&json)); + if borrowed.json.len() < json.len() { + return Ok(borrowed.to_owned()); } Ok(Self::from_owned(json.into_boxed_str())) } @@ -215,6 +229,12 @@ impl RawValue { } } +impl From> for Box { + fn from(raw_value: Box) -> Self { + RawValue::into_owned(raw_value) + } +} + /// Convert a `T` into a boxed `RawValue`. /// /// # Example @@ -267,11 +287,12 @@ impl RawValue { /// /// println!("{}", serde_json::value::to_raw_value(&map).unwrap_err()); /// ``` +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub fn to_raw_value(value: &T) -> Result, Error> where - T: Serialize, + T: ?Sized + Serialize, { - let json_string = crate::to_string(value)?; + let json_string = tri!(crate::to_string(value)); Ok(RawValue::from_owned(json_string.into_boxed_str())) } @@ -282,8 +303,8 @@ impl Serialize for RawValue { where S: Serializer, { - let mut s = serializer.serialize_struct(TOKEN, 1)?; - s.serialize_field(TOKEN, &self.json)?; + let mut s = tri!(serializer.serialize_struct(TOKEN, 1)); + tri!(s.serialize_field(TOKEN, &self.json)); s.end() } } @@ -306,7 +327,7 @@ impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue { where V: MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } @@ -336,7 +357,7 @@ impl<'de> Deserialize<'de> for Box { where V: MapAccess<'de>, { - let value = visitor.next_key::()?; + let value = tri!(visitor.next_key::()); if value.is_none() { return Err(de::Error::invalid_type(Unexpected::Map, &self)); } @@ -376,7 +397,7 @@ impl<'de> Deserialize<'de> for RawKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(RawKey) } } @@ -433,9 +454,10 @@ impl<'de> Visitor<'de> for BoxedFromString { where E: de::Error, { - self.visit_string(s.to_owned()) + Ok(RawValue::from_owned(s.to_owned().into_boxed_str())) } + #[cfg(any(feature = "std", feature = "alloc"))] fn visit_string(self, s: String) -> Result where E: de::Error, @@ -512,3 +534,251 @@ impl<'de> MapAccess<'de> for BorrowedRawDeserializer<'de> { seed.deserialize(BorrowedStrDeserializer::new(self.raw_value.take().unwrap())) } } + +impl<'de> IntoDeserializer<'de, Error> for &'de RawValue { + type Deserializer = &'de RawValue; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> Deserializer<'de> for &'de RawValue { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_any(visitor) + } + + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_bool(visitor) + } + + fn deserialize_i8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i8(visitor) + } + + fn deserialize_i16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i16(visitor) + } + + fn deserialize_i32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i32(visitor) + } + + fn deserialize_i64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i64(visitor) + } + + fn deserialize_i128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_i128(visitor) + } + + fn deserialize_u8(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u8(visitor) + } + + fn deserialize_u16(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u16(visitor) + } + + fn deserialize_u32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u32(visitor) + } + + fn deserialize_u64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u64(visitor) + } + + fn deserialize_u128(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_u128(visitor) + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_f32(visitor) + } + + fn deserialize_f64(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_f64(visitor) + } + + fn deserialize_char(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_char(visitor) + } + + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_str(visitor) + } + + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_string(visitor) + } + + fn deserialize_bytes(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_bytes(visitor) + } + + fn deserialize_byte_buf(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_byte_buf(visitor) + } + + fn deserialize_option(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_option(visitor) + } + + fn deserialize_unit(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_unit(visitor) + } + + fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_unit_struct(name, visitor) + } + + fn deserialize_newtype_struct( + self, + name: &'static str, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_newtype_struct(name, visitor) + } + + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_seq(visitor) + } + + fn deserialize_tuple(self, len: usize, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_tuple(len, visitor) + } + + fn deserialize_tuple_struct( + self, + name: &'static str, + len: usize, + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_tuple_struct(name, len, visitor) + } + + fn deserialize_map(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_map(visitor) + } + + fn deserialize_struct( + self, + name: &'static str, + fields: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_struct(name, fields, visitor) + } + + fn deserialize_enum( + self, + name: &'static str, + variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_enum(name, variants, visitor) + } + + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_identifier(visitor) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + crate::Deserializer::from_str(&self.json).deserialize_ignored_any(visitor) + } +} diff --git a/src/read.rs b/src/read.rs index 522c0e011..f90d9f74a 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,6 +1,9 @@ use crate::error::{Error, ErrorCode, Result}; -use crate::lib::ops::Deref; -use crate::lib::*; +use alloc::vec::Vec; +use core::cmp; +use core::mem; +use core::ops::Deref; +use core::str; #[cfg(feature = "std")] use crate::io; @@ -11,11 +14,13 @@ use crate::iter::LineColIterator; use crate::raw::BorrowedRawDeserializer; #[cfg(all(feature = "raw_value", feature = "std"))] use crate::raw::OwnedRawDeserializer; +#[cfg(all(feature = "raw_value", feature = "std"))] +use alloc::string::String; #[cfg(feature = "raw_value")] use serde::de::Visitor; /// Trait used by the deserializer for iterating over input. This is manually -/// "specialized" for iterating over &[u8]. Once feature(specialization) is +/// "specialized" for iterating over `&[u8]`. Once feature(specialization) is /// stable we can use actual specialization. /// /// This trait is sealed and cannot be implemented for types outside of @@ -78,7 +83,7 @@ pub trait Read<'de>: private::Sealed { #[doc(hidden)] fn ignore_str(&mut self) -> Result<()>; - /// Assumes the previous byte was a hex escape sequnce ('\u') in a string. + /// Assumes the previous byte was a hex escape sequence ('\u') in a string. /// Parses next hexadecimal sequence. #[doc(hidden)] fn decode_hex_escape(&mut self) -> Result; @@ -140,6 +145,7 @@ where /// JSON input source that reads from a std::io input stream. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct IoRead where R: io::Read, @@ -185,6 +191,12 @@ where R: io::Read, { /// Create a JSON input source to read from a std::io input stream. + /// + /// When reading from a source against which short reads are not efficient, such + /// as a [`File`], you will want to apply your own buffering because serde_json + /// will not buffer the input. See [`std::io::BufReader`]. + /// + /// [`File`]: std::fs::File pub fn new(reader: R) -> Self { IoRead { iter: LineColIterator::new(reader.bytes()), @@ -215,7 +227,7 @@ where { loop { let ch = tri!(next_or_eof(self)); - if !ESCAPE[ch as usize] { + if !is_escape(ch, true) { scratch.push(ch); continue; } @@ -224,7 +236,7 @@ where return result(self, scratch); } b'\\' => { - tri!(parse_escape(self, scratch)); + tri!(parse_escape(self, validate, scratch)); } _ => { if validate { @@ -248,7 +260,7 @@ where Some(ch) => { #[cfg(feature = "raw_value")] { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } @@ -259,7 +271,7 @@ where Some(Ok(ch)) => { #[cfg(feature = "raw_value")] { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } @@ -294,7 +306,7 @@ where #[cfg(feature = "raw_value")] fn discard(&mut self) { if let Some(ch) = self.ch.take() { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } @@ -336,7 +348,7 @@ where fn ignore_str(&mut self) -> Result<()> { loop { let ch = tri!(next_or_eof(self)); - if !ESCAPE[ch as usize] { + if !is_escape(ch, true) { continue; } match ch { @@ -354,16 +366,14 @@ where } fn decode_hex_escape(&mut self) -> Result { - let mut n = 0; - for _ in 0..4 { - match decode_hex_val(tri!(next_or_eof(self))) { - None => return error(self, ErrorCode::InvalidEscape), - Some(val) => { - n = (n << 4) + val; - } - } + let a = tri!(next_or_eof(self)); + let b = tri!(next_or_eof(self)); + let c = tri!(next_or_eof(self)); + let d = tri!(next_or_eof(self)); + match decode_four_hex_digits(a, b, c, d) { + Some(val) => Ok(val), + None => error(self, ErrorCode::InvalidEscape), } - Ok(n) } #[cfg(feature = "raw_value")] @@ -377,7 +387,10 @@ where V: Visitor<'de>, { let raw = self.raw_buffer.take().unwrap(); - let raw = String::from_utf8(raw).unwrap(); + let raw = match String::from_utf8(raw) { + Ok(raw) => raw, + Err(_) => return error(self, ErrorCode::InvalidUnicodeCodePoint), + }; visitor.visit_map(OwnedRawDeserializer { raw_value: Some(raw), }) @@ -406,19 +419,73 @@ impl<'a> SliceRead<'a> { } fn position_of_index(&self, i: usize) -> Position { - let mut position = Position { line: 1, column: 0 }; - for ch in &self.slice[..i] { - match *ch { - b'\n' => { - position.line += 1; - position.column = 0; - } - _ => { - position.column += 1; - } + let start_of_line = match memchr::memrchr(b'\n', &self.slice[..i]) { + Some(position) => position + 1, + None => 0, + }; + Position { + line: 1 + memchr::memchr_iter(b'\n', &self.slice[..start_of_line]).count(), + column: i - start_of_line, + } + } + + fn skip_to_escape(&mut self, forbid_control_characters: bool) { + // Immediately bail-out on empty strings and consecutive escapes (e.g. \u041b\u0435) + if self.index == self.slice.len() + || is_escape(self.slice[self.index], forbid_control_characters) + { + return; + } + self.index += 1; + + let rest = &self.slice[self.index..]; + + if !forbid_control_characters { + self.index += memchr::memchr2(b'"', b'\\', rest).unwrap_or(rest.len()); + return; + } + + // We wish to find the first byte in range 0x00..=0x1F or " or \. Ideally, we'd use + // something akin to memchr3, but the memchr crate does not support this at the moment. + // Therefore, we use a variation on Mycroft's algorithm [1] to provide performance better + // than a naive loop. It runs faster than equivalent two-pass memchr2+SWAR code on + // benchmarks and it's cross-platform, so probably the right fit. + // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ + + #[cfg(fast_arithmetic = "64")] + type Chunk = u64; + #[cfg(fast_arithmetic = "32")] + type Chunk = u32; + + const STEP: usize = mem::size_of::(); + const ONE_BYTES: Chunk = Chunk::MAX / 255; // 0x0101...01 + + for chunk in rest.chunks_exact(STEP) { + let chars = Chunk::from_le_bytes(chunk.try_into().unwrap()); + let contains_ctrl = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars; + let chars_quote = chars ^ (ONE_BYTES * Chunk::from(b'"')); + let contains_quote = chars_quote.wrapping_sub(ONE_BYTES) & !chars_quote; + let chars_backslash = chars ^ (ONE_BYTES * Chunk::from(b'\\')); + let contains_backslash = chars_backslash.wrapping_sub(ONE_BYTES) & !chars_backslash; + let masked = (contains_ctrl | contains_quote | contains_backslash) & (ONE_BYTES << 7); + if masked != 0 { + // SAFETY: chunk is in-bounds for slice + self.index = unsafe { chunk.as_ptr().offset_from(self.slice.as_ptr()) } as usize + + masked.trailing_zeros() as usize / 8; + return; } } - position + + self.index += rest.len() / STEP * STEP; + self.skip_to_escape_slow(); + } + + #[cold] + #[inline(never)] + fn skip_to_escape_slow(&mut self) { + while self.index < self.slice.len() && !is_escape(self.slice[self.index], true) { + self.index += 1; + } } /// The big optimization here over IoRead is that if the string contains no @@ -438,9 +505,7 @@ impl<'a> SliceRead<'a> { let mut start = self.index; loop { - while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] { - self.index += 1; - } + self.skip_to_escape(validate); if self.index == self.slice.len() { return error(self, ErrorCode::EofWhileParsingString); } @@ -461,14 +526,12 @@ impl<'a> SliceRead<'a> { b'\\' => { scratch.extend_from_slice(&self.slice[start..self.index]); self.index += 1; - tri!(parse_escape(self, scratch)); + tri!(parse_escape(self, validate, scratch)); start = self.index; } _ => { self.index += 1; - if validate { - return error(self, ErrorCode::ControlCharacterWhileParsingString); - } + return error(self, ErrorCode::ControlCharacterWhileParsingString); } } } @@ -534,9 +597,7 @@ impl<'a> Read<'a> for SliceRead<'a> { fn ignore_str(&mut self) -> Result<()> { loop { - while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] { - self.index += 1; - } + self.skip_to_escape(true); if self.index == self.slice.len() { return error(self, ErrorCode::EofWhileParsingString); } @@ -556,24 +617,21 @@ impl<'a> Read<'a> for SliceRead<'a> { } } + #[inline] fn decode_hex_escape(&mut self) -> Result { - if self.index + 4 > self.slice.len() { - self.index = self.slice.len(); - return error(self, ErrorCode::EofWhileParsingString); - } - - let mut n = 0; - for _ in 0..4 { - let ch = decode_hex_val(self.slice[self.index]); - self.index += 1; - match ch { - None => return error(self, ErrorCode::InvalidEscape), - Some(val) => { - n = (n << 4) + val; + match self.slice[self.index..] { + [a, b, c, d, ..] => { + self.index += 4; + match decode_four_hex_digits(a, b, c, d) { + Some(val) => Ok(val), + None => error(self, ErrorCode::InvalidEscape), } } + _ => { + self.index = self.slice.len(); + error(self, ErrorCode::EofWhileParsingString) + } } - Ok(n) } #[cfg(feature = "raw_value")] @@ -587,7 +645,10 @@ impl<'a> Read<'a> for SliceRead<'a> { V: Visitor<'a>, { let raw = &self.slice[self.raw_buffering_start_index..self.index]; - let raw = str::from_utf8(raw).unwrap(); + let raw = match str::from_utf8(raw) { + Ok(raw) => raw, + Err(_) => return error(self, ErrorCode::InvalidUnicodeCodePoint), + }; visitor.visit_map(BorrowedRawDeserializer { raw_value: Some(raw), }) @@ -647,8 +708,9 @@ impl<'a> Read<'a> for StrRead<'a> { fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec) -> Result> { self.delegate.parse_str_bytes(scratch, true, |_, bytes| { - // The input is assumed to be valid UTF-8 and the \u-escapes are - // checked along the way, so don't need to check here. + // The deserialization input came in as &str with a UTF-8 guarantee, + // and the \u-escapes are checked along the way, so don't need to + // check here. Ok(unsafe { str::from_utf8_unchecked(bytes) }) }) } @@ -670,7 +732,7 @@ impl<'a> Read<'a> for StrRead<'a> { #[cfg(feature = "raw_value")] fn begin_raw_buffering(&mut self) { - self.delegate.begin_raw_buffering() + self.delegate.begin_raw_buffering(); } #[cfg(feature = "raw_value")] @@ -695,9 +757,9 @@ impl<'a> Read<'a> for StrRead<'a> { ////////////////////////////////////////////////////////////////////////////// -impl<'a, 'de, R> private::Sealed for &'a mut R where R: Read<'de> {} +impl<'de, R> private::Sealed for &mut R where R: Read<'de> {} -impl<'a, 'de, R> Read<'de> for &'a mut R +impl<'de, R> Read<'de> for &mut R where R: Read<'de>, { @@ -710,7 +772,7 @@ where } fn discard(&mut self) { - R::discard(self) + R::discard(self); } fn position(&self) -> Position { @@ -746,7 +808,7 @@ where #[cfg(feature = "raw_value")] fn begin_raw_buffering(&mut self) { - R::begin_raw_buffering(self) + R::begin_raw_buffering(self); } #[cfg(feature = "raw_value")] @@ -760,7 +822,7 @@ where const should_early_return_if_failed: bool = R::should_early_return_if_failed; fn set_failed(&mut self, failed: &mut bool) { - R::set_failed(self, failed) + R::set_failed(self, failed); } } @@ -771,33 +833,9 @@ pub trait Fused: private::Sealed {} impl<'a> Fused for SliceRead<'a> {} impl<'a> Fused for StrRead<'a> {} -// Lookup table of bytes that must be escaped. A value of true at index i means -// that byte i requires an escape sequence in the input. -static ESCAPE: [bool; 256] = { - const CT: bool = true; // control character \x00..=\x1F - const QU: bool = true; // quote \x22 - const BS: bool = true; // backslash \x5C - const __: bool = false; // allow unescaped - [ - // 1 2 3 4 5 6 7 8 9 A B C D E F - CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 0 - CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 1 - __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 - __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F - ] -}; +fn is_escape(ch: u8, including_control_characters: bool) -> bool { + ch == b'"' || ch == b'\\' || (including_control_characters && ch < 0x20) +} fn next_or_eof<'de, R>(read: &mut R) -> Result where @@ -809,6 +847,16 @@ where } } +fn peek_or_eof<'de, R>(read: &mut R) -> Result +where + R: ?Sized + Read<'de>, +{ + match tri!(read.peek()) { + Some(b) => Ok(b), + None => error(read, ErrorCode::EofWhileParsingString), + } +} + fn error<'de, R, T>(read: &R, reason: ErrorCode) -> Result where R: ?Sized + Read<'de>, @@ -823,7 +871,11 @@ fn as_str<'de, 's, R: Read<'de>>(read: &R, slice: &'s [u8]) -> Result<&'s str> { /// Parses a JSON escape sequence and appends it into the scratch space. Assumes /// the previous byte read was a backslash. -fn parse_escape<'de, R: Read<'de>>(read: &mut R, scratch: &mut Vec) -> Result<()> { +fn parse_escape<'de, R: Read<'de>>( + read: &mut R, + validate: bool, + scratch: &mut Vec, +) -> Result<()> { let ch = tri!(next_or_eof(read)); match ch { @@ -835,54 +887,137 @@ fn parse_escape<'de, R: Read<'de>>(read: &mut R, scratch: &mut Vec) -> Resul b'n' => scratch.push(b'\n'), b'r' => scratch.push(b'\r'), b't' => scratch.push(b'\t'), - b'u' => { - let c = match tri!(read.decode_hex_escape()) { - 0xDC00..=0xDFFF => { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(next_or_eof(read)) != b'\\' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - if tri!(next_or_eof(read)) != b'u' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } + b'u' => return parse_unicode_escape(read, validate, scratch), + _ => return error(read, ErrorCode::InvalidEscape), + } - let n2 = tri!(read.decode_hex_escape()); + Ok(()) +} - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } +/// Parses a JSON \u escape and appends it into the scratch space. Assumes `\u` +/// has just been read. +#[cold] +fn parse_unicode_escape<'de, R: Read<'de>>( + read: &mut R, + validate: bool, + scratch: &mut Vec, +) -> Result<()> { + let mut n = tri!(read.decode_hex_escape()); + + // Non-BMP characters are encoded as a sequence of two hex escapes, + // representing UTF-16 surrogates. If deserializing a utf-8 string the + // surrogates are required to be paired, whereas deserializing a byte string + // accepts lone surrogates. + if validate && n >= 0xDC00 && n <= 0xDFFF { + // XXX: This is actually a trailing surrogate. + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } + + loop { + if n < 0xD800 || n > 0xDBFF { + // Every u16 outside of the surrogate ranges is guaranteed to be a + // legal char. + push_wtf8_codepoint(n as u32, scratch); + return Ok(()); + } - let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; + // n is a leading surrogate, we now expect a trailing surrogate. + let n1 = n; - match char::from_u32(n) { - Some(c) => c, - None => { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - } - } - - n => match char::from_u32(n as u32) { - Some(c) => c, - None => { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - }, + if tri!(peek_or_eof(read)) == b'\\' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + Ok(()) }; + } - scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes()); + if tri!(peek_or_eof(read)) == b'u' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + // The \ prior to this byte started an escape sequence, so we + // need to parse that now. This recursive call does not blow the + // stack on malicious input because the escape is not \u, so it + // will be handled by one of the easy nonrecursive cases. + parse_escape(read, validate, scratch) + }; } - _ => { - return error(read, ErrorCode::InvalidEscape); + + let n2 = tri!(read.decode_hex_escape()); + + if n2 < 0xDC00 || n2 > 0xDFFF { + if validate { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } + push_wtf8_codepoint(n1 as u32, scratch); + // If n2 is a leading surrogate, we need to restart. + n = n2; + continue; } + + // This value is in range U+10000..=U+10FFFF, which is always a valid + // codepoint. + let n = ((((n1 - 0xD800) as u32) << 10) | (n2 - 0xDC00) as u32) + 0x1_0000; + push_wtf8_codepoint(n, scratch); + return Ok(()); } +} - Ok(()) +/// Adds a WTF-8 codepoint to the end of the buffer. This is a more efficient +/// implementation of String::push. The codepoint may be a surrogate. +#[inline] +fn push_wtf8_codepoint(n: u32, scratch: &mut Vec) { + if n < 0x80 { + scratch.push(n as u8); + return; + } + + scratch.reserve(4); + + // SAFETY: After the `reserve` call, `scratch` has at least 4 bytes of + // allocated but uninitialized memory after its last initialized byte, which + // is where `ptr` points. All reachable match arms write `encoded_len` bytes + // to that region and update the length accordingly, and `encoded_len` is + // always <= 4. + unsafe { + let ptr = scratch.as_mut_ptr().add(scratch.len()); + + let encoded_len = match n { + 0..=0x7F => unreachable!(), + 0x80..=0x7FF => { + ptr.write(((n >> 6) & 0b0001_1111) as u8 | 0b1100_0000); + 2 + } + 0x800..=0xFFFF => { + ptr.write(((n >> 12) & 0b0000_1111) as u8 | 0b1110_0000); + ptr.add(1) + .write(((n >> 6) & 0b0011_1111) as u8 | 0b1000_0000); + 3 + } + 0x1_0000..=0x10_FFFF => { + ptr.write(((n >> 18) & 0b0000_0111) as u8 | 0b1111_0000); + ptr.add(1) + .write(((n >> 12) & 0b0011_1111) as u8 | 0b1000_0000); + ptr.add(2) + .write(((n >> 6) & 0b0011_1111) as u8 | 0b1000_0000); + 4 + } + 0x11_0000.. => unreachable!(), + }; + ptr.add(encoded_len - 1) + .write((n & 0b0011_1111) as u8 | 0b1000_0000); + + scratch.set_len(scratch.len() + encoded_len); + } } /// Parses a JSON escape sequence and discards the value. Assumes the previous @@ -896,36 +1031,13 @@ where match ch { b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't' => {} b'u' => { - let n = match tri!(read.decode_hex_escape()) { - 0xDC00..=0xDFFF => { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(next_or_eof(read)) != b'\\' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - if tri!(next_or_eof(read)) != b'u' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - - let n2 = tri!(read.decode_hex_escape()); - - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000 - } - - n => n as u32, - }; + // At this point we don't care if the codepoint is valid. We just + // want to consume it. We don't actually know what is valid or not + // at this point, because that depends on if this string will + // ultimately be parsed into a string or a byte buffer in the "real" + // parse. - if char::from_u32(n).is_none() { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } + tri!(read.decode_hex_escape()); } _ => { return error(read, ErrorCode::InvalidEscape); @@ -935,34 +1047,43 @@ where Ok(()) } -static HEX: [u8; 256] = { - const __: u8 = 255; // not a hex digit - [ - // 1 2 3 4 5 6 7 8 9 A B C D E F - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 1 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, __, __, __, __, __, __, // 3 - __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 4 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 5 - __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 6 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F - ] -}; - -fn decode_hex_val(val: u8) -> Option { - let n = HEX[val as usize] as u16; - if n == 255 { - None +const fn decode_hex_val_slow(val: u8) -> Option { + match val { + b'0'..=b'9' => Some(val - b'0'), + b'A'..=b'F' => Some(val - b'A' + 10), + b'a'..=b'f' => Some(val - b'a' + 10), + _ => None, + } +} + +const fn build_hex_table(shift: usize) -> [i16; 256] { + let mut table = [0; 256]; + let mut ch = 0; + while ch < 256 { + table[ch] = match decode_hex_val_slow(ch as u8) { + Some(val) => (val as i16) << shift, + None => -1, + }; + ch += 1; + } + table +} + +static HEX0: [i16; 256] = build_hex_table(0); +static HEX1: [i16; 256] = build_hex_table(4); + +fn decode_four_hex_digits(a: u8, b: u8, c: u8, d: u8) -> Option { + let a = HEX1[a as usize] as i32; + let b = HEX0[b as usize] as i32; + let c = HEX1[c as usize] as i32; + let d = HEX0[d as usize] as i32; + + let codepoint = ((a | b) << 8) | c | d; + + // A single sign bit check. + if codepoint >= 0 { + Some(codepoint as u16) } else { - Some(n) + None } } diff --git a/src/ser.rs b/src/ser.rs index b9b83cd62..50c06f938 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2,12 +2,18 @@ use crate::error::{Error, ErrorCode, Result}; use crate::io; -use crate::lib::num::FpCategory; -use crate::lib::*; +use alloc::string::String; +#[cfg(feature = "raw_value")] +use alloc::string::ToString; +use alloc::vec::Vec; +use core::fmt::{self, Display}; +use core::hint; +use core::num::FpCategory; +use core::str; use serde::ser::{self, Impossible, Serialize}; -use serde::serde_if_integer128; /// A structure for serializing Rust values into JSON. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct Serializer { writer: W, formatter: F, @@ -72,137 +78,105 @@ where #[inline] fn serialize_bool(self, value: bool) -> Result<()> { - tri!(self - .formatter + self.formatter .write_bool(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i8(self, value: i8) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i8(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i16(self, value: i16) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i16(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i32(self, value: i32) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i32(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i64(self, value: i64) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i64(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result<()> { - self.formatter - .write_number_str(&mut self.writer, &value.to_string()) - .map_err(Error::io) - } + fn serialize_i128(self, value: i128) -> Result<()> { + self.formatter + .write_i128(&mut self.writer, value) + .map_err(Error::io) } #[inline] fn serialize_u8(self, value: u8) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u8(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u16(self, value: u16) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u16(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u32(self, value: u32) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u32(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u64(self, value: u64) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u64(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result<()> { - self.formatter - .write_number_str(&mut self.writer, &value.to_string()) - .map_err(Error::io) - } + fn serialize_u128(self, value: u128) -> Result<()> { + self.formatter + .write_u128(&mut self.writer, value) + .map_err(Error::io) } #[inline] fn serialize_f32(self, value: f32) -> Result<()> { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => { - tri!(self - .formatter - .write_null(&mut self.writer) - .map_err(Error::io)); - } - _ => { - tri!(self - .formatter - .write_f32(&mut self.writer, value) - .map_err(Error::io)); - } + FpCategory::Nan | FpCategory::Infinite => self + .formatter + .write_null(&mut self.writer) + .map_err(Error::io), + _ => self + .formatter + .write_f32(&mut self.writer, value) + .map_err(Error::io), } - Ok(()) } #[inline] fn serialize_f64(self, value: f64) -> Result<()> { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => { - tri!(self - .formatter - .write_null(&mut self.writer) - .map_err(Error::io)); - } - _ => { - tri!(self - .formatter - .write_f64(&mut self.writer, value) - .map_err(Error::io)); - } + FpCategory::Nan | FpCategory::Infinite => self + .formatter + .write_null(&mut self.writer) + .map_err(Error::io), + _ => self + .formatter + .write_f64(&mut self.writer, value) + .map_err(Error::io), } - Ok(()) } #[inline] @@ -214,27 +188,21 @@ where #[inline] fn serialize_str(self, value: &str) -> Result<()> { - tri!(format_escaped_str(&mut self.writer, &mut self.formatter, value).map_err(Error::io)); - Ok(()) + format_escaped_str(&mut self.writer, &mut self.formatter, value).map_err(Error::io) } #[inline] fn serialize_bytes(self, value: &[u8]) -> Result<()> { - use serde::ser::SerializeSeq; - let mut seq = tri!(self.serialize_seq(Some(value.len()))); - for byte in value { - tri!(seq.serialize_element(byte)); - } - seq.end() + self.formatter + .write_byte_array(&mut self.writer, value) + .map_err(Error::io) } #[inline] fn serialize_unit(self) -> Result<()> { - tri!(self - .formatter + self.formatter .write_null(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] @@ -294,11 +262,9 @@ where .formatter .end_object_value(&mut self.writer) .map_err(Error::io)); - tri!(self - .formatter + self.formatter .end_object(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] @@ -316,11 +282,11 @@ where #[inline] fn serialize_seq(self, len: Option) -> Result { + tri!(self + .formatter + .begin_array(&mut self.writer) + .map_err(Error::io)); if len == Some(0) { - tri!(self - .formatter - .begin_array(&mut self.writer) - .map_err(Error::io)); tri!(self .formatter .end_array(&mut self.writer) @@ -330,10 +296,6 @@ where state: State::Empty, }) } else { - tri!(self - .formatter - .begin_array(&mut self.writer) - .map_err(Error::io)); Ok(Compound::Map { ser: self, state: State::First, @@ -385,11 +347,11 @@ where #[inline] fn serialize_map(self, len: Option) -> Result { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(Error::io)); if len == Some(0) { - tri!(self - .formatter - .begin_object(&mut self.writer) - .map_err(Error::io)); tri!(self .formatter .end_object(&mut self.writer) @@ -399,10 +361,6 @@ where state: State::Empty, }) } else { - tri!(self - .formatter - .begin_object(&mut self.writer) - .map_err(Error::io)); Ok(Compound::Map { ser: self, state: State::First, @@ -482,24 +440,20 @@ where .formatter .begin_string(&mut self.writer) .map_err(Error::io)); - { - let mut adapter = Adapter { - writer: &mut self.writer, - formatter: &mut self.formatter, - error: None, - }; - match write!(adapter, "{}", value) { - Ok(()) => debug_assert!(adapter.error.is_none()), - Err(fmt::Error) => { - return Err(Error::io(adapter.error.expect("there should be an error"))); - } + let mut adapter = Adapter { + writer: &mut self.writer, + formatter: &mut self.formatter, + error: None, + }; + match write!(adapter, "{}", value) { + Ok(()) => debug_assert!(adapter.error.is_none()), + Err(fmt::Error) => { + return Err(Error::io(adapter.error.expect("there should be an error"))); } } - tri!(self - .formatter + self.formatter .end_string(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } } @@ -538,22 +492,17 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { - ref mut ser, - ref mut state, - } => { + match self { + Compound::Map { ser, state } => { tri!(ser .formatter .begin_array_value(&mut ser.writer, *state == State::First) .map_err(Error::io)); *state = State::Rest; tri!(value.serialize(&mut **ser)); - tri!(ser - .formatter + ser.formatter .end_array_value(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -565,13 +514,10 @@ where #[inline] fn end(self) -> Result<()> { match self { - Compound::Map { ser, state } => { - match state { - State::Empty => {} - _ => tri!(ser.formatter.end_array(&mut ser.writer).map_err(Error::io)), - } - Ok(()) - } + Compound::Map { ser, state } => match state { + State::Empty => Ok(()), + _ => ser.formatter.end_array(&mut ser.writer).map_err(Error::io), + }, #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), #[cfg(feature = "raw_value")] @@ -652,8 +598,7 @@ where .formatter .end_object_value(&mut ser.writer) .map_err(Error::io)); - tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)); - Ok(()) + ser.formatter.end_object(&mut ser.writer).map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -676,11 +621,8 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { - ref mut ser, - ref mut state, - } => { + match self { + Compound::Map { ser, state } => { tri!(ser .formatter .begin_object_key(&mut ser.writer, *state == State::First) @@ -689,11 +631,9 @@ where tri!(key.serialize(MapKeySerializer { ser: *ser })); - tri!(ser - .formatter + ser.formatter .end_object_key(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -707,18 +647,16 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { ref mut ser, .. } => { + match self { + Compound::Map { ser, .. } => { tri!(ser .formatter .begin_object_value(&mut ser.writer) .map_err(Error::io)); tri!(value.serialize(&mut **ser)); - tri!(ser - .formatter + ser.formatter .end_object_value(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -730,13 +668,10 @@ where #[inline] fn end(self) -> Result<()> { match self { - Compound::Map { ser, state } => { - match state { - State::Empty => {} - _ => tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)), - } - Ok(()) - } + Compound::Map { ser, state } => match state { + State::Empty => Ok(()), + _ => ser.formatter.end_object(&mut ser.writer).map_err(Error::io), + }, #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), #[cfg(feature = "raw_value")] @@ -758,22 +693,20 @@ where where T: ?Sized + Serialize, { - match *self { + match self { Compound::Map { .. } => ser::SerializeMap::serialize_entry(self, key, value), #[cfg(feature = "arbitrary_precision")] - Compound::Number { ref mut ser, .. } => { + Compound::Number { ser, .. } => { if key == crate::number::TOKEN { - tri!(value.serialize(NumberStrEmitter(&mut *ser))); - Ok(()) + value.serialize(NumberStrEmitter(ser)) } else { Err(invalid_number()) } } #[cfg(feature = "raw_value")] - Compound::RawValue { ref mut ser, .. } => { + Compound::RawValue { ser, .. } => { if key == crate::raw::TOKEN { - tri!(value.serialize(RawValueStrEmitter(&mut *ser))); - Ok(()) + value.serialize(RawValueStrEmitter(ser)) } else { Err(invalid_raw_value()) } @@ -827,8 +760,7 @@ where .formatter .end_object_value(&mut ser.writer) .map_err(Error::io)); - tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)); - Ok(()) + ser.formatter.end_object(&mut ser.writer).map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -856,6 +788,10 @@ fn key_must_be_a_string() -> Error { Error::syntax(ErrorCode::KeyMustBeAString, 0, 0) } +fn float_key_must_be_finite() -> Error { + Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0) +} + impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F> where W: io::Write, @@ -895,8 +831,21 @@ where type SerializeStruct = Impossible<(), Error>; type SerializeStructVariant = Impossible<(), Error>; - fn serialize_bool(self, _value: bool) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_bool(self, value: bool) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_bool(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } fn serialize_i8(self, value: i8) -> Result<()> { @@ -910,12 +859,10 @@ where .formatter .write_i8(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i16(self, value: i16) -> Result<()> { @@ -929,12 +876,10 @@ where .formatter .write_i16(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i32(self, value: i32) -> Result<()> { @@ -948,12 +893,10 @@ where .formatter .write_i32(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i64(self, value: i64) -> Result<()> { @@ -967,33 +910,27 @@ where .formatter .write_i64(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result<()> { - tri!(self - .ser - .formatter - .begin_string(&mut self.ser.writer) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .write_number_str(&mut self.ser.writer, &value.to_string()) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) - } + fn serialize_i128(self, value: i128) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_i128(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } fn serialize_u8(self, value: u8) -> Result<()> { @@ -1007,12 +944,10 @@ where .formatter .write_u8(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u16(self, value: u16) -> Result<()> { @@ -1026,12 +961,10 @@ where .formatter .write_u16(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u32(self, value: u32) -> Result<()> { @@ -1045,12 +978,10 @@ where .formatter .write_u32(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u64(self, value: u64) -> Result<()> { @@ -1064,45 +995,73 @@ where .formatter .write_u64(&mut self.ser.writer, value) .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) + } + + fn serialize_u128(self, value: u128) -> Result<()> { tri!(self .ser .formatter - .end_string(&mut self.ser.writer) + .begin_string(&mut self.ser.writer) .map_err(Error::io)); - Ok(()) + tri!(self + .ser + .formatter + .write_u128(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result<()> { - tri!(self - .ser - .formatter - .begin_string(&mut self.ser.writer) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .write_number_str(&mut self.ser.writer, &value.to_string()) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + fn serialize_f32(self, value: f32) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); } - } - fn serialize_f32(self, _value: f32) -> Result<()> { - Err(key_must_be_a_string()) + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_f32(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } - fn serialize_f64(self, _value: f64) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_f64(self, value: f64) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); + } + + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_f64(&mut self.ser.writer, value) + .map_err(Error::io)); + self.ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io) } fn serialize_char(self, value: char) -> Result<()> { - self.ser.serialize_str(&value.to_string()) + self.ser.serialize_str(value.encode_utf8(&mut [0u8; 4])) } fn serialize_bytes(self, _value: &[u8]) -> Result<()> { @@ -1134,11 +1093,11 @@ where Err(key_must_be_a_string()) } - fn serialize_some(self, _value: &T) -> Result<()> + fn serialize_some(self, value: &T) -> Result<()> where T: ?Sized + Serialize, { - Err(key_must_be_a_string()) + value.serialize(self) } fn serialize_seq(self, _len: Option) -> Result { @@ -1229,10 +1188,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, Err(invalid_number()) } - serde_if_integer128! { - fn serialize_i128(self, _v: i128) -> Result<()> { - Err(invalid_number()) - } + fn serialize_i128(self, _v: i128) -> Result<()> { + Err(invalid_number()) } fn serialize_u8(self, _v: u8) -> Result<()> { @@ -1251,10 +1208,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, Err(invalid_number()) } - serde_if_integer128! { - fn serialize_u128(self, _v: u128) -> Result<()> { - Err(invalid_number()) - } + fn serialize_u128(self, _v: u128) -> Result<()> { + Err(invalid_number()) } fn serialize_f32(self, _v: f32) -> Result<()> { @@ -1410,10 +1365,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, Err(ser::Error::custom("expected RawValue")) } - serde_if_integer128! { - fn serialize_i128(self, _v: i128) -> Result<()> { - Err(ser::Error::custom("expected RawValue")) - } + fn serialize_i128(self, _v: i128) -> Result<()> { + Err(ser::Error::custom("expected RawValue")) } fn serialize_u8(self, _v: u8) -> Result<()> { @@ -1432,10 +1385,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, Err(ser::Error::custom("expected RawValue")) } - serde_if_integer128! { - fn serialize_u128(self, _v: u128) -> Result<()> { - Err(ser::Error::custom("expected RawValue")) - } + fn serialize_u128(self, _v: u128) -> Result<()> { + Err(ser::Error::custom("expected RawValue")) } fn serialize_f32(self, _v: f32) -> Result<()> { @@ -1553,6 +1504,13 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, ) -> Result { Err(ser::Error::custom("expected RawValue")) } + + fn collect_str(self, value: &T) -> Result + where + T: ?Sized + Display, + { + self.serialize_str(&value.to_string()) + } } /// Represents a character escape code in a type-safe manner. @@ -1578,23 +1536,6 @@ pub enum CharEscape { AsciiControl(u8), } -impl CharEscape { - #[inline] - fn from_escape_table(escape: u8, byte: u8) -> CharEscape { - match escape { - self::BB => CharEscape::Backspace, - self::TT => CharEscape::Tab, - self::NN => CharEscape::LineFeed, - self::FF => CharEscape::FormFeed, - self::RR => CharEscape::CarriageReturn, - self::QU => CharEscape::Quote, - self::BS => CharEscape::ReverseSolidus, - self::UU => CharEscape::AsciiControl(byte), - _ => unreachable!(), - } - } -} - /// This trait abstracts away serializing the JSON control characters, which allows the user to /// optionally pretty print the JSON output. pub trait Formatter { @@ -1665,6 +1606,17 @@ pub trait Formatter { writer.write_all(s.as_bytes()) } + /// Writes an integer value like `-123` to the specified writer. + #[inline] + fn write_i128(&mut self, writer: &mut W, value: i128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) + } + /// Writes an integer value like `123` to the specified writer. #[inline] fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> @@ -1709,7 +1661,32 @@ pub trait Formatter { writer.write_all(s.as_bytes()) } + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u128(&mut self, writer: &mut W, value: u128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) + } + /// Writes a floating point value like `-31.26e+12` to the specified writer. + /// + /// # Special cases + /// + /// This function **does not** check for NaN or infinity. If the input + /// number is not a finite float, the printed representation will be some + /// correctly formatted but unspecified numerical value. + /// + /// Please check [`is_finite`] yourself before calling this function, or + /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself + /// with a different `Formatter` method. + /// + /// [`is_finite`]: f32::is_finite + /// [`is_nan`]: f32::is_nan + /// [`is_infinite`]: f32::is_infinite #[inline] fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> where @@ -1721,6 +1698,20 @@ pub trait Formatter { } /// Writes a floating point value like `-31.26e+12` to the specified writer. + /// + /// # Special cases + /// + /// This function **does not** check for NaN or infinity. If the input + /// number is not a finite float, the printed representation will be some + /// correctly formatted but unspecified numerical value. + /// + /// Please check [`is_finite`] yourself before calling this function, or + /// check [`is_nan`] and [`is_infinite`] and handle those cases yourself + /// with a different `Formatter` method. + /// + /// [`is_finite`]: f64::is_finite + /// [`is_nan`]: f64::is_nan + /// [`is_infinite`]: f64::is_infinite #[inline] fn write_f64(&mut self, writer: &mut W, value: f64) -> io::Result<()> where @@ -1778,30 +1769,51 @@ pub trait Formatter { { use self::CharEscape::*; - let s = match char_escape { - Quote => b"\\\"", - ReverseSolidus => b"\\\\", - Solidus => b"\\/", - Backspace => b"\\b", - FormFeed => b"\\f", - LineFeed => b"\\n", - CarriageReturn => b"\\r", - Tab => b"\\t", + let escape_char = match char_escape { + Quote => b'"', + ReverseSolidus => b'\\', + Solidus => b'/', + Backspace => b'b', + FormFeed => b'f', + LineFeed => b'n', + CarriageReturn => b'r', + Tab => b't', + AsciiControl(_) => b'u', + }; + + match char_escape { AsciiControl(byte) => { static HEX_DIGITS: [u8; 16] = *b"0123456789abcdef"; let bytes = &[ b'\\', - b'u', + escape_char, b'0', b'0', HEX_DIGITS[(byte >> 4) as usize], HEX_DIGITS[(byte & 0xF) as usize], ]; - return writer.write_all(bytes); + writer.write_all(bytes) } - }; + _ => writer.write_all(&[b'\\', escape_char]), + } + } - writer.write_all(s) + /// Writes the representation of a byte array. Formatters can choose whether + /// to represent bytes as a JSON array of integers (the default), or some + /// JSON string encoding like hex or base64. + fn write_byte_array(&mut self, writer: &mut W, value: &[u8]) -> io::Result<()> + where + W: ?Sized + io::Write, + { + tri!(self.begin_array(writer)); + let mut first = true; + for byte in value { + tri!(self.begin_array_value(writer, first)); + tri!(self.write_u8(writer, *byte)); + tri!(self.end_array_value(writer)); + first = false; + } + self.end_array(writer) } /// Called before every array. Writes a `[` to the specified @@ -1923,7 +1935,7 @@ pub trait Formatter { } /// This structure compacts a JSON value with no extra whitespace. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct CompactFormatter; impl Formatter for CompactFormatter {} @@ -1989,13 +2001,8 @@ impl<'a> Formatter for PrettyFormatter<'a> { where W: ?Sized + io::Write, { - if first { - tri!(writer.write_all(b"\n")); - } else { - tri!(writer.write_all(b",\n")); - } - tri!(indent(writer, self.current_indent, self.indent)); - Ok(()) + tri!(writer.write_all(if first { b"\n" } else { b",\n" })); + indent(writer, self.current_indent, self.indent) } #[inline] @@ -2037,11 +2044,7 @@ impl<'a> Formatter for PrettyFormatter<'a> { where W: ?Sized + io::Write, { - if first { - tri!(writer.write_all(b"\n")); - } else { - tri!(writer.write_all(b",\n")); - } + tri!(writer.write_all(if first { b"\n" } else { b",\n" })); indent(writer, self.current_indent, self.indent) } @@ -2070,8 +2073,7 @@ where { tri!(formatter.begin_string(writer)); tri!(format_escaped_str_contents(writer, formatter, value)); - tri!(formatter.end_string(writer)); - Ok(()) + formatter.end_string(writer) } fn format_escaped_str_contents( @@ -2083,31 +2085,51 @@ where W: ?Sized + io::Write, F: ?Sized + Formatter, { - let bytes = value.as_bytes(); + let mut bytes = value.as_bytes(); - let mut start = 0; + let mut i = 0; + while i < bytes.len() { + let (string_run, rest) = bytes.split_at(i); + let (&byte, rest) = rest.split_first().unwrap(); - for (i, &byte) in bytes.iter().enumerate() { let escape = ESCAPE[byte as usize]; + + i += 1; if escape == 0 { continue; } - if start < i { - tri!(formatter.write_string_fragment(writer, &value[start..i])); + bytes = rest; + i = 0; + + // Safety: string_run is a valid utf8 string, since we only split on ascii sequences + let string_run = unsafe { str::from_utf8_unchecked(string_run) }; + if !string_run.is_empty() { + tri!(formatter.write_string_fragment(writer, string_run)); } - let char_escape = CharEscape::from_escape_table(escape, byte); + let char_escape = match escape { + self::BB => CharEscape::Backspace, + self::TT => CharEscape::Tab, + self::NN => CharEscape::LineFeed, + self::FF => CharEscape::FormFeed, + self::RR => CharEscape::CarriageReturn, + self::QU => CharEscape::Quote, + self::BS => CharEscape::ReverseSolidus, + self::UU => CharEscape::AsciiControl(byte), + // Safety: the escape table does not contain any other type of character. + _ => unsafe { hint::unreachable_unchecked() }, + }; tri!(formatter.write_char_escape(writer, char_escape)); - - start = i + 1; } - if start != bytes.len() { - tri!(formatter.write_string_fragment(writer, &value[start..])); + // Safety: bytes is a valid utf8 string, since we only split on ascii sequences + let string_run = unsafe { str::from_utf8_unchecked(bytes) }; + if string_run.is_empty() { + return Ok(()); } - Ok(()) + formatter.write_string_fragment(writer, string_run) } const BB: u8 = b'b'; // \x08 @@ -2142,39 +2164,43 @@ static ESCAPE: [u8; 256] = [ __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F ]; -/// Serialize the given data structure as JSON into the IO stream. +/// Serialize the given data structure as JSON into the I/O stream. +/// +/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer. /// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. #[inline] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_writer(writer: W, value: &T) -> Result<()> where W: io::Write, T: ?Sized + Serialize, { let mut ser = Serializer::new(writer); - tri!(value.serialize(&mut ser)); - Ok(()) + value.serialize(&mut ser) } -/// Serialize the given data structure as pretty-printed JSON into the IO +/// Serialize the given data structure as pretty-printed JSON into the I/O /// stream. /// +/// Serialization guarantees it only feeds valid UTF-8 sequences to the writer. +/// /// # Errors /// /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. #[inline] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_writer_pretty(writer: W, value: &T) -> Result<()> where W: io::Write, T: ?Sized + Serialize, { let mut ser = Serializer::pretty(writer); - tri!(value.serialize(&mut ser)); - Ok(()) + value.serialize(&mut ser) } /// Serialize the given data structure as a JSON byte vector. diff --git a/src/value/de.rs b/src/value/de.rs index a66d69239..9c3b35be6 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,14 +1,20 @@ -use crate::error::Error; -use crate::lib::str::FromStr; -use crate::lib::*; +use crate::error::{Error, ErrorCode}; use crate::map::Map; use crate::number::Number; use crate::value::Value; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; +#[cfg(feature = "raw_value")] +use alloc::string::ToString; +use alloc::vec::{self, Vec}; +use core::fmt; +use core::slice; +use core::str::FromStr; use serde::de::{ - self, Deserialize, DeserializeSeed, EnumAccess, Expected, IntoDeserializer, MapAccess, - SeqAccess, Unexpected, VariantAccess, Visitor, + self, Deserialize, DeserializeSeed, Deserializer as _, EnumAccess, Expected, IntoDeserializer, + MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, }; -use serde::{forward_to_deserialize_any, serde_if_integer128}; +use serde::forward_to_deserialize_any; #[cfg(feature = "arbitrary_precision")] use crate::number::NumberFromString; @@ -38,11 +44,27 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + fn visit_i128(self, value: i128) -> Result + where + E: serde::de::Error, + { + let de = serde::de::value::I128Deserializer::new(value); + Number::deserialize(de).map(Value::Number) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(Value::Number(value.into())) } + fn visit_u128(self, value: u128) -> Result + where + E: serde::de::Error, + { + let de = serde::de::value::U128Deserializer::new(value); + Number::deserialize(de).map(Value::Number) + } + #[inline] fn visit_f64(self, value: f64) -> Result { Ok(Number::from_f64(value).map_or(Value::Null, Value::Number)) @@ -100,15 +122,15 @@ impl<'de> Deserialize<'de> for Value { where V: MapAccess<'de>, { - match visitor.next_key_seed(KeyClassifier)? { + match tri!(visitor.next_key_seed(KeyClassifier)) { #[cfg(feature = "arbitrary_precision")] Some(KeyClass::Number) => { - let number: NumberFromString = visitor.next_value()?; + let number: NumberFromString = tri!(visitor.next_value()); Ok(Value::Number(number.value)) } #[cfg(feature = "raw_value")] Some(KeyClass::RawValue) => { - let value = visitor.next_value_seed(crate::raw::BoxedFromString)?; + let value = tri!(visitor.next_value_seed(crate::raw::BoxedFromString)); crate::from_str(value.get()).map_err(de::Error::custom) } Some(KeyClass::Map(first_key)) => { @@ -133,7 +155,14 @@ impl<'de> Deserialize<'de> for Value { impl FromStr for Value { type Err = Error; fn from_str(s: &str) -> Result { - super::super::de::from_str(s) + crate::from_str(s) + } +} + +impl FromStr for Map { + type Err = Error; + fn from_str(s: &str) -> Result { + crate::from_str(s) } } @@ -181,21 +210,72 @@ where } } -fn visit_object<'de, V>(object: Map, visitor: V) -> Result -where - V: Visitor<'de>, -{ - let len = object.len(); - let mut deserializer = MapDeserializer::new(object); - let map = tri!(visitor.visit_map(&mut deserializer)); - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(map) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in map", - )) +impl<'de> serde::Deserializer<'de> for Map { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let len = self.len(); + let mut deserializer = MapDeserializer::new(self); + let map = tri!(visitor.visit_map(&mut deserializer)); + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in map", + )) + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + let mut iter = self.into_iter(); + let (variant, value) = match iter.next() { + Some(v) => v, + None => { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + }; + // enums are encoded in json as maps with a single key:value pair + if iter.next().is_some() { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + + visitor.visit_enum(EnumDeserializer { + variant, + value: Some(value), + }) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + drop(self); + visitor.visit_unit() + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct identifier } } @@ -213,8 +293,10 @@ impl<'de> serde::Deserializer<'de> for Value { Value::Number(n) => n.deserialize_any(visitor), #[cfg(any(feature = "std", feature = "alloc"))] Value::String(v) => visitor.visit_string(v), + #[cfg(not(any(feature = "std", feature = "alloc")))] + Value::String(_) => unreachable!(), Value::Array(v) => visit_array(v, visitor), - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), } } @@ -222,18 +304,15 @@ impl<'de> serde::Deserializer<'de> for Value { deserialize_number!(deserialize_i16); deserialize_number!(deserialize_i32); deserialize_number!(deserialize_i64); + deserialize_number!(deserialize_i128); deserialize_number!(deserialize_u8); deserialize_number!(deserialize_u16); deserialize_number!(deserialize_u32); deserialize_number!(deserialize_u64); + deserialize_number!(deserialize_u128); deserialize_number!(deserialize_f32); deserialize_number!(deserialize_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128); - deserialize_number!(deserialize_u128); - } - #[inline] fn deserialize_option(self, visitor: V) -> Result where @@ -248,44 +327,24 @@ impl<'de> serde::Deserializer<'de> for Value { #[inline] fn deserialize_enum( self, - _name: &str, - _variants: &'static [&'static str], + name: &'static str, + variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { - let (variant, value) = match self { - Value::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - }; - // enums are encoded in json as maps with a single key:value pair - if iter.next().is_some() { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - (variant, Some(value)) - } - Value::String(variant) => (variant, None), - other => { - return Err(serde::de::Error::invalid_type( - other.unexpected(), - &"string or map", - )); - } - }; - - visitor.visit_enum(EnumDeserializer { variant, value }) + match self { + Value::Object(value) => value.deserialize_enum(name, variants, visitor), + Value::String(variant) => visitor.visit_enum(EnumDeserializer { + variant, + value: None, + }), + other => Err(serde::de::Error::invalid_type( + other.unexpected(), + &"string or map", + )), + } } #[inline] @@ -415,7 +474,7 @@ impl<'de> serde::Deserializer<'de> for Value { V: Visitor<'de>, { match self { - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -431,7 +490,7 @@ impl<'de> serde::Deserializer<'de> for Value { { match self { Value::Array(v) => visit_array(v, visitor), - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -479,6 +538,14 @@ impl<'de> IntoDeserializer<'de, Error> for Value { } } +impl<'de> IntoDeserializer<'de, Error> for &'de Value { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + struct VariantDeserializer { value: Option, } @@ -512,7 +579,11 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { { match self.value { Some(Value::Array(v)) => { - serde::Deserializer::deserialize_any(SeqDeserializer::new(v), visitor) + if v.is_empty() { + visitor.visit_unit() + } else { + visit_array(v, visitor) + } } Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), @@ -534,9 +605,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { V: Visitor<'de>, { match self.value { - Some(Value::Object(v)) => { - serde::Deserializer::deserialize_any(MapDeserializer::new(v), visitor) - } + Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -561,38 +630,6 @@ impl SeqDeserializer { } } -impl<'de> serde::Deserializer<'de> for SeqDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = tri!(visitor.visit_seq(&mut self)); - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - impl<'de> SeqAccess<'de> for SeqDeserializer { type Error = Error; @@ -665,24 +702,6 @@ impl<'de> MapAccess<'de> for MapDeserializer { } } -impl<'de> serde::Deserializer<'de> for MapDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - macro_rules! deserialize_value_ref_number { ($method:ident) => { #[cfg(not(feature = "arbitrary_precision"))] @@ -690,8 +709,8 @@ macro_rules! deserialize_value_ref_number { where V: Visitor<'de>, { - match *self { - Value::Number(ref n) => n.deserialize_any(visitor), + match self { + Value::Number(n) => n.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -701,8 +720,8 @@ macro_rules! deserialize_value_ref_number { where V: Visitor<'de>, { - match *self { - Value::Number(ref n) => n.$method(visitor), + match self { + Value::Number(n) => n.$method(visitor), _ => self.deserialize_any(visitor), } } @@ -727,21 +746,71 @@ where } } -fn visit_object_ref<'de, V>(object: &'de Map, visitor: V) -> Result -where - V: Visitor<'de>, -{ - let len = object.len(); - let mut deserializer = MapRefDeserializer::new(object); - let map = tri!(visitor.visit_map(&mut deserializer)); - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(map) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in map", - )) +impl<'de> serde::Deserializer<'de> for &'de Map { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let len = self.len(); + let mut deserializer = MapRefDeserializer::new(self); + let map = tri!(visitor.visit_map(&mut deserializer)); + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in map", + )) + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + let mut iter = self.into_iter(); + let (variant, value) = match iter.next() { + Some(v) => v, + None => { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + }; + // enums are encoded in json as maps with a single key:value pair + if iter.next().is_some() { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + + visitor.visit_enum(EnumRefDeserializer { + variant, + value: Some(value), + }) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct identifier } } @@ -752,13 +821,13 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { + match self { Value::Null => visitor.visit_unit(), - Value::Bool(v) => visitor.visit_bool(v), - Value::Number(ref n) => n.deserialize_any(visitor), - Value::String(ref v) => visitor.visit_borrowed_str(v), - Value::Array(ref v) => visit_array_ref(v, visitor), - Value::Object(ref v) => visit_object_ref(v, visitor), + Value::Bool(v) => visitor.visit_bool(*v), + Value::Number(n) => n.deserialize_any(visitor), + Value::String(v) => visitor.visit_borrowed_str(v), + Value::Array(v) => visit_array_ref(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), } } @@ -766,18 +835,15 @@ impl<'de> serde::Deserializer<'de> for &'de Value { deserialize_value_ref_number!(deserialize_i16); deserialize_value_ref_number!(deserialize_i32); deserialize_value_ref_number!(deserialize_i64); + deserialize_number!(deserialize_i128); deserialize_value_ref_number!(deserialize_u8); deserialize_value_ref_number!(deserialize_u16); deserialize_value_ref_number!(deserialize_u32); deserialize_value_ref_number!(deserialize_u64); + deserialize_number!(deserialize_u128); deserialize_value_ref_number!(deserialize_f32); deserialize_value_ref_number!(deserialize_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128); - deserialize_number!(deserialize_u128); - } - fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, @@ -790,44 +856,24 @@ impl<'de> serde::Deserializer<'de> for &'de Value { fn deserialize_enum( self, - _name: &str, - _variants: &'static [&'static str], + name: &'static str, + variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { - let (variant, value) = match *self { - Value::Object(ref value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - }; - // enums are encoded in json as maps with a single key:value pair - if iter.next().is_some() { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - (variant, Some(value)) - } - Value::String(ref variant) => (variant, None), - ref other => { - return Err(serde::de::Error::invalid_type( - other.unexpected(), - &"string or map", - )); - } - }; - - visitor.visit_enum(EnumRefDeserializer { variant, value }) + match self { + Value::Object(value) => value.deserialize_enum(name, variants, visitor), + Value::String(variant) => visitor.visit_enum(EnumRefDeserializer { + variant, + value: None, + }), + other => Err(serde::de::Error::invalid_type( + other.unexpected(), + &"string or map", + )), + } } #[inline] @@ -873,8 +919,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::String(ref v) => visitor.visit_borrowed_str(v), + match self { + Value::String(v) => visitor.visit_borrowed_str(v), _ => Err(self.invalid_type(&visitor)), } } @@ -890,9 +936,9 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::String(ref v) => visitor.visit_borrowed_str(v), - Value::Array(ref v) => visit_array_ref(v, visitor), + match self { + Value::String(v) => visitor.visit_borrowed_str(v), + Value::Array(v) => visit_array_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -925,8 +971,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Array(ref v) => visit_array_ref(v, visitor), + match self { + Value::Array(v) => visit_array_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -954,8 +1000,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Object(ref v) => visit_object_ref(v, visitor), + match self { + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -969,9 +1015,9 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Array(ref v) => visit_array_ref(v, visitor), - Value::Object(ref v) => visit_object_ref(v, visitor), + match self { + Value::Array(v) => visit_array_ref(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -1042,8 +1088,12 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Array(ref v)) => { - serde::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor) + Some(Value::Array(v)) => { + if v.is_empty() { + visitor.visit_unit() + } else { + visit_array_ref(v, visitor) + } } Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), @@ -1065,9 +1115,7 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Object(ref v)) => { - serde::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor) - } + Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -1090,38 +1138,6 @@ impl<'de> SeqRefDeserializer<'de> { } } -impl<'de> serde::Deserializer<'de> for SeqRefDeserializer<'de> { - type Error = Error; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = tri!(visitor.visit_seq(&mut self)); - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - impl<'de> SeqAccess<'de> for SeqRefDeserializer<'de> { type Error = Error; @@ -1194,40 +1210,34 @@ impl<'de> MapAccess<'de> for MapRefDeserializer<'de> { } } -impl<'de> serde::Deserializer<'de> for MapRefDeserializer<'de> { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - struct MapKeyDeserializer<'de> { key: Cow<'de, str>, } -macro_rules! deserialize_integer_key { - ($method:ident => $visit:ident) => { +macro_rules! deserialize_numeric_key { + ($method:ident) => { + deserialize_numeric_key!($method, deserialize_number); + }; + + ($method:ident, $using:ident) => { fn $method(self, visitor: V) -> Result where V: Visitor<'de>, { - match (self.key.parse(), self.key) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), - #[cfg(any(feature = "std", feature = "alloc"))] - (Err(_), Cow::Owned(s)) => visitor.visit_string(s), + let mut de = crate::Deserializer::from_str(&self.key); + + match tri!(de.peek()) { + Some(b'0'..=b'9' | b'-') => {} + _ => return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)), + } + + let number = tri!(de.$using(visitor)); + + if tri!(de.peek()).is_some() { + return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)); } + + Ok(number) } }; } @@ -1242,18 +1252,37 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { BorrowedCowStrDeserializer::new(self.key).deserialize_any(visitor) } - deserialize_integer_key!(deserialize_i8 => visit_i8); - deserialize_integer_key!(deserialize_i16 => visit_i16); - deserialize_integer_key!(deserialize_i32 => visit_i32); - deserialize_integer_key!(deserialize_i64 => visit_i64); - deserialize_integer_key!(deserialize_u8 => visit_u8); - deserialize_integer_key!(deserialize_u16 => visit_u16); - deserialize_integer_key!(deserialize_u32 => visit_u32); - deserialize_integer_key!(deserialize_u64 => visit_u64); + deserialize_numeric_key!(deserialize_i8); + deserialize_numeric_key!(deserialize_i16); + deserialize_numeric_key!(deserialize_i32); + deserialize_numeric_key!(deserialize_i64); + deserialize_numeric_key!(deserialize_u8); + deserialize_numeric_key!(deserialize_u16); + deserialize_numeric_key!(deserialize_u32); + deserialize_numeric_key!(deserialize_u64); + #[cfg(not(feature = "float_roundtrip"))] + deserialize_numeric_key!(deserialize_f32); + deserialize_numeric_key!(deserialize_f64); + + #[cfg(feature = "float_roundtrip")] + deserialize_numeric_key!(deserialize_f32, do_deserialize_f32); + deserialize_numeric_key!(deserialize_i128, do_deserialize_i128); + deserialize_numeric_key!(deserialize_u128, do_deserialize_u128); - serde_if_integer128! { - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u128 => visit_u128); + fn deserialize_bool(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.key == "true" { + visitor.visit_bool(true) + } else if self.key == "false" { + visitor.visit_bool(false) + } else { + Err(serde::de::Error::invalid_type( + Unexpected::Str(&self.key), + &visitor, + )) + } } #[inline] @@ -1292,8 +1321,8 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { } forward_to_deserialize_any! { - bool f32 f64 char str string bytes byte_buf unit unit_struct seq tuple - tuple_struct map struct identifier ignored_any + char str string bytes byte_buf unit unit_struct seq tuple tuple_struct + map struct identifier ignored_any } } @@ -1364,11 +1393,11 @@ impl Value { #[cold] fn unexpected(&self) -> Unexpected { - match *self { + match self { Value::Null => Unexpected::Unit, - Value::Bool(b) => Unexpected::Bool(b), - Value::Number(ref n) => n.unexpected(), - Value::String(ref s) => Unexpected::Str(s), + Value::Bool(b) => Unexpected::Bool(*b), + Value::Number(n) => n.unexpected(), + Value::String(s) => Unexpected::Str(s), Value::Array(_) => Unexpected::Seq, Value::Object(_) => Unexpected::Map, } @@ -1396,6 +1425,8 @@ impl<'de> de::Deserializer<'de> for BorrowedCowStrDeserializer<'de> { Cow::Borrowed(string) => visitor.visit_borrowed_str(string), #[cfg(any(feature = "std", feature = "alloc"))] Cow::Owned(string) => visitor.visit_string(string), + #[cfg(not(any(feature = "std", feature = "alloc")))] + Cow::Owned(_) => unreachable!(), } } @@ -1426,7 +1457,7 @@ impl<'de> de::EnumAccess<'de> for BorrowedCowStrDeserializer<'de> { where T: de::DeserializeSeed<'de>, { - let value = seed.deserialize(self)?; + let value = tri!(seed.deserialize(self)); Ok((value, UnitOnly)) } } diff --git a/src/value/from.rs b/src/value/from.rs index 4423a02fb..df4b2038c 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,11 +1,9 @@ use super::Value; -use crate::lib::iter::FromIterator; -use crate::lib::*; use crate::map::Map; use crate::number::Number; - -#[cfg(feature = "arbitrary_precision")] -use serde::serde_if_integer128; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; +use alloc::vec::Vec; macro_rules! from_integer { ($($ty:ident)*) => { @@ -25,14 +23,13 @@ from_integer! { } #[cfg(feature = "arbitrary_precision")] -serde_if_integer128! { - from_integer! { - i128 u128 - } +from_integer! { + i128 u128 } impl From for Value { - /// Convert 32-bit floating point number to `Value` + /// Convert 32-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -43,12 +40,13 @@ impl From for Value { /// let x: Value = f.into(); /// ``` fn from(f: f32) -> Self { - From::from(f as f64) + Number::from_f32(f).map_or(Value::Null, Value::Number) } } impl From for Value { - /// Convert 64-bit floating point number to `Value` + /// Convert 64-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -64,7 +62,7 @@ impl From for Value { } impl From for Value { - /// Convert boolean to `Value` + /// Convert boolean to `Value::Bool`. /// /// # Examples /// @@ -80,14 +78,14 @@ impl From for Value { } impl From for Value { - /// Convert `String` to `Value` + /// Convert `String` to `Value::String`. /// /// # Examples /// /// ``` /// use serde_json::Value; /// - /// let s: String = "lorem".to_string(); + /// let s: String = "lorem".to_owned(); /// let x: Value = s.into(); /// ``` fn from(f: String) -> Self { @@ -95,8 +93,8 @@ impl From for Value { } } -impl<'a> From<&'a str> for Value { - /// Convert string slice to `Value` +impl From<&str> for Value { + /// Convert string slice to `Value::String`. /// /// # Examples /// @@ -107,12 +105,12 @@ impl<'a> From<&'a str> for Value { /// let x: Value = s.into(); /// ``` fn from(f: &str) -> Self { - Value::String(f.to_string()) + Value::String(f.to_owned()) } } impl<'a> From> for Value { - /// Convert copy-on-write string to `Value` + /// Convert copy-on-write string to `Value::String`. /// /// # Examples /// @@ -128,7 +126,7 @@ impl<'a> From> for Value { /// use serde_json::Value; /// use std::borrow::Cow; /// - /// let s: Cow = Cow::Owned("lorem".to_string()); + /// let s: Cow = Cow::Owned("lorem".to_owned()); /// let x: Value = s.into(); /// ``` fn from(f: Cow<'a, str>) -> Self { @@ -136,8 +134,24 @@ impl<'a> From> for Value { } } +impl From for Value { + /// Convert `Number` to `Value::Number`. + /// + /// # Examples + /// + /// ``` + /// use serde_json::{Number, Value}; + /// + /// let n = Number::from(7); + /// let x: Value = n.into(); + /// ``` + fn from(f: Number) -> Self { + Value::Number(f) + } +} + impl From> for Value { - /// Convert map (with string keys) to `Value` + /// Convert map (with string keys) to `Value::Object`. /// /// # Examples /// @@ -145,7 +159,7 @@ impl From> for Value { /// use serde_json::{Map, Value}; /// /// let mut m = Map::new(); - /// m.insert("Lorem".to_string(), "ipsum".into()); + /// m.insert("Lorem".to_owned(), "ipsum".into()); /// let x: Value = m.into(); /// ``` fn from(f: Map) -> Self { @@ -154,7 +168,7 @@ impl From> for Value { } impl> From> for Value { - /// Convert a `Vec` to `Value` + /// Convert a `Vec` to `Value::Array`. /// /// # Examples /// @@ -169,8 +183,14 @@ impl> From> for Value { } } -impl<'a, T: Clone + Into> From<&'a [T]> for Value { - /// Convert a slice to `Value` +impl, const N: usize> From<[T; N]> for Value { + fn from(array: [T; N]) -> Self { + Value::Array(array.into_iter().map(Into::into).collect()) + } +} + +impl> From<&[T]> for Value { + /// Convert a slice to `Value::Array`. /// /// # Examples /// @@ -180,13 +200,13 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { /// let v: &[&str] = &["lorem", "ipsum", "dolor"]; /// let x: Value = v.into(); /// ``` - fn from(f: &'a [T]) -> Self { + fn from(f: &[T]) -> Self { Value::Array(f.iter().cloned().map(Into::into).collect()) } } impl> FromIterator for Value { - /// Convert an iteratable type to a `Value` + /// Create a `Value::Array` by collecting an iterator of array elements. /// /// # Examples /// @@ -216,7 +236,7 @@ impl> FromIterator for Value { } impl, V: Into> FromIterator<(K, V)> for Value { - /// Convert an iteratable type to a `Value` + /// Create a `Value::Object` by collecting an iterator of key-value pairs. /// /// # Examples /// @@ -227,12 +247,16 @@ impl, V: Into> FromIterator<(K, V)> for Value { /// let x: Value = v.into_iter().collect(); /// ``` fn from_iter>(iter: I) -> Self { - Value::Object(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + Value::Object( + iter.into_iter() + .map(|(k, v)| (k.into(), v.into())) + .collect(), + ) } } impl From<()> for Value { - /// Convert `()` to `Value` + /// Convert `()` to `Value::Null`. /// /// # Examples /// @@ -246,3 +270,15 @@ impl From<()> for Value { Value::Null } } + +impl From> for Value +where + T: Into, +{ + fn from(opt: Option) -> Self { + match opt { + None => Value::Null, + Some(value) => Into::into(value), + } + } +} diff --git a/src/value/index.rs b/src/value/index.rs index d759a1df0..7b0011004 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,6 +1,9 @@ use super::Value; -use crate::lib::*; use crate::map::Map; +use alloc::borrow::ToOwned; +use alloc::string::String; +use core::fmt::{self, Display}; +use core::ops; /// A type that can be used to index into a `serde_json::Value`. /// @@ -9,9 +12,9 @@ use crate::map::Map; /// trait is implemented for strings which are used as the index into a JSON /// map, and for `usize` which is used as the index into a JSON array. /// -/// [`get`]: ../enum.Value.html#method.get -/// [`get_mut`]: ../enum.Value.html#method.get_mut -/// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E +/// [`get`]: Value::get +/// [`get_mut`]: Value::get_mut +/// [square-bracket indexing operator]: Value#impl-Index%3CI%3E-for-Value /// /// This trait is sealed and cannot be implemented for types outside of /// `serde_json`. @@ -50,20 +53,20 @@ pub trait Index: private::Sealed { impl Index for usize { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { - match *v { - Value::Array(ref vec) => vec.get(*self), + match v { + Value::Array(vec) => vec.get(*self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { - match *v { - Value::Array(ref mut vec) => vec.get_mut(*self), + match v { + Value::Array(vec) => vec.get_mut(*self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { - match *v { - Value::Array(ref mut vec) => { + match v { + Value::Array(vec) => { let len = vec.len(); vec.get_mut(*self).unwrap_or_else(|| { panic!( @@ -79,23 +82,23 @@ impl Index for usize { impl Index for str { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { - match *v { - Value::Object(ref map) => map.get(self), + match v { + Value::Object(map) => map.get(self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { - match *v { - Value::Object(ref mut map) => map.get_mut(self), + match v { + Value::Object(map) => map.get_mut(self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { - if let Value::Null = *v { + if let Value::Null = v { *v = Value::Object(Map::new()); } - match *v { - Value::Object(ref mut map) => map.entry(self.to_owned()).or_insert(Value::Null), + match v { + Value::Object(map) => map.entry(self.to_owned()).or_insert(Value::Null), _ => panic!("cannot access key {:?} in JSON {}", self, Type(v)), } } @@ -113,7 +116,7 @@ impl Index for String { } } -impl<'a, T> Index for &'a T +impl Index for &T where T: ?Sized + Index, { @@ -133,14 +136,14 @@ mod private { pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} - impl Sealed for super::String {} - impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} + impl Sealed for alloc::string::String {} + impl Sealed for &T where T: ?Sized + Sealed {} } /// Used in panic messages. struct Type<'a>(&'a Value); -impl<'a> fmt::Display for Type<'a> { +impl<'a> Display for Type<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self.0 { Value::Null => formatter.write_str("null"), diff --git a/src/value/mod.rs b/src/value/mod.rs index 07f4cc27e..7c562f82d 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -85,14 +85,18 @@ //! # untyped_example().unwrap(); //! ``` //! -//! [macro]: https://docs.serde.rs/serde_json/macro.json.html -//! [from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html -//! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html -//! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html +//! [macro]: crate::json +//! [from_str]: crate::de::from_str +//! [from_slice]: crate::de::from_slice +//! [from_reader]: crate::de::from_reader use crate::error::Error; use crate::io; -use crate::lib::*; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt::{self, Debug, Display}; +use core::mem; +use core::str; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -102,12 +106,13 @@ pub use crate::map::Map; pub use crate::number::Number; #[cfg(feature = "raw_value")] +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. /// -/// See the `serde_json::value` module documentation for usage examples. -#[derive(Clone, Eq, PartialEq)] +/// See the [`serde_json::value` module documentation](self) for usage examples. +#[derive(Clone, Eq, PartialEq, Hash)] pub enum Value { /// Represents a JSON null value. /// @@ -172,47 +177,24 @@ pub enum Value { impl Debug for Value { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match *self { - Value::Null => formatter.debug_tuple("Null").finish(), - Value::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(), - Value::Number(ref v) => Debug::fmt(v, formatter), - Value::String(ref v) => formatter.debug_tuple("String").field(v).finish(), - Value::Array(ref v) => { - formatter.write_str("Array(")?; - Debug::fmt(v, formatter)?; - formatter.write_str(")") + match self { + Value::Null => formatter.write_str("Null"), + Value::Bool(boolean) => write!(formatter, "Bool({})", boolean), + Value::Number(number) => Debug::fmt(number, formatter), + Value::String(string) => write!(formatter, "String({:?})", string), + Value::Array(vec) => { + tri!(formatter.write_str("Array ")); + Debug::fmt(vec, formatter) } - Value::Object(ref v) => { - formatter.write_str("Object(")?; - Debug::fmt(v, formatter)?; - formatter.write_str(")") + Value::Object(map) => { + tri!(formatter.write_str("Object ")); + Debug::fmt(map, formatter) } } } } -struct WriterFormatter<'a, 'b: 'a> { - inner: &'a mut fmt::Formatter<'b>, -} - -impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { - fn write(&mut self, buf: &[u8]) -> io::Result { - fn io_error(_: E) -> io::Error { - // Error value does not matter because fmt::Display impl below just - // maps it to fmt::Error - io::Error::new(io::ErrorKind::Other, "fmt error") - } - let s = tri!(str::from_utf8(buf).map_err(io_error)); - tri!(self.inner.write_str(s).map_err(io_error)); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl fmt::Display for Value { +impl Display for Value { /// Display a JSON value as a string. /// /// ``` @@ -238,6 +220,30 @@ impl fmt::Display for Value { /// "{\n \"city\": \"London\",\n \"street\": \"10 Downing Street\"\n}"); /// ``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct WriterFormatter<'a, 'b: 'a> { + inner: &'a mut fmt::Formatter<'b>, + } + + impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { + fn write(&mut self, buf: &[u8]) -> io::Result { + // Safety: the serializer below only emits valid utf8 when using + // the default formatter. + let s = unsafe { str::from_utf8_unchecked(buf) }; + tri!(self.inner.write_str(s).map_err(io_error)); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + fn io_error(_: fmt::Error) -> io::Error { + // Error value does not matter because Display impl just maps it + // back to fmt::Error. + io::Error::new(io::ErrorKind::Other, "fmt error") + } + let alternate = f.alternate(); let mut wr = WriterFormatter { inner: f }; if alternate { @@ -358,8 +364,8 @@ impl Value { /// assert_eq!(v["b"].as_object(), None); /// ``` pub fn as_object(&self) -> Option<&Map> { - match *self { - Value::Object(ref map) => Some(map), + match self { + Value::Object(map) => Some(map), _ => None, } } @@ -376,8 +382,8 @@ impl Value { /// assert_eq!(v, json!({ "a": {} })); /// ``` pub fn as_object_mut(&mut self) -> Option<&mut Map> { - match *self { - Value::Object(ref mut map) => Some(map), + match self { + Value::Object(map) => Some(map), _ => None, } } @@ -417,8 +423,8 @@ impl Value { /// assert_eq!(v["b"].as_array(), None); /// ``` pub fn as_array(&self) -> Option<&Vec> { - match *self { - Value::Array(ref array) => Some(&*array), + match self { + Value::Array(array) => Some(array), _ => None, } } @@ -435,8 +441,8 @@ impl Value { /// assert_eq!(v, json!({ "a": [] })); /// ``` pub fn as_array_mut(&mut self) -> Option<&mut Vec> { - match *self { - Value::Array(ref mut list) => Some(list), + match self { + Value::Array(list) => Some(list), _ => None, } } @@ -484,8 +490,8 @@ impl Value { /// println!("The value is: {}", v["a"].as_str().unwrap()); /// ``` pub fn as_str(&self) -> Option<&str> { - match *self { - Value::String(ref s) => Some(s), + match self { + Value::String(s) => Some(s), _ => None, } } @@ -509,6 +515,28 @@ impl Value { } } + /// If the `Value` is a Number, returns the associated [`Number`]. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::{json, Number}; + /// # + /// let v = json!({ "a": 1, "b": 2.2, "c": -3, "d": "4" }); + /// + /// assert_eq!(v["a"].as_number(), Some(&Number::from(1u64))); + /// assert_eq!(v["b"].as_number(), Some(&Number::from_f64(2.2).unwrap())); + /// assert_eq!(v["c"].as_number(), Some(&Number::from(-3i64))); + /// + /// // The string `"4"` is not a number. + /// assert_eq!(v["d"].as_number(), None); + /// ``` + pub fn as_number(&self) -> Option<&Number> { + match self { + Value::Number(number) => Some(number), + _ => None, + } + } + /// Returns true if the `Value` is an integer between `i64::MIN` and /// `i64::MAX`. /// @@ -530,8 +558,8 @@ impl Value { /// assert!(!v["c"].is_i64()); /// ``` pub fn is_i64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_i64(), + match self { + Value::Number(n) => n.is_i64(), _ => false, } } @@ -555,8 +583,8 @@ impl Value { /// assert!(!v["c"].is_u64()); /// ``` pub fn is_u64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_u64(), + match self { + Value::Number(n) => n.is_u64(), _ => false, } } @@ -581,8 +609,8 @@ impl Value { /// assert!(!v["c"].is_f64()); /// ``` pub fn is_f64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_f64(), + match self { + Value::Number(n) => n.is_f64(), _ => false, } } @@ -601,8 +629,8 @@ impl Value { /// assert_eq!(v["c"].as_i64(), None); /// ``` pub fn as_i64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_i64(), + match self { + Value::Number(n) => n.as_i64(), _ => None, } } @@ -620,8 +648,8 @@ impl Value { /// assert_eq!(v["c"].as_u64(), None); /// ``` pub fn as_u64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_u64(), + match self { + Value::Number(n) => n.as_u64(), _ => None, } } @@ -639,8 +667,8 @@ impl Value { /// assert_eq!(v["c"].as_f64(), Some(-64.0)); /// ``` pub fn as_f64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_f64(), + match self { + Value::Number(n) => n.as_f64(), _ => None, } } @@ -755,25 +783,15 @@ impl Value { if !pointer.starts_with('/') { return None; } - let tokens = pointer + pointer .split('/') .skip(1) - .map(|x| x.replace("~1", "/").replace("~0", "~")); - let mut target = self; - - for token in tokens { - let target_opt = match *target { - Value::Object(ref map) => map.get(&token), - Value::Array(ref list) => parse_index(&token).and_then(|x| list.get(x)), - _ => return None, - }; - if let Some(t) = target_opt { - target = t; - } else { - return None; - } - } - Some(target) + .map(|x| x.replace("~1", "/").replace("~0", "~")) + .try_fold(self, |target, token| match target { + Value::Object(map) => map.get(&token), + Value::Array(list) => parse_index(&token).and_then(|x| list.get(x)), + _ => None, + }) } /// Looks up a value by a JSON Pointer and returns a mutable reference to @@ -820,30 +838,15 @@ impl Value { if !pointer.starts_with('/') { return None; } - let tokens = pointer + pointer .split('/') .skip(1) - .map(|x| x.replace("~1", "/").replace("~0", "~")); - let mut target = self; - - for token in tokens { - // borrow checker gets confused about `target` being mutably borrowed too many times because of the loop - // this once-per-loop binding makes the scope clearer and circumvents the error - let target_once = target; - let target_opt = match *target_once { - Value::Object(ref mut map) => map.get_mut(&token), - Value::Array(ref mut list) => { - parse_index(&token).and_then(move |x| list.get_mut(x)) - } - _ => return None, - }; - if let Some(t) = target_opt { - target = t; - } else { - return None; - } - } - Some(target) + .map(|x| x.replace("~1", "/").replace("~0", "~")) + .try_fold(self, |target, token| match target { + Value::Object(map) => map.get_mut(&token), + Value::Array(list) => parse_index(&token).and_then(move |x| list.get_mut(x)), + _ => None, + }) } /// Takes the value out of the `Value`, leaving a `Null` in its place. @@ -858,6 +861,32 @@ impl Value { pub fn take(&mut self) -> Value { mem::replace(self, Value::Null) } + + /// Reorders the entries of all `Value::Object` nested within this JSON + /// value according to `str`'s usual ordering. + /// + /// If serde_json's "preserve_order" feature is not enabled, this method + /// does no work because all JSON maps are always kept in a sorted state. + /// + /// If serde_json's "preserve_order" feature is enabled, this method + /// destroys the original source order or insertion order of the JSON + /// objects in favor of an alphanumerical order that matches how a BTreeMap + /// with the same contents would be ordered. + pub fn sort_all_objects(&mut self) { + #[cfg(feature = "preserve_order")] + { + match self { + Value::Object(map) => { + map.sort_keys(); + map.values_mut().for_each(Value::sort_all_objects); + } + Value::Array(list) => { + list.iter_mut().for_each(Value::sort_all_objects); + } + _ => {} + } + } + } } /// The default value is `Value::Null`. @@ -895,6 +924,13 @@ impl Default for Value { } } +impl Default for &Value { + fn default() -> Self { + const DEFAULT: Value = Value::Null; + &DEFAULT + } +} + mod de; mod from; mod index; @@ -909,7 +945,6 @@ mod ser; /// ``` /// use serde::Serialize; /// use serde_json::json; -/// /// use std::error::Error; /// /// #[derive(Serialize)] @@ -918,7 +953,7 @@ mod ser; /// location: String, /// } /// -/// fn compare_json_values() -> Result<(), Box> { +/// fn compare_json_values() -> Result<(), Box> { /// let u = User { /// fingerprint: "0xF9BA143B95FF6D82".to_owned(), /// location: "Menlo Park, CA".to_owned(), diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 354ea5a5f..8626eed7f 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,24 +1,31 @@ use super::Value; -use crate::lib::*; +use alloc::string::String; fn eq_i64(value: &Value, other: i64) -> bool { - value.as_i64().map_or(false, |i| i == other) + value.as_i64() == Some(other) } fn eq_u64(value: &Value, other: u64) -> bool { - value.as_u64().map_or(false, |i| i == other) + value.as_u64() == Some(other) +} + +fn eq_f32(value: &Value, other: f32) -> bool { + match value { + Value::Number(n) => n.as_f32() == Some(other), + _ => false, + } } fn eq_f64(value: &Value, other: f64) -> bool { - value.as_f64().map_or(false, |i| i == other) + value.as_f64() == Some(other) } fn eq_bool(value: &Value, other: bool) -> bool { - value.as_bool().map_or(false, |i| i == other) + value.as_bool() == Some(other) } fn eq_str(value: &Value, other: &str) -> bool { - value.as_str().map_or(false, |i| i == other) + value.as_str() == Some(other) } impl PartialEq for Value { @@ -27,7 +34,7 @@ impl PartialEq for Value { } } -impl<'a> PartialEq<&'a str> for Value { +impl PartialEq<&str> for Value { fn eq(&self, other: &&str) -> bool { eq_str(self, *other) } @@ -39,7 +46,7 @@ impl PartialEq for str { } } -impl<'a> PartialEq for &'a str { +impl PartialEq for &str { fn eq(&self, other: &Value) -> bool { eq_str(other, *self) } @@ -90,6 +97,7 @@ macro_rules! partialeq_numeric { partialeq_numeric! { eq_i64[i8 i16 i32 i64 isize] eq_u64[u8 u16 u32 u64 usize] - eq_f64[f32 f64] + eq_f32[f32] + eq_f64[f64] eq_bool[bool] } diff --git a/src/value/ser.rs b/src/value/ser.rs index 03cb12bb1..1ffe7b7f1 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,27 +1,27 @@ use crate::error::{Error, ErrorCode, Result}; -use crate::lib::*; use crate::map::Map; -use crate::number::Number; use crate::value::{to_value, Value}; +use alloc::borrow::ToOwned; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::fmt::Display; +use core::result; use serde::ser::{Impossible, Serialize}; -#[cfg(feature = "arbitrary_precision")] -use serde::serde_if_integer128; - impl Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> result::Result where S: ::serde::Serializer, { - match *self { + match self { Value::Null => serializer.serialize_unit(), - Value::Bool(b) => serializer.serialize_bool(b), - Value::Number(ref n) => n.serialize(serializer), - Value::String(ref s) => serializer.serialize_str(s), - Value::Array(ref v) => v.serialize(serializer), + Value::Bool(b) => serializer.serialize_bool(*b), + Value::Number(n) => n.serialize(serializer), + Value::String(s) => serializer.serialize_str(s), + Value::Array(v) => v.serialize(serializer), #[cfg(any(feature = "std", feature = "alloc"))] - Value::Object(ref m) => { + Value::Object(m) => { use serde::ser::SerializeMap; let mut map = tri!(serializer.serialize_map(Some(m.len()))); for (k, v) in m { @@ -29,6 +29,8 @@ impl Serialize for Value { } map.end() } + #[cfg(not(any(feature = "std", feature = "alloc")))] + Value::Object(_) => unreachable!(), } } } @@ -91,11 +93,22 @@ impl serde::Serializer for Serializer { Ok(Value::Number(value.into())) } - #[cfg(feature = "arbitrary_precision")] - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result { + fn serialize_i128(self, value: i128) -> Result { + #[cfg(feature = "arbitrary_precision")] + { Ok(Value::Number(value.into())) } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(value) = u64::try_from(value) { + Ok(Value::Number(value.into())) + } else if let Ok(value) = i64::try_from(value) { + Ok(Value::Number(value.into())) + } else { + Err(Error::syntax(ErrorCode::NumberOutOfRange, 0, 0)) + } + } } #[inline] @@ -118,21 +131,30 @@ impl serde::Serializer for Serializer { Ok(Value::Number(value.into())) } - #[cfg(feature = "arbitrary_precision")] - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result { + fn serialize_u128(self, value: u128) -> Result { + #[cfg(feature = "arbitrary_precision")] + { Ok(Value::Number(value.into())) } + + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(value) = u64::try_from(value) { + Ok(Value::Number(value.into())) + } else { + Err(Error::syntax(ErrorCode::NumberOutOfRange, 0, 0)) + } + } } #[inline] - fn serialize_f32(self, value: f32) -> Result { - self.serialize_f64(value as f64) + fn serialize_f32(self, float: f32) -> Result { + Ok(Value::from(float)) } #[inline] - fn serialize_f64(self, value: f64) -> Result { - Ok(Number::from_f64(value).map_or(Value::Null, Value::Number)) + fn serialize_f64(self, float: f64) -> Result { + Ok(Value::from(float)) } #[inline] @@ -191,7 +213,7 @@ impl serde::Serializer for Serializer { T: ?Sized + Serialize, { let mut values = Map::new(); - values.insert(String::from(variant), tri!(to_value(&value))); + values.insert(String::from(variant), tri!(to_value(value))); Ok(Value::Object(values)) } @@ -239,9 +261,9 @@ impl serde::Serializer for Serializer { }) } - fn serialize_map(self, _len: Option) -> Result { + fn serialize_map(self, len: Option) -> Result { Ok(SerializeMap::Map { - map: Map::new(), + map: Map::with_capacity(len.unwrap_or(0)), next_key: None, }) } @@ -269,9 +291,9 @@ impl serde::Serializer for Serializer { }) } - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display, + T: ?Sized + Display, { Ok(Value::String(value.to_string())) } @@ -310,7 +332,7 @@ impl serde::ser::SerializeSeq for SerializeVec { where T: ?Sized + Serialize, { - self.vec.push(tri!(to_value(&value))); + self.vec.push(tri!(to_value(value))); Ok(()) } @@ -359,7 +381,7 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { where T: ?Sized + Serialize, { - self.vec.push(tri!(to_value(&value))); + self.vec.push(tri!(to_value(value))); Ok(()) } @@ -380,10 +402,8 @@ impl serde::ser::SerializeMap for SerializeMap { where T: ?Sized + Serialize, { - match *self { - SerializeMap::Map { - ref mut next_key, .. - } => { + match self { + SerializeMap::Map { next_key, .. } => { *next_key = Some(tri!(key.serialize(MapKeySerializer))); Ok(()) } @@ -398,16 +418,13 @@ impl serde::ser::SerializeMap for SerializeMap { where T: ?Sized + Serialize, { - match *self { - SerializeMap::Map { - ref mut map, - ref mut next_key, - } => { + match self { + SerializeMap::Map { map, next_key } => { let key = next_key.take(); // Panic because this indicates a bug in the program rather than an // expected failure. let key = key.expect("serialize_value called before serialize_key"); - map.insert(key, tri!(to_value(&value))); + map.insert(key, tri!(to_value(value))); Ok(()) } #[cfg(feature = "arbitrary_precision")] @@ -434,6 +451,10 @@ fn key_must_be_a_string() -> Error { Error::syntax(ErrorCode::KeyMustBeAString, 0, 0) } +fn float_key_must_be_finite() -> Error { + Error::syntax(ErrorCode::FloatKeyMustBeFinite, 0, 0) +} + impl serde::Serializer for MapKeySerializer { type Ok = String; type Error = Error; @@ -464,48 +485,64 @@ impl serde::Serializer for MapKeySerializer { value.serialize(self) } - fn serialize_bool(self, _value: bool) -> Result { - Err(key_must_be_a_string()) + fn serialize_bool(self, value: bool) -> Result { + Ok(if value { "true" } else { "false" }.to_owned()) } fn serialize_i8(self, value: i8) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i16(self, value: i16) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i32(self, value: i32) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i64(self, value: i64) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) + } + + fn serialize_i128(self, value: i128) -> Result { + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u8(self, value: u8) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u16(self, value: u16) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u32(self, value: u32) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u64(self, value: u64) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } - fn serialize_f32(self, _value: f32) -> Result { - Err(key_must_be_a_string()) + fn serialize_u128(self, value: u128) -> Result { + Ok(itoa::Buffer::new().format(value).to_owned()) } - fn serialize_f64(self, _value: f64) -> Result { - Err(key_must_be_a_string()) + fn serialize_f32(self, value: f32) -> Result { + if value.is_finite() { + Ok(ryu::Buffer::new().format_finite(value).to_owned()) + } else { + Err(float_key_must_be_finite()) + } + } + + fn serialize_f64(self, value: f64) -> Result { + if value.is_finite() { + Ok(ryu::Buffer::new().format_finite(value).to_owned()) + } else { + Err(float_key_must_be_finite()) + } } #[inline] @@ -602,9 +639,9 @@ impl serde::Serializer for MapKeySerializer { Err(key_must_be_a_string()) } - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display, + T: ?Sized + Display, { Ok(value.to_string()) } @@ -618,21 +655,21 @@ impl serde::ser::SerializeStruct for SerializeMap { where T: ?Sized + Serialize, { - match *self { + match self { SerializeMap::Map { .. } => serde::ser::SerializeMap::serialize_entry(self, key, value), #[cfg(feature = "arbitrary_precision")] - SerializeMap::Number { ref mut out_value } => { + SerializeMap::Number { out_value } => { if key == crate::number::TOKEN { - *out_value = Some(value.serialize(NumberValueEmitter)?); + *out_value = Some(tri!(value.serialize(NumberValueEmitter))); Ok(()) } else { Err(invalid_number()) } } #[cfg(feature = "raw_value")] - SerializeMap::RawValue { ref mut out_value } => { + SerializeMap::RawValue { out_value } => { if key == crate::raw::TOKEN { - *out_value = Some(value.serialize(RawValueEmitter)?); + *out_value = Some(tri!(value.serialize(RawValueEmitter))); Ok(()) } else { Err(invalid_raw_value()) @@ -664,7 +701,7 @@ impl serde::ser::SerializeStructVariant for SerializeStructVariant { where T: ?Sized + Serialize, { - self.map.insert(String::from(key), tri!(to_value(&value))); + self.map.insert(String::from(key), tri!(to_value(value))); Ok(()) } @@ -1016,4 +1053,11 @@ impl serde::ser::Serializer for RawValueEmitter { ) -> Result { Err(invalid_raw_value()) } + + fn collect_str(self, value: &T) -> Result + where + T: ?Sized + Display, + { + self.serialize_str(&value.to_string()) + } } diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f9aea23b5..23a6a065e 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ -#[rustversion::attr(not(nightly), ignore)] +#[rustversion::attr(not(nightly), ignore = "requires nightly")] +#[cfg_attr(miri, ignore = "incompatible with miri")] #[test] fn ui() { let t = trybuild::TestCases::new(); diff --git a/tests/crate/.gitignore b/tests/crate/.gitignore new file mode 100644 index 000000000..e9e21997b --- /dev/null +++ b/tests/crate/.gitignore @@ -0,0 +1,2 @@ +/target/ +/Cargo.lock diff --git a/tests/crate/Cargo.toml b/tests/crate/Cargo.toml index 59da7c726..e13df6a8e 100644 --- a/tests/crate/Cargo.toml +++ b/tests/crate/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "serde_json_test" version = "0.0.0" -edition = "2018" +authors = ["David Tolnay "] +edition = "2021" publish = false [lib] @@ -14,7 +15,8 @@ serde_json = { path = "../..", default-features = false } default = ["std"] std = ["serde_json/std"] alloc = ["serde_json/alloc"] -preserve_order = ["serde_json/preserve_order"] +#preserve_order = ["serde_json/preserve_order"] +float_roundtrip = ["serde_json/float_roundtrip"] arbitrary_precision = ["serde_json/arbitrary_precision"] raw_value = ["serde_json/raw_value"] unbounded_depth = ["serde_json/unbounded_depth"] diff --git a/tests/debug.rs b/tests/debug.rs index d2d8448be..8ddcf5a38 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -1,3 +1,4 @@ +use indoc::indoc; use serde_json::{json, Number, Value}; #[test] @@ -26,6 +27,8 @@ fn value_number() { assert_eq!(format!("{:?}", json!(1)), "Number(1)"); assert_eq!(format!("{:?}", json!(-1)), "Number(-1)"); assert_eq!(format!("{:?}", json!(1.0)), "Number(1.0)"); + assert_eq!(Number::from_f64(1.0).unwrap().to_string(), "1.0"); // not just "1" + assert_eq!(Number::from_f64(12e40).unwrap().to_string(), "1.2e41"); } #[test] @@ -35,12 +38,12 @@ fn value_string() { #[test] fn value_array() { - assert_eq!(format!("{:?}", json!([])), "Array([])"); + assert_eq!(format!("{:?}", json!([])), "Array []"); } #[test] fn value_object() { - assert_eq!(format!("{:?}", json!({})), "Object({})"); + assert_eq!(format!("{:?}", json!({})), "Object {}"); } #[test] @@ -50,19 +53,29 @@ fn error() { assert_eq!(format!("{:?}", err), expected); } -const INDENTED_EXPECTED: &str = r#"Object({ - "array": Array([ - Number( - 0, - ), - Number( - 1, - ), - ]), -})"#; - #[test] fn indented() { - let j = json!({ "array": [0, 1] }); - assert_eq!(format!("{:#?}", j), INDENTED_EXPECTED); + let j = json!({ + "Array": [true], + "Bool": true, + "EmptyArray": [], + "EmptyObject": {}, + "Null": null, + "Number": 1, + "String": "...", + }); + let expected = indoc! {r#" + Object { + "Array": Array [ + Bool(true), + ], + "Bool": Bool(true), + "EmptyArray": Array [], + "EmptyObject": Object {}, + "Null": Null, + "Number": Number(1), + "String": String("..."), + }"# + }; + assert_eq!(format!("{:#?}", j), expected); } diff --git a/tests/lexical.rs b/tests/lexical.rs index 11b870abb..77967956c 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -1,17 +1,32 @@ #![allow( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_sign_loss, clippy::comparison_chain, + clippy::doc_markdown, clippy::excessive_precision, - clippy::float_cmp + clippy::float_cmp, + clippy::if_not_else, + clippy::let_underscore_untyped, + clippy::module_name_repetitions, + clippy::needless_late_init, + clippy::question_mark, + clippy::shadow_unrelated, + clippy::similar_names, + clippy::single_match_else, + clippy::too_many_lines, + clippy::unreadable_literal, + clippy::unseparated_literal_suffix, + clippy::wildcard_imports )] +extern crate alloc; + #[path = "../src/lexical/mod.rs"] mod lexical; -mod lib { - pub use std::vec::Vec; - pub use std::{cmp, iter, mem, ops}; -} - #[path = "lexical/algorithm.rs"] mod algorithm; diff --git a/tests/lexical/exponent.rs b/tests/lexical/exponent.rs index f7a847be3..c109ff07d 100644 --- a/tests/lexical/exponent.rs +++ b/tests/lexical/exponent.rs @@ -17,38 +17,20 @@ fn scientific_exponent_test() { assert_eq!(scientific_exponent(-10, 2, 20), -9); // Underflow - assert_eq!( - scientific_exponent(i32::min_value(), 0, 0), - i32::min_value() - ); - assert_eq!( - scientific_exponent(i32::min_value(), 0, 5), - i32::min_value() - ); + assert_eq!(scientific_exponent(i32::MIN, 0, 0), i32::MIN); + assert_eq!(scientific_exponent(i32::MIN, 0, 5), i32::MIN); // Overflow - assert_eq!( - scientific_exponent(i32::max_value(), 0, 0), - i32::max_value() - 1 - ); - assert_eq!( - scientific_exponent(i32::max_value(), 5, 0), - i32::max_value() - ); + assert_eq!(scientific_exponent(i32::MAX, 0, 0), i32::MAX - 1); + assert_eq!(scientific_exponent(i32::MAX, 5, 0), i32::MAX); } #[test] fn mantissa_exponent_test() { assert_eq!(mantissa_exponent(10, 5, 0), 5); assert_eq!(mantissa_exponent(0, 5, 0), -5); - assert_eq!( - mantissa_exponent(i32::max_value(), 5, 0), - i32::max_value() - 5 - ); - assert_eq!(mantissa_exponent(i32::max_value(), 0, 5), i32::max_value()); - assert_eq!(mantissa_exponent(i32::min_value(), 5, 0), i32::min_value()); - assert_eq!( - mantissa_exponent(i32::min_value(), 0, 5), - i32::min_value() + 5 - ); + assert_eq!(mantissa_exponent(i32::MAX, 5, 0), i32::MAX - 5); + assert_eq!(mantissa_exponent(i32::MAX, 0, 5), i32::MAX); + assert_eq!(mantissa_exponent(i32::MIN, 5, 0), i32::MIN); + assert_eq!(mantissa_exponent(i32::MIN, 0, 5), i32::MIN + 5); } diff --git a/tests/lexical/float.rs b/tests/lexical/float.rs index 554c4c49e..c87e7e1ed 100644 --- a/tests/lexical/float.rs +++ b/tests/lexical/float.rs @@ -181,7 +181,7 @@ fn round_to_f64_test() { // Bug fixes // 1.2345e-308 - check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074) + check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074); } fn assert_normalized_eq(mut x: ExtendedFloat, mut y: ExtendedFloat) { diff --git a/tests/lexical/math.rs b/tests/lexical/math.rs index 79d3ef3ee..454eaa606 100644 --- a/tests/lexical/math.rs +++ b/tests/lexical/math.rs @@ -18,12 +18,12 @@ impl Math for Bigint { } } -#[cfg(limb_width_32)] +#[cfg(fast_arithmetic = "32")] pub(crate) fn from_u32(x: &[u32]) -> Vec { x.iter().cloned().collect() } -#[cfg(limb_width_64)] +#[cfg(fast_arithmetic = "64")] pub(crate) fn from_u32(x: &[u32]) -> Vec { let mut v = Vec::::default(); for xi in x.chunks(2) { diff --git a/tests/lexical/num.rs b/tests/lexical/num.rs index 1a94be013..e7d08652e 100644 --- a/tests/lexical/num.rs +++ b/tests/lexical/num.rs @@ -63,7 +63,6 @@ fn check_float(x: T) { assert!(T::from_bits(x.to_bits()) == x); // Check properties - let _ = x.to_bits() & T::SIGN_MASK; let _ = x.to_bits() & T::EXPONENT_MASK; let _ = x.to_bits() & T::HIDDEN_BIT_MASK; let _ = x.to_bits() & T::MANTISSA_MASK; diff --git a/tests/lexical/parse.rs b/tests/lexical/parse.rs index 80ca25e77..03ec1a9a6 100644 --- a/tests/lexical/parse.rs +++ b/tests/lexical/parse.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/Alexhuszagh/rust-lexical. use crate::lexical::num::Float; -use crate::lexical::parse::{parse_concise_float, parse_truncated_float}; +use crate::lexical::{parse_concise_float, parse_truncated_float}; use core::f64; use core::fmt::Debug; diff --git a/tests/macros/mod.rs b/tests/macros/mod.rs index 8ac4619f7..aaf820f07 100644 --- a/tests/macros/mod.rs +++ b/tests/macros/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_macro_rules)] + macro_rules! json_str { ([]) => { "[]" diff --git a/tests/map.rs b/tests/map.rs index 387a72cdd..aa3cb2549 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -15,6 +15,17 @@ fn test_preserve_order() { assert_eq!(keys, EXPECTED); } +#[test] +#[cfg(feature = "preserve_order")] +fn test_shift_insert() { + let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); + let val = v.as_object_mut().unwrap(); + val.shift_insert(0, "d".to_owned(), Value::Null); + + let keys: Vec<_> = val.keys().collect(); + assert_eq!(keys, &["d", "b", "a", "c"]); +} + #[test] fn test_append() { // Sorted order @@ -34,3 +45,13 @@ fn test_append() { assert_eq!(keys, EXPECTED); assert!(val.is_empty()); } + +#[test] +fn test_retain() { + let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); + let val = v.as_object_mut().unwrap(); + val.retain(|k, _| k.as_str() != "b"); + + let keys: Vec<_> = val.keys().collect(); + assert_eq!(keys, &["a", "c"]); +} diff --git a/tests/regression.rs b/tests/regression.rs index fb2b25c89..315bb1545 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1,3 +1,5 @@ +#![allow(clippy::elidable_lifetime_names, clippy::needless_lifetimes)] + mod regression { automod::dir!("tests/regression"); } diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs new file mode 100644 index 000000000..c09fb9610 --- /dev/null +++ b/tests/regression/issue1004.rs @@ -0,0 +1,12 @@ +#![cfg(feature = "arbitrary_precision")] + +#[test] +fn test() { + let float = 5.55f32; + let value = serde_json::to_value(float).unwrap(); + let json = serde_json::to_string(&value).unwrap(); + + // If the f32 were cast to f64 by Value before serialization, then this + // would incorrectly serialize as 5.550000190734863. + assert_eq!(json, "5.55"); +} diff --git a/tests/regression/issue520.rs b/tests/regression/issue520.rs index 9ed367731..730ecc60a 100644 --- a/tests/regression/issue520.rs +++ b/tests/regression/issue520.rs @@ -1,6 +1,6 @@ #![allow(clippy::float_cmp)] -use serde_derive::{Serialize, Deserialize}; +use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type", content = "data")] diff --git a/tests/regression/issue795.rs b/tests/regression/issue795.rs new file mode 100644 index 000000000..411e8af5d --- /dev/null +++ b/tests/regression/issue795.rs @@ -0,0 +1,62 @@ +#![allow(clippy::assertions_on_result_states)] + +use serde::de::{ + Deserialize, Deserializer, EnumAccess, IgnoredAny, MapAccess, VariantAccess, Visitor, +}; +use serde_json::json; +use std::fmt; + +#[derive(Debug)] +pub enum Enum { + Variant { + #[allow(dead_code)] + x: u8, + }, +} + +impl<'de> Deserialize<'de> for Enum { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EnumVisitor; + + impl<'de> Visitor<'de> for EnumVisitor { + type Value = Enum; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("enum Enum") + } + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + let (IgnoredAny, variant) = data.variant()?; + variant.struct_variant(&["x"], self) + } + + fn visit_map(self, mut data: A) -> Result + where + A: MapAccess<'de>, + { + let mut x = 0; + if let Some((IgnoredAny, value)) = data.next_entry()? { + x = value; + } + Ok(Enum::Variant { x }) + } + } + + deserializer.deserialize_enum("Enum", &["Variant"], EnumVisitor) + } +} + +#[test] +fn test() { + let s = r#" {"Variant":{"x":0,"y":0}} "#; + assert!(serde_json::from_str::(s).is_err()); + + let j = json!({"Variant":{"x":0,"y":0}}); + assert!(serde_json::from_value::(j).is_err()); +} diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs new file mode 100644 index 000000000..7b6564dad --- /dev/null +++ b/tests/regression/issue845.rs @@ -0,0 +1,74 @@ +#![allow(clippy::trait_duplication_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/8757 + +use serde::{Deserialize, Deserializer}; +use std::fmt::{self, Display}; +use std::marker::PhantomData; +use std::str::FromStr; + +pub struct NumberVisitor { + marker: PhantomData, +} + +impl<'de, T> serde::de::Visitor<'de> for NumberVisitor +where + T: TryFrom + TryFrom + FromStr, + >::Error: Display, + >::Error: Display, + ::Err: Display, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer or string") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + T::try_from(v).map_err(serde::de::Error::custom) + } + + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + T::try_from(v).map_err(serde::de::Error::custom) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(serde::de::Error::custom) + } +} + +fn deserialize_integer_or_string<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: TryFrom + TryFrom + FromStr, + >::Error: Display, + >::Error: Display, + ::Err: Display, +{ + deserializer.deserialize_any(NumberVisitor { + marker: PhantomData, + }) +} + +#[derive(Deserialize, Debug)] +pub struct Struct { + #[serde(deserialize_with = "deserialize_integer_or_string")] + #[allow(dead_code)] + pub i: i64, +} + +#[test] +fn test() { + let j = r#" {"i":100} "#; + println!("{:?}", serde_json::from_str::(j).unwrap()); + + let j = r#" {"i":"100"} "#; + println!("{:?}", serde_json::from_str::(j).unwrap()); +} diff --git a/tests/regression/issue953.rs b/tests/regression/issue953.rs new file mode 100644 index 000000000..771aa5287 --- /dev/null +++ b/tests/regression/issue953.rs @@ -0,0 +1,9 @@ +use serde_json::Value; + +#[test] +fn test() { + let x1 = serde_json::from_str::("18446744073709551615."); + assert!(x1.is_err()); + let x2 = serde_json::from_str::("18446744073709551616."); + assert!(x2.is_err()); +} diff --git a/tests/stream.rs b/tests/stream.rs index ca54e9a15..fa52cedeb 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -1,4 +1,4 @@ -#![cfg(not(feature = "preserve_order"))] +#![allow(clippy::assertions_on_result_states)] use serde_json::{json, Deserializer, Value}; diff --git a/tests/test.rs b/tests/test.rs index ac6436dbd..d41a2336a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,40 +1,56 @@ -#![cfg(not(feature = "preserve_order"))] #![allow( + clippy::assertions_on_result_states, + clippy::byte_char_slices, + clippy::cast_precision_loss, + clippy::derive_partial_eq_without_eq, clippy::excessive_precision, clippy::float_cmp, - clippy::unreadable_literal + clippy::incompatible_msrv, // https://github.com/rust-lang/rust-clippy/issues/12257 + clippy::items_after_statements, + clippy::large_digit_groups, + clippy::let_underscore_untyped, + clippy::shadow_unrelated, + clippy::too_many_lines, + clippy::unreadable_literal, + clippy::unseparated_literal_suffix, + clippy::vec_init_then_push, + clippy::zero_sized_map_values )] -#![cfg_attr(feature = "trace-macros", feature(trace_macros))] -#[cfg(feature = "trace-macros")] -trace_macros!(true); #[macro_use] mod macros; +#[cfg(feature = "raw_value")] +use ref_cast::RefCast; use serde::de::{self, IgnoredAny, IntoDeserializer}; -use serde::ser::{self, Serializer}; +use serde::ser::{self, SerializeMap, SerializeSeq, Serializer}; use serde::{Deserialize, Serialize}; use serde_bytes::{ByteBuf, Bytes}; +#[cfg(feature = "raw_value")] +use serde_json::value::RawValue; use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, - to_vec, to_writer, Deserializer, Number, Value, + to_vec, Deserializer, Number, Value, }; use std::collections::BTreeMap; +#[cfg(feature = "raw_value")] +use std::collections::HashMap; use std::fmt::{self, Debug}; +use std::hash::BuildHasher; +#[cfg(feature = "raw_value")] +use std::hash::{Hash, Hasher}; use std::io; use std::iter; use std::marker::PhantomData; +use std::mem; use std::str::FromStr; -use std::string::ToString; use std::{f32, f64}; -use std::{i16, i32, i64, i8}; -use std::{u16, u32, u64, u8}; macro_rules! treemap { () => { BTreeMap::new() }; - ($($k:expr => $v:expr),+) => { + ($($k:expr => $v:expr),+ $(,)?) => { { let mut m = BTreeMap::new(); $( @@ -76,7 +92,7 @@ where let s = to_string(value).unwrap(); assert_eq!(s, out); - let v = to_value(&value).unwrap(); + let v = to_value(value).unwrap(); let s = to_string(&v).unwrap(); assert_eq!(s, out); } @@ -92,7 +108,7 @@ where let s = to_string_pretty(value).unwrap(); assert_eq!(s, out); - let v = to_value(&value).unwrap(); + let v = to_value(value).unwrap(); let s = to_string_pretty(&v).unwrap(); assert_eq!(s, out); } @@ -141,16 +157,28 @@ fn test_write_f64() { #[test] fn test_encode_nonfinite_float_yields_null() { - let v = to_value(::std::f64::NAN).unwrap(); + let v = to_value(f64::NAN.copysign(1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f64::INFINITY).unwrap(); + let v = to_value(f64::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::NAN).unwrap(); + let v = to_value(f64::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::INFINITY).unwrap(); + let v = to_value(-f64::INFINITY).unwrap(); + assert!(v.is_null()); + + let v = to_value(f32::NAN.copysign(1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(f32::NAN.copysign(-1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(f32::INFINITY).unwrap(); + assert!(v.is_null()); + + let v = to_value(-f32::INFINITY).unwrap(); assert!(v.is_null()); } @@ -241,11 +269,11 @@ fn test_write_list() { fn test_write_object() { test_encode_ok(&[ (treemap!(), "{}"), - (treemap!("a".to_string() => true), "{\"a\":true}"), + (treemap!("a".to_owned() => true), "{\"a\":true}"), ( treemap!( - "a".to_string() => true, - "b".to_string() => false + "a".to_owned() => true, + "b".to_owned() => false, ), "{\"a\":true,\"b\":false}", ), @@ -254,45 +282,45 @@ fn test_write_object() { test_encode_ok(&[ ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], "{\"a\":{},\"b\":{},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], "{\"a\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"b\":{},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "c".to_string() => treemap![] + "c".to_owned() => treemap![], ], "{\"a\":{},\"b\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], + ], ], "{\"a\":{},\"b\":{},\"c\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}}}", ), @@ -303,9 +331,9 @@ fn test_write_object() { test_pretty_encode_ok(&[ ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], pretty_str!({ "a": {}, @@ -315,13 +343,13 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], pretty_str!({ "a": { @@ -341,13 +369,13 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "c".to_string() => treemap![] + "c".to_owned() => treemap![], ], pretty_str!({ "a": {}, @@ -367,13 +395,13 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], + ], ], pretty_str!({ "a": {}, @@ -396,15 +424,15 @@ fn test_write_object() { test_pretty_encode_ok(&[ (treemap!(), "{}"), ( - treemap!("a".to_string() => true), + treemap!("a".to_owned() => true), pretty_str!({ "a": true }), ), ( treemap!( - "a".to_string() => true, - "b".to_string() => false + "a".to_owned() => true, + "b".to_owned() => false, ), pretty_str!( { "a": true, @@ -465,26 +493,26 @@ fn test_write_enum() { test_encode_ok(&[ (Animal::Dog, "\"Dog\""), ( - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), "{\"Frog\":[\"Henry\",[]]}", ), ( - Animal::Frog("Henry".to_string(), vec![349]), + Animal::Frog("Henry".to_owned(), vec![349]), "{\"Frog\":[\"Henry\",[349]]}", ), ( - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), "{\"Frog\":[\"Henry\",[349,102]]}", ), ( Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, "{\"Cat\":{\"age\":5,\"name\":\"Kate\"}}", ), ( - Animal::AntHive(vec!["Bob".to_string(), "Stuart".to_string()]), + Animal::AntHive(vec!["Bob".to_owned(), "Stuart".to_owned()]), "{\"AntHive\":[\"Bob\",\"Stuart\"]}", ), ]); @@ -492,7 +520,7 @@ fn test_write_enum() { test_pretty_encode_ok(&[ (Animal::Dog, "\"Dog\""), ( - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), pretty_str!({ "Frog": [ "Henry", @@ -501,7 +529,7 @@ fn test_write_enum() { }), ), ( - Animal::Frog("Henry".to_string(), vec![349]), + Animal::Frog("Henry".to_owned(), vec![349]), pretty_str!({ "Frog": [ "Henry", @@ -512,7 +540,7 @@ fn test_write_enum() { }), ), ( - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), pretty_str!({ "Frog": [ "Henry", @@ -702,11 +730,7 @@ fn test_parse_char() { ), ( "10", - if cfg!(feature = "arbitrary_precision") { - "invalid type: number, expected a character at line 1 column 2" - } else { - "invalid type: integer `10`, expected a character at line 1 column 2" - }, + "invalid type: integer `10`, expected a character at line 1 column 2", ), ]); @@ -805,16 +829,22 @@ fn test_parse_u64() { #[test] fn test_parse_negative_zero() { for negative_zero in &[ + "-0", "-0.0", "-0e2", "-0.0e2", "-1e-400", "-1e-4000000000000000000000000000000000000000000000000", ] { + assert!( + from_str::(negative_zero).unwrap().is_sign_negative(), + "should have been negative: {:?}", + negative_zero, + ); assert!( from_str::(negative_zero).unwrap().is_sign_negative(), "should have been negative: {:?}", - negative_zero + negative_zero, ); } } @@ -1007,8 +1037,14 @@ fn test_parse_number() { #[cfg(feature = "arbitrary_precision")] test_parse_ok(vec![ ("1e999", Number::from_string_unchecked("1e999".to_owned())), + ("1e+999", Number::from_string_unchecked("1e+999".to_owned())), ("-1e999", Number::from_string_unchecked("-1e999".to_owned())), ("1e-999", Number::from_string_unchecked("1e-999".to_owned())), + ("1E999", Number::from_string_unchecked("1E999".to_owned())), + ("1E+999", Number::from_string_unchecked("1E+999".to_owned())), + ("-1E999", Number::from_string_unchecked("-1E999".to_owned())), + ("1E-999", Number::from_string_unchecked("1E-999".to_owned())), + ("1E+000", Number::from_string_unchecked("1E+000".to_owned())), ( "2.3e999", Number::from_string_unchecked("2.3e999".to_owned()), @@ -1055,15 +1091,15 @@ fn test_parse_string() { ), ( &[b'"', b'\\', b'u', 250, 48, 51, 48, b'"'], - "invalid escape at line 1 column 4", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 250, 51, 48, b'"'], - "invalid escape at line 1 column 5", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 48, 250, 48, b'"'], - "invalid escape at line 1 column 6", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 48, 51, 250, b'"'], @@ -1080,17 +1116,17 @@ fn test_parse_string() { ]); test_parse_ok(vec![ - ("\"\"", "".to_string()), - ("\"foo\"", "foo".to_string()), - (" \"foo\" ", "foo".to_string()), - ("\"\\\"\"", "\"".to_string()), - ("\"\\b\"", "\x08".to_string()), - ("\"\\n\"", "\n".to_string()), - ("\"\\r\"", "\r".to_string()), - ("\"\\t\"", "\t".to_string()), - ("\"\\u12ab\"", "\u{12ab}".to_string()), - ("\"\\uAB12\"", "\u{AB12}".to_string()), - ("\"\\uD83C\\uDF95\"", "\u{1F395}".to_string()), + ("\"\"", String::new()), + ("\"foo\"", "foo".to_owned()), + (" \"foo\" ", "foo".to_owned()), + ("\"\\\"\"", "\"".to_owned()), + ("\"\\b\"", "\x08".to_owned()), + ("\"\\n\"", "\n".to_owned()), + ("\"\\r\"", "\r".to_owned()), + ("\"\\t\"", "\t".to_owned()), + ("\"\\u12ab\"", "\u{12ab}".to_owned()), + ("\"\\uAB12\"", "\u{AB12}".to_owned()), + ("\"\\uD83C\\uDF95\"", "\u{1F395}".to_owned()), ]); } @@ -1148,25 +1184,25 @@ fn test_parse_object() { test_parse_ok(vec![ ("{}", treemap!()), ("{ }", treemap!()), - ("{\"a\":3}", treemap!("a".to_string() => 3u64)), - ("{ \"a\" : 3 }", treemap!("a".to_string() => 3)), + ("{\"a\":3}", treemap!("a".to_owned() => 3u64)), + ("{ \"a\" : 3 }", treemap!("a".to_owned() => 3)), ( "{\"a\":3,\"b\":4}", - treemap!("a".to_string() => 3, "b".to_string() => 4), + treemap!("a".to_owned() => 3, "b".to_owned() => 4), ), ( " { \"a\" : 3 , \"b\" : 4 } ", - treemap!("a".to_string() => 3, "b".to_string() => 4), + treemap!("a".to_owned() => 3, "b".to_owned() => 4), ), ]); test_parse_ok(vec![( "{\"a\": {\"b\": 3, \"c\": 4}}", treemap!( - "a".to_string() => treemap!( - "b".to_string() => 3u64, - "c".to_string() => 4 - ) + "a".to_owned() => treemap!( + "b".to_owned() => 3u64, + "c".to_owned() => 4, + ), ), )]); @@ -1178,11 +1214,7 @@ fn test_parse_struct() { test_parse_err::(&[ ( "5", - if cfg!(feature = "arbitrary_precision") { - "invalid type: number, expected struct Outer at line 1 column 1" - } else { - "invalid type: integer `5`, expected struct Outer at line 1 column 1" - }, + "invalid type: integer `5`, expected struct Outer at line 1 column 1", ), ( "\"hello\"", @@ -1216,7 +1248,7 @@ fn test_parse_struct() { inner: vec![Inner { a: (), b: 2, - c: vec!["abc".to_string(), "xyz".to_string()], + c: vec!["abc".to_owned(), "xyz".to_owned()], }], }, ), @@ -1237,7 +1269,7 @@ fn test_parse_struct() { inner: vec![Inner { a: (), b: 2, - c: vec!["abc".to_string(), "xyz".to_string()], + c: vec!["abc".to_owned(), "xyz".to_owned()], }], } ); @@ -1251,7 +1283,7 @@ fn test_parse_struct() { fn test_parse_option() { test_parse_ok(vec![ ("null", None::), - ("\"jodhpurs\"", Some("jodhpurs".to_string())), + ("\"jodhpurs\"", Some("jodhpurs".to_owned())), ]); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -1306,29 +1338,29 @@ fn test_parse_enum() { (" \"Dog\" ", Animal::Dog), ( "{\"Frog\":[\"Henry\",[]]}", - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), ), ( " { \"Frog\": [ \"Henry\" , [ 349, 102 ] ] } ", - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), ), ( "{\"Cat\": {\"age\": 5, \"name\": \"Kate\"}}", Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, ), ( " { \"Cat\" : { \"age\" : 5 , \"name\" : \"Kate\" } } ", Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, ), ( " { \"AntHive\" : [\"Bob\", \"Stuart\"] } ", - Animal::AntHive(vec!["Bob".to_string(), "Stuart".to_string()]), + Animal::AntHive(vec!["Bob".to_owned(), "Stuart".to_owned()]), ), ]); @@ -1345,8 +1377,8 @@ fn test_parse_enum() { "}" ), treemap!( - "a".to_string() => Animal::Dog, - "b".to_string() => Animal::Frog("Henry".to_string(), vec![]) + "a".to_owned() => Animal::Dog, + "b".to_owned() => Animal::Frog("Henry".to_owned(), vec![]), ), )]); } @@ -1429,12 +1461,10 @@ fn test_serialize_seq_with_no_len() { where T: ser::Serialize, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { - use serde::ser::SerializeSeq; let mut seq = serializer.serialize_seq(None)?; for elem in &self.0 { seq.serialize_element(elem)?; @@ -1457,7 +1487,6 @@ fn test_serialize_seq_with_no_len() { formatter.write_str("array") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1465,7 +1494,6 @@ fn test_serialize_seq_with_no_len() { Ok(MyVec(Vec::new())) } - #[inline] fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: de::SeqAccess<'de>, @@ -1516,12 +1544,10 @@ fn test_serialize_map_with_no_len() { K: ser::Serialize + Ord, V: ser::Serialize, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, { - use serde::ser::SerializeMap; let mut map = serializer.serialize_map(None)?; for (k, v) in &self.0 { map.serialize_entry(k, v)?; @@ -1545,7 +1571,6 @@ fn test_serialize_map_with_no_len() { formatter.write_str("map") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1553,7 +1578,6 @@ fn test_serialize_map_with_no_len() { Ok(MyMap(BTreeMap::new())) } - #[inline] fn visit_map(self, mut visitor: Visitor) -> Result, Visitor::Error> where Visitor: de::MapAccess<'de>, @@ -1598,10 +1622,11 @@ fn test_serialize_map_with_no_len() { assert_eq!(s, expected); } +#[cfg(not(miri))] #[test] fn test_deserialize_from_stream() { - use serde::Deserialize; - use std::net; + use serde_json::to_writer; + use std::net::{TcpListener, TcpStream}; use std::thread; #[derive(Debug, PartialEq, Serialize, Deserialize)] @@ -1609,7 +1634,7 @@ fn test_deserialize_from_stream() { message: String, } - let l = net::TcpListener::bind("localhost:20000").unwrap(); + let l = TcpListener::bind("localhost:20000").unwrap(); thread::spawn(|| { let l = l; @@ -1626,9 +1651,9 @@ fn test_deserialize_from_stream() { } }); - let mut stream = net::TcpStream::connect("localhost:20000").unwrap(); + let mut stream = TcpStream::connect("localhost:20000").unwrap(); let request = Message { - message: "hi there".to_string(), + message: "hi there".to_owned(), }; to_writer(&mut stream, &request).unwrap(); @@ -1638,23 +1663,12 @@ fn test_deserialize_from_stream() { assert_eq!(request, response); } -#[test] -fn test_serialize_rejects_bool_keys() { - let map = treemap!( - true => 2, - false => 4 - ); - - let err = to_vec(&map).unwrap_err(); - assert_eq!(err.to_string(), "key must be a string"); -} - #[test] fn test_serialize_rejects_adt_keys() { let map = treemap!( Some("a") => 2, Some("b") => 4, - None => 6 + None => 6, ); let err = to_vec(&map).unwrap_err(); @@ -1665,20 +1679,20 @@ fn test_serialize_rejects_adt_keys() { fn test_bytes_ser() { let buf = vec![]; let bytes = Bytes::new(&buf); - assert_eq!(to_string(&bytes).unwrap(), "[]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[]".to_owned()); let buf = vec![1, 2, 3]; let bytes = Bytes::new(&buf); - assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_owned()); } #[test] fn test_byte_buf_ser() { let bytes = ByteBuf::new(); - assert_eq!(to_string(&bytes).unwrap(), "[]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[]".to_owned()); let bytes = ByteBuf::from(vec![1, 2, 3]); - assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_owned()); } #[test] @@ -1692,6 +1706,90 @@ fn test_byte_buf_de() { assert_eq!(v, bytes); } +#[test] +fn test_byte_buf_de_invalid_surrogates() { + let bytes = ByteBuf::from(vec![237, 160, 188]); + let v: ByteBuf = from_str(r#""\ud83c""#).unwrap(); + assert_eq!(v, bytes); + + let bytes = ByteBuf::from(vec![237, 160, 188, 10]); + let v: ByteBuf = from_str(r#""\ud83c\n""#).unwrap(); + assert_eq!(v, bytes); + + let bytes = ByteBuf::from(vec![237, 160, 188, 32]); + let v: ByteBuf = from_str(r#""\ud83c ""#).unwrap(); + assert_eq!(v, bytes); + + let res = from_str::(r#""\ud83c\!""#); + assert!(res.is_err()); + + let res = from_str::(r#""\ud83c\u""#); + assert!(res.is_err()); + + // lone trailing surrogate + let bytes = ByteBuf::from(vec![237, 176, 129]); + let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by other leading surrogate + let bytes = ByteBuf::from(vec![237, 160, 188, 237, 160, 188]); + let v: ByteBuf = from_str(r#""\ud83c\ud83c""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by "a" (U+0061) in \u encoding + let bytes = ByteBuf::from(vec![237, 160, 188, 97]); + let v: ByteBuf = from_str(r#""\ud83c\u0061""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+0080 + let bytes = ByteBuf::from(vec![237, 160, 188, 194, 128]); + let v: ByteBuf = from_str(r#""\ud83c\u0080""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+FFFF + let bytes = ByteBuf::from(vec![237, 160, 188, 239, 191, 191]); + let v: ByteBuf = from_str(r#""\ud83c\uffff""#).unwrap(); + assert_eq!(v, bytes); +} + +#[test] +fn test_byte_buf_de_surrogate_pair() { + // leading surrogate followed by trailing surrogate + let bytes = ByteBuf::from(vec![240, 159, 128, 128]); + let v: ByteBuf = from_str(r#""\ud83c\udc00""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by a surrogate pair + let bytes = ByteBuf::from(vec![237, 160, 188, 240, 159, 128, 128]); + let v: ByteBuf = from_str(r#""\ud83c\ud83c\udc00""#).unwrap(); + assert_eq!(v, bytes); +} + +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_de_invalid_surrogates() { + use serde_json::value::RawValue; + + assert!(from_str::>(r#""\ud83c""#).is_ok()); + assert!(from_str::>(r#""\ud83c\n""#).is_ok()); + assert!(from_str::>(r#""\ud83c ""#).is_ok()); + assert!(from_str::>(r#""\udc01 ""#).is_ok()); + assert!(from_str::>(r#""\udc01\!""#).is_err()); + assert!(from_str::>(r#""\udc01\u""#).is_err()); + assert!(from_str::>(r#""\ud83c\ud83c""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0061""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0080""#).is_ok()); + assert!(from_str::>(r#""\ud83c\uffff""#).is_ok()); +} + +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_de_surrogate_pair() { + use serde_json::value::RawValue; + + assert!(from_str::>(r#""\ud83c\udc00""#).is_ok()); +} + #[test] fn test_byte_buf_de_multiple() { let s: Vec = from_str(r#"["ab\nc", "cd\ne"]"#).unwrap(); @@ -1739,8 +1837,6 @@ fn test_json_pointer() { #[test] fn test_json_pointer_mut() { - use std::mem; - // Test case taken from https://tools.ietf.org/html/rfc6901#page-5 let mut data: Value = from_str( r#"{ @@ -1806,7 +1902,7 @@ fn test_stack_overflow() { .collect(); let _: Value = from_str(&brackets).unwrap(); - let brackets: String = iter::repeat('[').take(129).collect(); + let brackets = "[".repeat(129); test_parse_err::(&[(&brackets, "recursion limit exceeded at line 1 column 128")]); } @@ -1828,23 +1924,41 @@ fn test_integer_key() { // map with integer keys let map = treemap!( 1 => 2, - -1 => 6 + -1 => 6, ); let j = r#"{"-1":6,"1":2}"#; test_encode_ok(&[(&map, j)]); test_parse_ok(vec![(j, map)]); - let j = r#"{"x":null}"#; - test_parse_err::>(&[( - j, - "invalid type: string \"x\", expected i32 at line 1 column 4", - )]); + test_parse_err::>(&[ + ( + r#"{"x":null}"#, + "invalid value: expected key to be a number in quotes at line 1 column 2", + ), + ( + r#"{" 123":null}"#, + "invalid value: expected key to be a number in quotes at line 1 column 2", + ), + (r#"{"123 ":null}"#, "expected `\"` at line 1 column 6"), + ]); + + let err = from_value::>(json!({" 123":null})).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid value: expected key to be a number in quotes", + ); + + let err = from_value::>(json!({"123 ":null})).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid value: expected key to be a number in quotes", + ); } #[test] fn test_integer128_key() { let map = treemap! { - 100000000000000000000000000000000000000u128 => () + 100000000000000000000000000000000000000u128 => (), }; let j = r#"{"100000000000000000000000000000000000000":null}"#; assert_eq!(to_string(&map).unwrap(), j); @@ -1852,21 +1966,104 @@ fn test_integer128_key() { } #[test] -fn test_deny_float_key() { - #[derive(Eq, PartialEq, Ord, PartialOrd)] +fn test_float_key() { + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] struct Float; impl Serialize for Float { fn serialize(&self, serializer: S) -> Result where S: Serializer, { - serializer.serialize_f32(1.0) + serializer.serialize_f32(1.23) + } + } + impl<'de> Deserialize<'de> for Float { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + f32::deserialize(deserializer).map(|_| Float) } } // map with float key - let map = treemap!(Float => "x"); - assert!(serde_json::to_value(&map).is_err()); + let map = treemap!(Float => "x".to_owned()); + let j = r#"{"1.23":"x"}"#; + + test_encode_ok(&[(&map, j)]); + test_parse_ok(vec![(j, map)]); + + let j = r#"{"x": null}"#; + test_parse_err::>(&[( + j, + "invalid value: expected key to be a number in quotes at line 1 column 2", + )]); +} + +#[test] +fn test_deny_non_finite_f32_key() { + // We store float bits so that we can derive Ord, and other traits. In a + // real context the code might involve a crate like ordered-float. + + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] + struct F32Bits(u32); + impl Serialize for F32Bits { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f32(f32::from_bits(self.0)) + } + } + + let map = treemap!(F32Bits(f32::INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F32Bits(f32::NEG_INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F32Bits(f32::NAN.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); +} + +#[test] +fn test_deny_non_finite_f64_key() { + // We store float bits so that we can derive Ord, and other traits. In a + // real context the code might involve a crate like ordered-float. + + #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone)] + struct F64Bits(u64); + impl Serialize for F64Bits { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_f64(f64::from_bits(self.0)) + } + } + + let map = treemap!(F64Bits(f64::INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F64Bits(f64::NEG_INFINITY.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); + + let map = treemap!(F64Bits(f64::NAN.to_bits()) => "x".to_owned()); + assert!(serde_json::to_string(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); +} + +#[test] +fn test_boolean_key() { + let map = treemap!(false => 0, true => 1); + let j = r#"{"false":0,"true":1}"#; + test_encode_ok(&[(&map, j)]); + test_parse_ok(vec![(j, map)]); } #[test] @@ -1892,7 +2089,7 @@ fn test_effectively_string_keys() { } let map = treemap! { Enum::One => 1, - Enum::Two => 2 + Enum::Two => 2, }; let expected = r#"{"One":1,"Two":2}"#; test_encode_ok(&[(&map, expected)]); @@ -1902,7 +2099,7 @@ fn test_effectively_string_keys() { struct Wrapper(String); let map = treemap! { Wrapper("zero".to_owned()) => 0, - Wrapper("one".to_owned()) => 1 + Wrapper("one".to_owned()) => 1, }; let expected = r#"{"one":1,"zero":0}"#; test_encode_ok(&[(&map, expected)]); @@ -1946,20 +2143,20 @@ fn issue_220() { assert_eq!(from_str::(r#"{"V": 0}"#).unwrap(), E::V(0)); } -macro_rules! number_partialeq_ok { - ($($n:expr)*) => { - $( - let value = to_value($n).unwrap(); - let s = $n.to_string(); - assert_eq!(value, $n); - assert_eq!($n, value); - assert_ne!(value, s); - )* - } -} - #[test] fn test_partialeq_number() { + macro_rules! number_partialeq_ok { + ($($n:expr)*) => { + $( + let value = to_value($n).unwrap(); + let s = $n.to_string(); + assert_eq!(value, $n); + assert_eq!($n, value); + assert_ne!(value, s); + )* + }; + } + number_partialeq_ok!(0 1 100 i8::MIN i8::MAX i16::MIN i16::MAX i32::MIN i32::MAX i64::MIN i64::MAX u8::MIN u8::MAX u16::MIN u16::MAX u32::MIN u32::MAX u64::MIN u64::MAX @@ -1970,13 +2167,6 @@ fn test_partialeq_number() { ); } -#[test] -#[cfg(integer128)] -#[cfg(feature = "arbitrary_precision")] -fn test_partialeq_integer128() { - number_partialeq_ok!(i128::MIN i128::MAX u128::MIN u128::MAX) -} - #[test] fn test_partialeq_string() { let v = to_value("42").unwrap(); @@ -2084,8 +2274,8 @@ fn null_invalid_type() { #[test] fn test_integer128() { - let signed = &[i128::min_value(), -1, 0, 1, i128::max_value()]; - let unsigned = &[0, 1, u128::max_value()]; + let signed = &[i128::MIN, -1, 0, 1, i128::MAX]; + let unsigned = &[0, 1, u128::MAX]; for integer128 in signed { let expected = integer128.to_string(); @@ -2119,18 +2309,37 @@ fn test_integer128() { ]); } +#[test] +fn test_integer128_to_value() { + let signed = &[i128::from(i64::MIN), i128::from(u64::MAX)]; + let unsigned = &[0, u128::from(u64::MAX)]; + + for integer128 in signed { + let expected = integer128.to_string(); + assert_eq!(to_value(integer128).unwrap().to_string(), expected); + } + + for integer128 in unsigned { + let expected = integer128.to_string(); + assert_eq!(to_value(integer128).unwrap().to_string(), expected); + } + + if !cfg!(feature = "arbitrary_precision") { + let err = to_value(u128::from(u64::MAX) + 1).unwrap_err(); + assert_eq!(err.to_string(), "number out of range"); + } +} + #[cfg(feature = "raw_value")] #[test] fn test_borrowed_raw_value() { - use serde_json::value::RawValue; - #[derive(Serialize, Deserialize)] struct Wrapper<'a> { a: i8, #[serde(borrow)] b: &'a RawValue, c: i8, - }; + } let wrapper_from_str: Wrapper = serde_json::from_str(r#"{"a": 1, "b": {"foo": 2}, "c": 3}"#).unwrap(); @@ -2145,9 +2354,9 @@ fn test_borrowed_raw_value() { let array_from_str: Vec<&RawValue> = serde_json::from_str(r#"["a", 42, {"foo": "bar"}, null]"#).unwrap(); assert_eq!(r#""a""#, array_from_str[0].get()); - assert_eq!(r#"42"#, array_from_str[1].get()); + assert_eq!("42", array_from_str[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_str[2].get()); - assert_eq!(r#"null"#, array_from_str[3].get()); + assert_eq!("null", array_from_str[3].get()); let array_to_string = serde_json::to_string(&array_from_str).unwrap(); assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); @@ -2155,15 +2364,53 @@ fn test_borrowed_raw_value() { #[cfg(feature = "raw_value")] #[test] -fn test_boxed_raw_value() { - use serde_json::value::RawValue; +fn test_raw_value_in_map_key() { + #[derive(RefCast)] + #[repr(transparent)] + struct RawMapKey(RawValue); + + #[allow(unknown_lints)] + #[allow(non_local_definitions)] // false positive: https://github.com/rust-lang/rust/issues/121621 + impl<'de> Deserialize<'de> for &'de RawMapKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let raw_value = <&RawValue>::deserialize(deserializer)?; + Ok(RawMapKey::ref_cast(raw_value)) + } + } + + impl PartialEq for RawMapKey { + fn eq(&self, other: &Self) -> bool { + self.0.get() == other.0.get() + } + } + + impl Eq for RawMapKey {} + + impl Hash for RawMapKey { + fn hash(&self, hasher: &mut H) { + self.0.get().hash(hasher); + } + } + + let map_from_str: HashMap<&RawMapKey, &RawValue> = + serde_json::from_str(r#" {"\\k":"\\v"} "#).unwrap(); + let (map_k, map_v) = map_from_str.into_iter().next().unwrap(); + assert_eq!("\"\\\\k\"", map_k.0.get()); + assert_eq!("\"\\\\v\"", map_v.get()); +} +#[cfg(feature = "raw_value")] +#[test] +fn test_boxed_raw_value() { #[derive(Serialize, Deserialize)] struct Wrapper { a: i8, b: Box, c: i8, - }; + } let wrapper_from_str: Wrapper = serde_json::from_str(r#"{"a": 1, "b": {"foo": 2}, "c": 3}"#).unwrap(); @@ -2186,25 +2433,52 @@ fn test_boxed_raw_value() { let array_from_str: Vec> = serde_json::from_str(r#"["a", 42, {"foo": "bar"}, null]"#).unwrap(); assert_eq!(r#""a""#, array_from_str[0].get()); - assert_eq!(r#"42"#, array_from_str[1].get()); + assert_eq!("42", array_from_str[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_str[2].get()); - assert_eq!(r#"null"#, array_from_str[3].get()); + assert_eq!("null", array_from_str[3].get()); let array_from_reader: Vec> = serde_json::from_reader(br#"["a", 42, {"foo": "bar"}, null]"#.as_ref()).unwrap(); assert_eq!(r#""a""#, array_from_reader[0].get()); - assert_eq!(r#"42"#, array_from_reader[1].get()); + assert_eq!("42", array_from_reader[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_reader[2].get()); - assert_eq!(r#"null"#, array_from_reader[3].get()); + assert_eq!("null", array_from_reader[3].get()); let array_to_string = serde_json::to_string(&array_from_str).unwrap(); assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); } +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_invalid_utf8() { + let j = &[b'"', b'\xCE', b'\xF8', b'"']; + let value_err = serde_json::from_slice::(j).unwrap_err(); + let raw_value_err = serde_json::from_slice::>(j).unwrap_err(); + + assert_eq!( + value_err.to_string(), + "invalid unicode code point at line 1 column 4", + ); + assert_eq!( + raw_value_err.to_string(), + "invalid unicode code point at line 1 column 4", + ); +} + +#[cfg(feature = "raw_value")] +#[test] +fn test_serialize_unsized_value_to_raw_value() { + assert_eq!( + serde_json::value::to_raw_value("foobar").unwrap().get(), + r#""foobar""#, + ); +} + #[test] fn test_borrow_in_map_key() { #[derive(Deserialize, Debug)] struct Outer { + #[allow(dead_code)] map: BTreeMap, } @@ -2241,6 +2515,46 @@ fn test_value_into_deserializer() { let mut map = BTreeMap::new(); map.insert("inner", json!({ "string": "Hello World" })); + let outer = Outer::deserialize(serde::de::value::MapDeserializer::new( + map.iter().map(|(k, v)| (*k, v)), + )) + .unwrap(); + assert_eq!(outer.inner.string, "Hello World"); + let outer = Outer::deserialize(map.into_deserializer()).unwrap(); assert_eq!(outer.inner.string, "Hello World"); } + +#[test] +fn hash_positive_and_negative_zero() { + let rand = std::hash::RandomState::new(); + + let k1 = serde_json::from_str::("0.0").unwrap(); + let k2 = serde_json::from_str::("-0.0").unwrap(); + if cfg!(feature = "arbitrary_precision") { + assert_ne!(k1, k2); + assert_ne!(rand.hash_one(k1), rand.hash_one(k2)); + } else { + assert_eq!(k1, k2); + assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); + } +} + +#[test] +fn test_control_character_search() { + // Different space circumstances + for n in 0..16 { + for m in 0..16 { + test_parse_err::(&[( + &format!("\"{}\n{}\"", " ".repeat(n), " ".repeat(m)), + "control character (\\u0000-\\u001F) found while parsing a string at line 2 column 0", + )]); + } + } + + // Multiple occurrences + test_parse_err::(&[( + "\"\t\n\r\"", + "control character (\\u0000-\\u001F) found while parsing a string at line 1 column 2", + )]); +} diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 0402397d1..d5f6466e4 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -1,7 +1,12 @@ error: unexpected end of macro invocation - --> $DIR/missing_colon.rs:4:5 + --> tests/ui/missing_colon.rs:4:5 | 4 | json!({ "a" }); - | ^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +note: while trying to match `@` + --> src/macros.rs + | + | (@array [$($elems:expr,)*]) => { + | ^ + = note: this error originates in the macro `$crate::json_internal` which comes from the expansion of the macro `json` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/missing_comma.stderr b/tests/ui/missing_comma.stderr index ab25b740e..b0f0e4bd4 100644 --- a/tests/ui/missing_comma.stderr +++ b/tests/ui/missing_comma.stderr @@ -1,7 +1,13 @@ -error: no rules expected the token `"2"` - --> $DIR/missing_comma.rs:4:21 +error: no rules expected `"2"` + --> tests/ui/missing_comma.rs:4:21 | 4 | json!({ "1": "" "2": "" }); | -^^^ no rules expected this token in macro call | | | help: missing comma here + | +note: while trying to match `,` + --> src/macros.rs + | + | ($e:expr , $($tt:tt)*) => {}; + | ^ diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 0cf37fc6b..69f6ca361 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -1,7 +1,12 @@ error: unexpected end of macro invocation - --> $DIR/missing_value.rs:4:5 + --> tests/ui/missing_value.rs:4:5 | 4 | json!({ "a" : }); - | ^^^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) +note: while trying to match `@` + --> src/macros.rs + | + | (@array [$($elems:expr,)*]) => { + | ^ + = note: this error originates in the macro `$crate::json_internal` which comes from the expansion of the macro `json` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/not_found.stderr b/tests/ui/not_found.stderr index b38f1a3ed..6fec18040 100644 --- a/tests/ui/not_found.stderr +++ b/tests/ui/not_found.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `x` in this scope - --> $DIR/not_found.rs:4:19 + --> tests/ui/not_found.rs:4:19 | 4 | json!({ "a" : x }); | ^ not found in this scope diff --git a/tests/ui/parse_expr.stderr b/tests/ui/parse_expr.stderr index d3240df35..70cd74162 100644 --- a/tests/ui/parse_expr.stderr +++ b/tests/ui/parse_expr.stderr @@ -1,5 +1,11 @@ -error: no rules expected the token `~` - --> $DIR/parse_expr.rs:4:19 +error: no rules expected `~` + --> tests/ui/parse_expr.rs:4:19 | 4 | json!({ "a" : ~ }); | ^ no rules expected this token in macro call + | +note: while trying to match meta-variable `$e:expr` + --> src/macros.rs + | + | ($e:expr , $($tt:tt)*) => {}; + | ^^^^^^^ diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr index ea09836c9..15662dc50 100644 --- a/tests/ui/parse_key.stderr +++ b/tests/ui/parse_key.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `s` on type `&'static str` - --> $DIR/parse_key.rs:4:16 + --> tests/ui/parse_key.rs:4:16 | 4 | json!({ "".s : true }); - | ^ + | ^ unknown field diff --git a/tests/ui/unexpected_after_array_element.stderr b/tests/ui/unexpected_after_array_element.stderr index 3708992c9..b848e4dbd 100644 --- a/tests/ui/unexpected_after_array_element.stderr +++ b/tests/ui/unexpected_after_array_element.stderr @@ -1,5 +1,7 @@ -error: no rules expected the token `=>` - --> $DIR/unexpected_after_array_element.rs:4:18 +error: no rules expected `=>` + --> tests/ui/unexpected_after_array_element.rs:4:18 | 4 | json!([ true => ]); | ^^ no rules expected this token in macro call + | + = note: while trying to match end of macro diff --git a/tests/ui/unexpected_after_map_entry.stderr b/tests/ui/unexpected_after_map_entry.stderr index 60f9815bd..9f77c0729 100644 --- a/tests/ui/unexpected_after_map_entry.stderr +++ b/tests/ui/unexpected_after_map_entry.stderr @@ -1,5 +1,7 @@ -error: no rules expected the token `=>` - --> $DIR/unexpected_after_map_entry.rs:4:23 +error: no rules expected `=>` + --> tests/ui/unexpected_after_map_entry.rs:4:23 | 4 | json!({ "k": true => }); | ^^ no rules expected this token in macro call + | + = note: while trying to match end of macro diff --git a/tests/ui/unexpected_colon.stderr b/tests/ui/unexpected_colon.stderr index 2708b0883..d47e88161 100644 --- a/tests/ui/unexpected_colon.stderr +++ b/tests/ui/unexpected_colon.stderr @@ -1,5 +1,7 @@ -error: no rules expected the token `:` - --> $DIR/unexpected_colon.rs:4:13 +error: no rules expected `:` + --> tests/ui/unexpected_colon.rs:4:13 | 4 | json!({ : true }); | ^ no rules expected this token in macro call + | + = note: while trying to match end of macro diff --git a/tests/ui/unexpected_comma.stderr b/tests/ui/unexpected_comma.stderr index 65e045304..e30827453 100644 --- a/tests/ui/unexpected_comma.stderr +++ b/tests/ui/unexpected_comma.stderr @@ -1,5 +1,7 @@ -error: no rules expected the token `,` - --> $DIR/unexpected_comma.rs:4:17 +error: no rules expected `,` + --> tests/ui/unexpected_comma.rs:4:17 | 4 | json!({ "a" , "b": true }); | ^ no rules expected this token in macro call + | + = note: while trying to match end of macro