From 4f194c91416e522a0d14fe208ae5e3e6b88090c2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Oct 2022 21:27:06 -0700 Subject: [PATCH 001/142] Resolve needless_borrow pedantic clippy lint in test error: the borrowed expression implements the required traits --> tests/test.rs:1931:34 | 1931 | assert!(serde_json::to_value(&map).is_err()); | ^^^^ help: change this to: `map` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D clippy::all` --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index aa5b5caa0..0c0f00e8b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1928,7 +1928,7 @@ fn test_deny_float_key() { // map with float key let map = treemap!(Float => "x"); - assert!(serde_json::to_value(&map).is_err()); + assert!(serde_json::to_value(map).is_err()); } #[test] From ca41bdd563689da29c58ac53a952628476bec59e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Nov 2022 19:06:56 -0800 Subject: [PATCH 002/142] Update ui test suite to nightly-2022-11-16 --- tests/ui/missing_colon.stderr | 5 +++++ tests/ui/missing_comma.stderr | 6 ++++++ tests/ui/missing_value.stderr | 5 +++++ tests/ui/parse_expr.stderr | 6 ++++++ tests/ui/unexpected_after_array_element.stderr | 2 ++ tests/ui/unexpected_after_map_entry.stderr | 2 ++ tests/ui/unexpected_colon.stderr | 2 ++ tests/ui/unexpected_comma.stderr | 2 ++ 8 files changed, 30 insertions(+) diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 3cebc4fd3..1515211ad 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -4,4 +4,9 @@ error: unexpected end of macro invocation 4 | json!({ "a" }); | ^^^^^^^^^^^^^^ missing tokens in macro arguments | +note: while trying to match `@` + --> src/macros.rs + | + | (@array [$($elems:expr,)*]) => { + | ^ = note: this error originates in the macro `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 bd911d035..bafa0f891 100644 --- a/tests/ui/missing_comma.stderr +++ b/tests/ui/missing_comma.stderr @@ -5,3 +5,9 @@ error: no rules expected the token `"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 a1edbc37b..9c9de99ca 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -4,4 +4,9 @@ error: unexpected end of macro invocation 4 | json!({ "a" : }); | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | +note: while trying to match `@` + --> src/macros.rs + | + | (@array [$($elems:expr,)*]) => { + | ^ = note: this error originates in the macro `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/parse_expr.stderr b/tests/ui/parse_expr.stderr index 6959673d9..cd3e1c94d 100644 --- a/tests/ui/parse_expr.stderr +++ b/tests/ui/parse_expr.stderr @@ -3,3 +3,9 @@ error: no rules expected the token `~` | 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/unexpected_after_array_element.stderr b/tests/ui/unexpected_after_array_element.stderr index f745a212d..ef449f764 100644 --- a/tests/ui/unexpected_after_array_element.stderr +++ b/tests/ui/unexpected_after_array_element.stderr @@ -3,3 +3,5 @@ error: no rules expected the token `=>` | 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 a18c9b4cd..c62d90ba0 100644 --- a/tests/ui/unexpected_after_map_entry.stderr +++ b/tests/ui/unexpected_after_map_entry.stderr @@ -3,3 +3,5 @@ error: no rules expected the token `=>` | 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 ed038f608..7e47726bc 100644 --- a/tests/ui/unexpected_colon.stderr +++ b/tests/ui/unexpected_colon.stderr @@ -3,3 +3,5 @@ error: no rules expected the token `:` | 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 a4309c4e5..552f399a5 100644 --- a/tests/ui/unexpected_comma.stderr +++ b/tests/ui/unexpected_comma.stderr @@ -3,3 +3,5 @@ error: no rules expected the token `,` | 4 | json!({ "a" , "b": true }); | ^ no rules expected this token in macro call + | + = note: while trying to match end of macro From 3347248d82007c9f6b9f21e6eb657a37b5dea488 Mon Sep 17 00:00:00 2001 From: Nathan West Date: Thu, 17 Nov 2022 10:56:11 -0500 Subject: [PATCH 003/142] Add `clone_from` to `Map`; `Map::append` now uses `IndexMap::extend` --- src/map.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/map.rs b/src/map.rs index 87cf54566..c17920bd9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -197,9 +197,8 @@ impl Map { #[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); } @@ -304,6 +303,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 { From 9eb66da7e0a63ebe0d1bba9d6eab92869cf89fb7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 17 Nov 2022 23:25:29 -0800 Subject: [PATCH 004/142] Release 1.0.88 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d528f3e8e..a8d2a35ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.87" # remember to update html_root_url +version = "1.0.88" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 3ced8d43f..84ce5ce26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [macro]: https://docs.serde.rs/serde_json/macro.json.html //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.87")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.88")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 586fefb5a1e619b0f1a82d94b52ce25754b73ea0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 17 Nov 2022 23:33:29 -0800 Subject: [PATCH 005/142] Resolve semicolon_if_nothing_returned pedantic clippy lint error: consider adding a `;` to the last statement for consistent formatting --> src/map.rs:309:9 | 309 | self.map.clone_from(&source.map) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.map.clone_from(&source.map);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D clippy::pedantic` --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index c17920bd9..3e8a3814c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -306,7 +306,7 @@ impl Clone for Map { #[inline] fn clone_from(&mut self, source: &Self) { - self.map.clone_from(&source.map) + self.map.clone_from(&source.map); } } From c27b02334b57a96b479b74156b9a149274d677be Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 21 Nov 2022 19:15:11 -0800 Subject: [PATCH 006/142] Add regression test for issue 953 --- tests/regression/issue953.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/regression/issue953.rs 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()); +} From 9d94e920ef735a84d02df1852f48b06140037146 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 21 Nov 2022 22:27:31 -0800 Subject: [PATCH 007/142] Require at least one digit after decimal point --- src/de.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index 378b71062..88d0f2624 100644 --- a/src/de.rs +++ b/src/de.rs @@ -451,30 +451,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()) { + 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), From d2f936855d4c5f402ab516ce1598e2c79b1c65fe Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 21 Nov 2022 22:41:17 -0800 Subject: [PATCH 008/142] Release 1.0.89 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a8d2a35ab..17627920c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.88" # remember to update html_root_url +version = "1.0.89" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 84ce5ce26..6059aa709 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [macro]: https://docs.serde.rs/serde_json/macro.json.html //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.88")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.89")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 9295c96c8e2f3056beb23ba02a8d47f1704e5b02 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 22 Nov 2022 18:36:47 -0800 Subject: [PATCH 009/142] Resolve needless_borrowed_reference clippy lints error: dereferencing a tuple pattern where every element takes a reference --> src/value/de.rs:997:18 | 997 | Some(&Value::Array(ref v)) => { | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference = note: `-D clippy::needless-borrowed-reference` implied by `-D clippy::all` help: try removing the `&` and `ref` parts | 997 - Some(&Value::Array(ref v)) => { 997 + Some(Value::Array(v)) => { | error: dereferencing a tuple pattern where every element takes a reference --> src/value/de.rs:1024:18 | 1024 | Some(&Value::Object(ref v)) => visit_object_ref(v, visitor), | ^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference help: try removing the `&` and `ref` parts | 1024 - Some(&Value::Object(ref v)) => visit_object_ref(v, visitor), 1024 + Some(Value::Object(v)) => visit_object_ref(v, visitor), | --- src/value/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index 09d69c980..9c266d08a 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -994,7 +994,7 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Array(ref v)) => { + Some(Value::Array(v)) => { if v.is_empty() { visitor.visit_unit() } else { @@ -1021,7 +1021,7 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Object(ref v)) => visit_object_ref(v, visitor), + Some(Value::Object(v)) => visit_object_ref(v, visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", From ecad462c8e966b7f9c059b8aa3362bcf676b776c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 22 Nov 2022 18:47:52 -0800 Subject: [PATCH 010/142] Fix renamed let_underscore_drop lint error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop` --> tests/test.rs:9:5 | 9 | clippy::let_underscore_drop, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop` | = note: `-D renamed-and-removed-lints` implied by `-D warnings` --- tests/test.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 0c0f00e8b..c2050724b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -6,7 +6,6 @@ clippy::excessive_precision, clippy::float_cmp, clippy::items_after_statements, - clippy::let_underscore_drop, clippy::shadow_unrelated, clippy::too_many_lines, clippy::unreadable_literal, From 0b548714d8760dd9581b9bb87fd89aaa48256d86 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 25 Nov 2022 18:56:19 -0800 Subject: [PATCH 011/142] Time out workflows after 45 minutes GitHub's default timeout is 6 hours. Recently some of my GitHub Actions jobs have started randomly stalling for that long, which is inconvenient because it ties up a chunk of my runner quota. It apepars to be very rare for a job to recover after stalling. It's better to time out quicker and retry on a different runner. --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 55baf110d..411aa42f1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,6 +19,7 @@ jobs: fail-fast: false matrix: os: [ubuntu, windows] + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly @@ -44,6 +45,7 @@ jobs: target: aarch64-unknown-none - rust: stable os: windows + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master @@ -71,6 +73,7 @@ jobs: runs-on: ubuntu-latest env: MIRIFLAGS: -Zmiri-strict-provenance + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@miri @@ -81,6 +84,7 @@ jobs: name: Clippy runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy @@ -90,6 +94,7 @@ jobs: docs: name: Documentation runs-on: ubuntu-latest + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly @@ -100,6 +105,7 @@ jobs: fuzz: name: Fuzz runs-on: ubuntu-latest + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly @@ -110,6 +116,7 @@ jobs: name: Outdated runs-on: ubuntu-latest if: github.event_name != 'pull_request' + timeout-minutes: 45 steps: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated From 8794844eee024c816aa803d1c4776d1031247820 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 12 Dec 2022 14:37:41 -0800 Subject: [PATCH 012/142] Prevent build.rs rerunning unnecessarily on all source changes --- build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.rs b/build.rs index e9ec7d56a..0e12602e4 100644 --- a/build.rs +++ b/build.rs @@ -3,6 +3,8 @@ use std::process::Command; use std::str::{self, FromStr}; fn main() { + println!("cargo:rerun-if-changed=build.rs"); + // 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(); From 0a43394ef69559a961f4cda47f45a3952a4d23f5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Dec 2022 17:52:44 -0800 Subject: [PATCH 013/142] Update build status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2efdfd14d..fc460393c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.36+]][rustc] -[Build Status]: https://img.shields.io/github/workflow/status/serde-rs/json/CI/master +[Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/json/ci.yml?branch=master [travis]: 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 From 87538296f7f2fb9ed8c55e7949508bafffd4583b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Dec 2022 17:58:56 -0800 Subject: [PATCH 014/142] Replace ancient CI service provider in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fc460393c..50b5d458d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.36+]][rustc] +# Serde JSON   [![Build Status]][actions] [![Latest Version]][crates.io] [![Rustc Version 1.36+]][rustc] [Build Status]: https://img.shields.io/github/actions/workflow/status/serde-rs/json/ci.yml?branch=master -[travis]: https://github.com/serde-rs/json/actions?query=branch%3Amaster +[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.36+]: https://img.shields.io/badge/rustc-1.36+-lightgray.svg From 331511d73dfe72aa67709d3cf40c4b856832bbaa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 17 Dec 2022 11:51:51 -0800 Subject: [PATCH 015/142] Release 1.0.90 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 17627920c..ca49b7b9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.89" # remember to update html_root_url +version = "1.0.90" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 6059aa709..4a02f227f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [macro]: https://docs.serde.rs/serde_json/macro.json.html //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.89")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.90")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From d9cdb98f2e376c89fe1c4c1e290b81e83298df5c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 18 Dec 2022 09:04:10 -0800 Subject: [PATCH 016/142] Opt out -Zrustdoc-scrape-examples on docs.rs I'd like a chance to audit all the code that rustdoc is inserting into the docs. Currently I am skeptical that showing serde_json's internal usages of APIs is a net benefit to the public documentation. I am also skeptical that quite so many examples are needed, and that they should be featured so prominently in comparison to handwritten docs. Lastly I wish there were a way to turn this behavior off on a more granular basis. --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index ca49b7b9a..efd7c4303 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,9 @@ trybuild = { version = "1.0.49", features = ["diff"] } [workspace] members = ["tests/crate"] +[lib] +doc-scrape-examples = false + [package.metadata.docs.rs] features = ["raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] From 26f147fde7ce6441f7db978131150605c5500c28 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 18 Dec 2022 09:04:55 -0800 Subject: [PATCH 017/142] Release 1.0.91 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index efd7c4303..2feb6dd30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.90" # remember to update html_root_url +version = "1.0.91" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 4a02f227f..fdd95a121 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [macro]: https://docs.serde.rs/serde_json/macro.json.html //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.90")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.91")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 9947ae606e7807a7d8a38c2409810c1134e10ab8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 18 Dec 2022 11:53:30 -0800 Subject: [PATCH 018/142] Point documentation links to docs.rs instead of docs.serde.rs --- Cargo.toml | 2 +- README.md | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2feb6dd30..d5167a8a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.0.91" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" -documentation = "https://docs.serde.rs/serde_json/" +documentation = "https://docs.rs/serde_json" edition = "2018" keywords = ["json", "serde", "serialization"] license = "MIT OR Apache-2.0" diff --git a/README.md b/README.md index 50b5d458d..d70497924 100644 --- a/README.md +++ b/README.md @@ -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) @@ -126,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 @@ -363,14 +363,14 @@ For JSON support in Serde without a memory allocator, please see the [`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
From 9edf7fa9b3204ac6f6f5689303c00ec05fc40c8d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 18 Dec 2022 11:55:02 -0800 Subject: [PATCH 019/142] Replace docs.serde.rs links with intra-rustdoc links --- src/lib.rs | 18 +++++++++--------- src/value/mod.rs | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fdd95a121..d2d56f363 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,7 +104,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 @@ -290,14 +290,14 @@ //! 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 +//! [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.91")] diff --git a/src/value/mod.rs b/src/value/mod.rs index c467df6cc..470b6b24d 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -85,10 +85,10 @@ //! # 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; From 74f510e56d67020fe17a469a0ad9b02bb5a59aa3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 30 Dec 2022 12:00:52 -0800 Subject: [PATCH 020/142] Sync license text with rust-lang repos --- LICENSE-APACHE | 25 ------------------------- 1 file changed, 25 deletions(-) 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. From 8cebe895007bd1305228f21d5fe6d200e136c4f3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 19 Jan 2023 17:28:31 -0800 Subject: [PATCH 021/142] Speed up cargo fuzz CI job https://github.com/rust-fuzz/cargo-fuzz/pull/317 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 411aa42f1..bfaffaf6c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,7 +110,7 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - - run: cargo fuzz build -O + - run: cargo fuzz check outdated: name: Outdated From e41ee42d92022dbffc00f4ed50580fa5e060a379 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 29 Jan 2023 14:35:26 -0800 Subject: [PATCH 022/142] Update indoc dev-dependency to version 2 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d5167a8a0..c9582c14a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ ryu = "1.0" [dev-dependencies] automod = "1.0" -indoc = "1.0" +indoc = "2.0" ref-cast = "1.0" rustversion = "1.0" serde = { version = "1.0.100", features = ["derive"] } From 7bc6c863101474c651d1cbd5a4927f1edfef5ed0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Feb 2023 20:54:28 -0800 Subject: [PATCH 023/142] RawValue -> repr(transparent) This code is from September 2018, at which point serde_json supported rustc back to 1.15. repr(transparent) stabilized in 1.28. Today the minimum required compiler is 1.36, so repr(transparent) is okay to use. --- src/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw.rs b/src/raw.rs index c8377ac82..a136a5214 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -112,7 +112,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// raw_value: Box, /// } /// ``` -#[repr(C)] +#[repr(transparent)] #[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub struct RawValue { json: str, From eaa287cb3a873ca0513b11c8b9da6fbc956548d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Feb 2023 20:57:40 -0800 Subject: [PATCH 024/142] Hide repr attribute from documentation --- src/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw.rs b/src/raw.rs index a136a5214..6aa4ffcb6 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -112,7 +112,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// raw_value: Box, /// } /// ``` -#[repr(transparent)] +#[cfg_attr(not(doc), repr(transparent))] #[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub struct RawValue { json: str, From a9c984f13e6086c093c2f7a1014148c1922bac27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 4 Feb 2023 21:05:33 -0800 Subject: [PATCH 025/142] Release 1.0.92 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c9582c14a..891401b61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.91" # remember to update html_root_url +version = "1.0.92" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index d2d56f363..d716a5969 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.91")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.92")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From f77ad4750f6594a3d717cf34424089756c745c60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Feb 2023 12:06:48 -0800 Subject: [PATCH 026/142] Add test of integer128 to_value --- tests/test.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index c2050724b..f62a545e7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2180,6 +2180,27 @@ fn test_integer128() { ]); } +#[test] +fn test_integer128_to_value() { + let signed = &[i128::from(i64::min_value()), i128::from(u64::max_value())]; + let unsigned = &[0, u128::from(u64::max_value())]; + + 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_value()) + 1).unwrap_err(); + assert_eq!(err.to_string(), "number out of range"); + } +} + #[cfg(feature = "raw_value")] #[test] fn test_borrowed_raw_value() { From e3d13cd61aeacd28bacc4b86af45dc454e57549a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Feb 2023 12:15:07 -0800 Subject: [PATCH 027/142] Support 128-bit integers in to_value --- src/value/ser.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index 892a63d5f..a29814e92 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -5,6 +5,8 @@ use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::vec::Vec; +#[cfg(not(feature = "arbitrary_precision"))] +use core::convert::TryFrom; use core::fmt::Display; use core::result; use serde::ser::{Impossible, Serialize}; @@ -92,9 +94,22 @@ impl serde::Serializer for Serializer { Ok(Value::Number(value.into())) } - #[cfg(feature = "arbitrary_precision")] fn serialize_i128(self, value: i128) -> Result { - Ok(Value::Number(value.into())) + #[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] @@ -117,9 +132,20 @@ impl serde::Serializer for Serializer { Ok(Value::Number(value.into())) } - #[cfg(feature = "arbitrary_precision")] fn serialize_u128(self, value: u128) -> Result { - Ok(Value::Number(value.into())) + #[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] From 0ebeede28a0d5f0f07f18124078f55d62b180a2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 8 Feb 2023 12:31:03 -0800 Subject: [PATCH 028/142] Release 1.0.93 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 891401b61..3e8fa0d97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.92" # remember to update html_root_url +version = "1.0.93" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index d716a5969..48d0fe219 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.92")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.93")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From d9447c30eb0ff682923499dfb18fb229d5dea84d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 26 Feb 2023 23:03:08 -0700 Subject: [PATCH 029/142] Ignore let_underscore_untyped pedantic clippy lint error: non-binding `let` without a type annotation --> src/de.rs:1712:9 | 1712 | let _ = name; | ^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped = note: `-D clippy::let-underscore-untyped` implied by `-D clippy::pedantic` error: non-binding `let` without a type annotation --> src/de.rs:2190:9 | 2190 | let _ = name; | ^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> src/map.rs:51:17 | 51 | let _ = capacity; | ^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> src/value/de.rs:312:9 | 312 | let _ = name; | ^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> src/value/de.rs:803:9 | 803 | let _ = name; | ^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:26:5 | 26 | let _ = x; | ^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped = note: `-D clippy::let-underscore-untyped` implied by `-D clippy::pedantic` error: non-binding `let` without a type annotation --> tests/lexical/num.rs:31:5 | 31 | let _ = y + x; | ^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:48:5 | 48 | let _ = x & T::ZERO; | ^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:61:5 | 61 | let _ = x.pow10(5); | ^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:62:5 | 62 | let _ = x.to_bits(); | ^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:66:5 | 66 | let _ = x.to_bits() & T::SIGN_MASK; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:67:5 | 67 | let _ = x.to_bits() & T::EXPONENT_MASK; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:68:5 | 68 | let _ = x.to_bits() & T::HIDDEN_BIT_MASK; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/lexical/num.rs:69:5 | 69 | let _ = x.to_bits() & T::MANTISSA_MASK; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/test.rs:1977:5 | 1977 | / let _ = json!([ 1978 | | as Clone>::clone(&Ok(())), 1979 | | as Clone>::clone(&Err(())) 1980 | | ]); | |_______^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped = note: `-D clippy::let-underscore-untyped` implied by `-D clippy::pedantic` error: non-binding `let` without a type annotation --> tests/test.rs:1983:5 | 1983 | / let _ = json!({ 1984 | | "ok": as Clone>::clone(&Ok(())), 1985 | | "err": as Clone>::clone(&Err(())) 1986 | | }); | |_______^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/test.rs:1989:5 | 1989 | / let _ = json!({ 1990 | | ( as Clone>::clone(&Ok("")).unwrap()): "ok", 1991 | | ( as Clone>::clone(&Err("")).unwrap_err()): "err" 1992 | | }); | |_______^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped error: non-binding `let` without a type annotation --> tests/test.rs:1995:5 | 1995 | let _ = json!({ "architecture": [true, null] }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a type annotation or removing the `let` keyword = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_untyped --- src/lib.rs | 1 + tests/lexical.rs | 1 + tests/test.rs | 1 + 3 files changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 48d0fe219..8909f0db4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -338,6 +338,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, diff --git a/tests/lexical.rs b/tests/lexical.rs index 6e0f07b8c..d3dfb852b 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -9,6 +9,7 @@ clippy::excessive_precision, clippy::float_cmp, clippy::if_not_else, + clippy::let_underscore_untyped, clippy::module_name_repetitions, clippy::needless_late_init, clippy::shadow_unrelated, diff --git a/tests/test.rs b/tests/test.rs index f62a545e7..6c08cc8d2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -6,6 +6,7 @@ clippy::excessive_precision, clippy::float_cmp, clippy::items_after_statements, + clippy::let_underscore_untyped, clippy::shadow_unrelated, clippy::too_many_lines, clippy::unreadable_literal, From 7eeb169f9b51e2a30997d6c92aa3e170a2927b7f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 5 Mar 2023 10:16:06 -0800 Subject: [PATCH 030/142] Fix message duplication between error Display and source() --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 1875ef08b..0898baf90 100644 --- a/src/error.rs +++ b/src/error.rs @@ -319,7 +319,7 @@ impl serde::de::StdError for Error { #[cfg(feature = "std")] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self.err.code { - ErrorCode::Io(err) => Some(err), + ErrorCode::Io(err) => err.source(), _ => None, } } From a15bd0968639884ec7b73107360d58fd655e2071 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 5 Mar 2023 10:27:27 -0800 Subject: [PATCH 031/142] Release 1.0.94 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3e8fa0d97..ee5428450 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.93" # remember to update html_root_url +version = "1.0.94" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 8909f0db4..bd4976398 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.93")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.94")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 4b9699612f57512b6734b43238c2382186245b31 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 17 Mar 2023 19:19:54 -0700 Subject: [PATCH 032/142] No longer test so many old compiler versions --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfaffaf6c..b58492dba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, 1.56.1] os: [ubuntu] include: - rust: stable From 02e583360db1a4aa7d21db911a9b62f34161c386 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Mar 2023 21:35:20 -0700 Subject: [PATCH 033/142] Update fuzz crate gitignore to ignore coverage dir --- fuzz/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/fuzz/.gitignore b/fuzz/.gitignore index 188f19609..f83457aec 100644 --- a/fuzz/.gitignore +++ b/fuzz/.gitignore @@ -1,3 +1,4 @@ artifacts/ corpus/ +coverage/ target/ From b0990a51db0f32558cd95ace2e64ef4e23e2cae4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 27 Mar 2023 09:37:10 -0700 Subject: [PATCH 034/142] Add regression test for issue 1004 --- tests/regression/issue1004.rs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/regression/issue1004.rs diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs new file mode 100644 index 000000000..d2c3e74c0 --- /dev/null +++ b/tests/regression/issue1004.rs @@ -0,0 +1,9 @@ +#![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(); + assert_eq!(json, "5.550000190734863"); // FIXME +} From 06f3443c6e6713526d744100768a78bb085aadd8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 27 Mar 2023 09:14:05 -0700 Subject: [PATCH 035/142] Eliminate f32-to-f64 casting in arbitrary_precision mode --- src/number.rs | 18 ++++++++++++++++++ src/value/from.rs | 2 +- src/value/ser.rs | 9 ++++----- tests/regression/issue1004.rs | 5 ++++- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/number.rs b/src/number.rs index 21a76411c..ca2fd55fd 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,6 +279,24 @@ impl Number { } } + 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)] diff --git a/src/value/from.rs b/src/value/from.rs index c5a6a3960..462ad3f51 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -40,7 +40,7 @@ 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) } } diff --git a/src/value/ser.rs b/src/value/ser.rs index a29814e92..875d22e24 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,6 +1,5 @@ use crate::error::{Error, ErrorCode, Result}; use crate::map::Map; -use crate::number::Number; use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; @@ -149,13 +148,13 @@ impl serde::Serializer for Serializer { } #[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] diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs index d2c3e74c0..3f5bd96aa 100644 --- a/tests/regression/issue1004.rs +++ b/tests/regression/issue1004.rs @@ -5,5 +5,8 @@ fn test() { let float = 5.55f32; let value = serde_json::to_value(&float).unwrap(); let json = serde_json::to_string(&value).unwrap(); - assert_eq!(json, "5.550000190734863"); // FIXME + + // If the f32 were cast to f64 by Value before serialization, then this + // would incorrectly serialize as 5.550000190734863. + assert_eq!(json, "5.55"); } From c9bff92c1fc750bc6e71af15ecb9498cb75b1cf5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 27 Mar 2023 09:47:21 -0700 Subject: [PATCH 036/142] Fix PartialEq between Value and f32 Caught by test_partialeq_number: thread 'test_partialeq_number' panicked at 'assertion failed: `(left == right)` left: `-3.4028235e38`, right: `Number(-3.4028235e38)`', tests/test.rs:2033:5 --- src/number.rs | 11 +++++++++++ src/value/partial_eq.rs | 10 +++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index ca2fd55fd..5ecbde873 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,6 +279,17 @@ impl Number { } } + 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 = { diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index b4ef84c4f..6b2e350b6 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -9,6 +9,13 @@ fn eq_u64(value: &Value, other: u64) -> bool { value.as_u64().map_or(false, |i| i == other) } +fn eq_f32(value: &Value, other: f32) -> bool { + match value { + Value::Number(n) => n.as_f32().map_or(false, |i| i == other), + _ => false, + } +} + fn eq_f64(value: &Value, other: f64) -> bool { value.as_f64().map_or(false, |i| i == other) } @@ -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] } From 4ea38c40019d3c00c04a0b6f080c4c62df92ae27 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 27 Mar 2023 10:00:56 -0700 Subject: [PATCH 037/142] Release 1.0.95 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ee5428450..030282f3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.94" # remember to update html_root_url +version = "1.0.95" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index bd4976398..95242d40b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.94")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.95")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From ce53b862b9a09130cdd5363088135b4b5c853735 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 27 Mar 2023 10:11:43 -0700 Subject: [PATCH 038/142] Fix needless_borrow clippy lint in test error: the borrowed expression implements the required traits --> tests/regression/issue1004.rs:6:38 | 6 | let value = serde_json::to_value(&float).unwrap(); | ^^^^^^ help: change this to: `float` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-D clippy::needless-borrow` implied by `-D clippy::all` --- tests/regression/issue1004.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/issue1004.rs b/tests/regression/issue1004.rs index 3f5bd96aa..c09fb9610 100644 --- a/tests/regression/issue1004.rs +++ b/tests/regression/issue1004.rs @@ -3,7 +3,7 @@ #[test] fn test() { let float = 5.55f32; - let value = serde_json::to_value(&float).unwrap(); + 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 From cd5ed8204a22cf2b9fd2ea2b20956909b8ac1f4a Mon Sep 17 00:00:00 2001 From: Stiopa Koltsov Date: Wed, 12 Apr 2023 12:40:35 +0100 Subject: [PATCH 039/142] Document to_writer only writes valid UTF-8 strings --- src/ser.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ser.rs b/src/ser.rs index 80c2deb0c..b38f3486b 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2064,6 +2064,8 @@ static ESCAPE: [u8; 256] = [ /// Serialize the given data structure as JSON into the IO 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 @@ -2082,6 +2084,8 @@ where /// Serialize the given data structure as pretty-printed JSON into the IO /// 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 From 187f7dadc6ab3423128dde5e5598a83617fb18f2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 12 Apr 2023 16:38:44 -0700 Subject: [PATCH 040/142] Release 1.0.96 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 030282f3e..553949857 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.95" # remember to update html_root_url +version = "1.0.96" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 95242d40b..637d1ce2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.95")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.96")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 0d7b0d3c18166bb65643afda9651e962b6f03d55 Mon Sep 17 00:00:00 2001 From: Neil Mitchell Date: Thu, 18 May 2023 10:34:39 +0100 Subject: [PATCH 041/142] Add an example of producing a Null in a json! literal I tried to do this, and while you can piece together the way to do this from the interpolation comment, it's the one piece of direct JSON syntax that isn't immediately obvious. --- src/macros.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 5287998b4..70acde25e 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -2,6 +2,7 @@ /// /// ``` /// # use serde_json::json; +/// # use serde_json::Value; /// # /// let value = json!({ /// "code": 200, @@ -10,7 +11,8 @@ /// "features": [ /// "serde", /// "json" -/// ] +/// ], +/// "empty": Value::Null /// } /// }); /// ``` From 931ee23b1abf6b302df7c20a3a5866bf1a8a33ca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 23 May 2023 08:29:47 -0700 Subject: [PATCH 042/142] Show error details during miri setup in CI Without this, if it fails, the only information printed is useless: Preparing a sysroot for Miri (target: x86_64-unknown-linux-gnu)... fatal error: failed to build sysroot; run `cargo miri setup` to see the error details --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b58492dba..7603eab60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@miri + - run: cargo miri setup - run: cargo miri test - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value From 009a53b472017dbec18442611fb849b9801db41d Mon Sep 17 00:00:00 2001 From: Neil Mitchell Date: Thu, 1 Jun 2023 15:29:37 +0100 Subject: [PATCH 043/142] Switch to using null --- src/macros.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 70acde25e..0936f7231 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -2,7 +2,6 @@ /// /// ``` /// # use serde_json::json; -/// # use serde_json::Value; /// # /// let value = json!({ /// "code": 200, @@ -12,7 +11,7 @@ /// "serde", /// "json" /// ], -/// "empty": Value::Null +/// "empty": null /// } /// }); /// ``` From 207a57b68880769c81d525f9f5b38d3be1340806 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Jun 2023 21:06:09 -0700 Subject: [PATCH 044/142] Standardize on "I/O" instead of "IO" --- src/de.rs | 4 ++-- src/error.rs | 17 +++++++++-------- src/io/core.rs | 2 +- src/ser.rs | 4 ++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/de.rs b/src/de.rs index 88d0f2624..4b16ba2a8 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2408,9 +2408,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 diff --git a/src/error.rs b/src/error.rs index 0898baf90..52169a058 100644 --- a/src/error.rs +++ b/src/error.rs @@ -36,15 +36,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 @@ -76,7 +77,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 } @@ -109,7 +110,7 @@ impl Error { /// 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, @@ -134,8 +135,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; @@ -182,7 +183,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. diff --git a/src/io/core.rs b/src/io/core.rs index 465ab8b24..54c8ddfda 100644 --- a/src/io/core.rs +++ b/src/io/core.rs @@ -9,7 +9,7 @@ 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/ser.rs b/src/ser.rs index b38f3486b..34ca87c5f 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2062,7 +2062,7 @@ 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. /// @@ -2081,7 +2081,7 @@ where 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. From 9308d97b3d9e729438ccaae7506b054c157b708b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Jun 2023 21:11:32 -0700 Subject: [PATCH 045/142] Add Error::io_error_kind --- src/error.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 52169a058..7ba3a4edd 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,8 @@ 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. @@ -105,6 +107,55 @@ 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`. @@ -166,8 +217,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), } } } From a0ddb25ff6b86f43912f8fc637797bcbb920c61e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Jun 2023 21:48:10 -0700 Subject: [PATCH 046/142] Release 1.0.97 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 553949857..09fb908ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.96" # remember to update html_root_url +version = "1.0.97" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 637d1ce2f..af60a727b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.96")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.97")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 050311197c17859a3b16e786894e7337384a92d4 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sat, 17 Jun 2023 19:53:44 -0400 Subject: [PATCH 047/142] Allow f32 and f64 keys Now we allow objects with `f32` or `f64` string-serialized keys. For example: ```json { "1.123": "foo", "2.0": "bar" } ``` The `test_deny_float_key` test has been inverted, and the `deserialize_integer_key` macros have been renamed to `deserialize_numeric_key` in order to support `f32` and `f64`. --- src/de.rs | 28 +++++++++++++++------------- src/ser.rs | 34 ++++++++++++++++++++++++++++++---- src/value/de.rs | 26 ++++++++++++++------------ src/value/ser.rs | 8 ++++---- tests/test.rs | 27 ++++++++++++++++++++++----- 5 files changed, 85 insertions(+), 38 deletions(-) diff --git a/src/de.rs b/src/de.rs index 4b16ba2a8..556fe8292 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2118,7 +2118,7 @@ struct MapKey<'a, R: 'a> { de: &'a mut Deserializer, } -macro_rules! deserialize_integer_key { +macro_rules! deserialize_numeric_key { ($method:ident => $visit:ident) => { fn $method(self, visitor: V) -> Result where @@ -2155,16 +2155,18 @@ 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_i128 => visit_i128); - 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_integer_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_i8 => visit_i8); + deserialize_numeric_key!(deserialize_i16 => visit_i16); + deserialize_numeric_key!(deserialize_i32 => visit_i32); + deserialize_numeric_key!(deserialize_i64 => visit_i64); + deserialize_numeric_key!(deserialize_i128 => visit_i128); + deserialize_numeric_key!(deserialize_u8 => visit_u8); + deserialize_numeric_key!(deserialize_u16 => visit_u16); + deserialize_numeric_key!(deserialize_u32 => visit_u32); + deserialize_numeric_key!(deserialize_u64 => visit_u64); + deserialize_numeric_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_f32 => visit_f32); + deserialize_numeric_key!(deserialize_f64 => visit_f64); #[inline] fn deserialize_option(self, visitor: V) -> Result @@ -2221,8 +2223,8 @@ where } forward_to_deserialize_any! { - bool f32 f64 char str string unit unit_struct seq tuple tuple_struct map - struct identifier ignored_any + bool char str string unit unit_struct seq tuple tuple_struct map struct + identifier ignored_any } } diff --git a/src/ser.rs b/src/ser.rs index 34ca87c5f..1a8c58cc0 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1002,12 +1002,38 @@ where .map_err(Error::io) } - fn serialize_f32(self, _value: f32) -> Result<()> { - Err(key_must_be_a_string()) + fn serialize_f32(self, value: f32) -> Result<()> { + 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<()> { + 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<()> { diff --git a/src/value/de.rs b/src/value/de.rs index 9c266d08a..341d779b8 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1120,7 +1120,7 @@ struct MapKeyDeserializer<'de> { key: Cow<'de, str>, } -macro_rules! deserialize_integer_key { +macro_rules! deserialize_numeric_key { ($method:ident => $visit:ident) => { fn $method(self, visitor: V) -> Result where @@ -1146,16 +1146,18 @@ 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_i128 => visit_i128); - 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_integer_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_i8 => visit_i8); + deserialize_numeric_key!(deserialize_i16 => visit_i16); + deserialize_numeric_key!(deserialize_i32 => visit_i32); + deserialize_numeric_key!(deserialize_i64 => visit_i64); + deserialize_numeric_key!(deserialize_i128 => visit_i128); + deserialize_numeric_key!(deserialize_u8 => visit_u8); + deserialize_numeric_key!(deserialize_u16 => visit_u16); + deserialize_numeric_key!(deserialize_u32 => visit_u32); + deserialize_numeric_key!(deserialize_u64 => visit_u64); + deserialize_numeric_key!(deserialize_u128 => visit_u128); + deserialize_numeric_key!(deserialize_f32 => visit_f32); + deserialize_numeric_key!(deserialize_f64 => visit_f64); #[inline] fn deserialize_option(self, visitor: V) -> Result @@ -1193,7 +1195,7 @@ 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 + bool char str string bytes byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any } } diff --git a/src/value/ser.rs b/src/value/ser.rs index 875d22e24..920294f73 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -517,12 +517,12 @@ impl serde::Serializer for MapKeySerializer { Ok(value.to_string()) } - fn serialize_f32(self, _value: f32) -> Result { - Err(key_must_be_a_string()) + fn serialize_f32(self, value: f32) -> Result { + Ok(value.to_string()) } - fn serialize_f64(self, _value: f64) -> Result { - Err(key_must_be_a_string()) + fn serialize_f64(self, value: f64) -> Result { + Ok(value.to_string()) } #[inline] diff --git a/tests/test.rs b/tests/test.rs index 6c08cc8d2..48ddd1d17 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1914,21 +1914,38 @@ 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 type: string \"x\", expected f32 at line 1 column 4", + )]); } #[test] From b0fa9788f4f1fc03a9fc8066ea4c2aa0f26c5400 Mon Sep 17 00:00:00 2001 From: Lucien Greathouse Date: Wed, 21 Jun 2023 18:24:47 -0400 Subject: [PATCH 048/142] Change MapKeySerializer::serialize_some to fall through instead of erroring --- src/ser.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 34ca87c5f..3c08f3700 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1043,11 +1043,12 @@ where Err(key_must_be_a_string()) } - fn serialize_some(self, _value: &T) -> Result<()> + #[inline] + 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 { From 51459078f38c851bb750d84ab9be4d8f40dc7693 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 20:03:35 -0700 Subject: [PATCH 049/142] Delete unneeded conditional on preserve_order steps in CI Obsoleted by 4b9699612f57512b6734b43238c2382186245b31. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7603eab60..bc74a5111 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,9 +61,7 @@ jobs: - 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 --features preserve_order - if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order - if: matrix.rust != '1.53.0' && matrix.rust != '1.46.0' && matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' - name: Build without std run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc if: matrix.target From e09d78f793b1fd189bf5ed59251ea906feb24df3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 20:05:18 -0700 Subject: [PATCH 050/142] Update indexmap dependency used for preserve_order feature to version 2 --- .github/workflows/ci.yml | 4 +++- Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc74a5111..03cd3ae48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.56.1] + rust: [beta, 1.64.0, 1.56.1] os: [ubuntu] include: - rust: stable @@ -61,7 +61,9 @@ jobs: - 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 --features preserve_order + if: matrix.rust != '1.56.1' - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order + if: matrix.rust != '1.56.1' - name: Build without std run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc if: matrix.target diff --git a/Cargo.toml b/Cargo.toml index 09fb908ca..5f1ad071e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ rust-version = "1.36" [dependencies] serde = { version = "1.0.100", default-features = false } -indexmap = { version = "1.5.2", features = ["std"], optional = true } +indexmap = { version = "2", optional = true } itoa = "1.0" ryu = "1.0" From d4c98d05b993de5add76feec4eb371b1035d8f77 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 20:51:14 -0700 Subject: [PATCH 051/142] Move serde_json_test crate to own workspace Fixes the following error when testing against a compiler older than 1.64: error: failed to select a version for the requirement `hashbrown = "^0.14"` candidate versions found which didn't match: 0.13.2, 0.13.1, 0.12.3, ... location searched: crates.io index required by package `indexmap v2.0.0` ... which satisfies dependency `indexmap = "^2"` of package `serde_json v1.0.97` ... which satisfies path dependency `serde_json` of package `serde_json_test v0.0.0` --- .github/workflows/ci.yml | 2 +- Cargo.toml | 3 --- fuzz/Cargo.toml | 2 -- tests/crate/Cargo.toml | 2 +- 4 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03cd3ae48..6c3972a60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,5 +121,5 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --exit-code 1 - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 diff --git a/Cargo.toml b/Cargo.toml index 5f1ad071e..af0c3047e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,9 +28,6 @@ serde_derive = "1.0" serde_stacker = "0.1" trybuild = { version = "1.0.49", features = ["diff"] } -[workspace] -members = ["tests/crate"] - [lib] doc-scrape-examples = false diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 8e925bf94..5fe38b679 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -17,5 +17,3 @@ name = "from_slice" path = "fuzz_targets/from_slice.rs" test = false doc = false - -[workspace] diff --git a/tests/crate/Cargo.toml b/tests/crate/Cargo.toml index 6a3ac7034..739149fd2 100644 --- a/tests/crate/Cargo.toml +++ b/tests/crate/Cargo.toml @@ -15,7 +15,7 @@ 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"] arbitrary_precision = ["serde_json/arbitrary_precision"] raw_value = ["serde_json/raw_value"] unbounded_depth = ["serde_json/unbounded_depth"] From 706fc2b5592f415329d17df7b3777b56af909413 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 20:56:24 -0700 Subject: [PATCH 052/142] Do all CI builds with old rustc using shim crate Fixes the following error when testing against a compiler older than 1.64: error: failed to select a version for the requirement `hashbrown = "^0.14"` candidate versions found which didn't match: 0.13.2, 0.13.1, 0.12.3, ... location searched: crates.io index required by package `indexmap v2.0.0` ... which satisfies dependency `indexmap = "^2"` of package `serde_json v1.0.97` ... which satisfies path dependency `serde_json` of package `serde_json_test v0.0.0` --- .github/workflows/ci.yml | 14 +++++++------- tests/crate/Cargo.toml | 1 + 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c3972a60..92cacf5bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,17 +52,17 @@ jobs: with: toolchain: ${{matrix.rust}} targets: ${{matrix.target}} - - run: cargo check - - run: cargo check --features float_roundtrip - - run: cargo check --features arbitrary_precision - - run: cargo check --features raw_value - - run: cargo check --features unbounded_depth + - 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 --features preserve_order + - 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,preserve_order + - 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: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc diff --git a/tests/crate/Cargo.toml b/tests/crate/Cargo.toml index 739149fd2..03fde9c44 100644 --- a/tests/crate/Cargo.toml +++ b/tests/crate/Cargo.toml @@ -16,6 +16,7 @@ default = ["std"] std = ["serde_json/std"] alloc = ["serde_json/alloc"] #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"] From ba29a89a098dd5db6e889830bf1ac33837c1416e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 21:10:47 -0700 Subject: [PATCH 053/142] Release 1.0.98 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index af0c3047e..2efe47f61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.97" # remember to update html_root_url +version = "1.0.98" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index af60a727b..2bca0bc6e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.97")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.98")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From b4ec50ce7a47f1b344dd964de09a8be99fbe8300 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 21:35:50 -0700 Subject: [PATCH 054/142] Release 1.0.99 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2efe47f61..e0d97b981 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.98" # remember to update html_root_url +version = "1.0.99" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 2bca0bc6e..227a87bd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.98")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.99")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From fdb7800f5aa2870ffa87712dac417883daac3656 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Jun 2023 22:50:52 -0700 Subject: [PATCH 055/142] Support a manual trigger on CI workflow --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92cacf5bc..8365beb43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,7 @@ name: CI on: push: pull_request: + workflow_dispatch: schedule: [cron: "40 1 * * *"] permissions: From e98e664b42aceb245eaf801fc9ab726284693a68 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 2 Jul 2023 21:13:55 -0700 Subject: [PATCH 056/142] Resolve explicit_iter_loop pedantic clippy lint error: it is more concise to loop over references to containers instead of using explicit iteration methods --> tests/../src/lexical/math.rs:339:19 | 339 | for xi in x.iter_mut() { | ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *x` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop = note: `-D clippy::explicit-iter-loop` implied by `-D clippy::pedantic` error: it is more concise to loop over references to containers instead of using explicit iteration methods --> tests/../src/lexical/math.rs:485:19 | 485 | for xi in x.iter_mut() { | ^^^^^^^^^^^^ help: to write this more concisely, try: `&mut *x` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop --- src/lexical/math.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 37cc1d24a..d7122bffa 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -336,7 +336,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); } @@ -482,7 +482,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; From 79caf276312947b502df0b905c117f76557edc61 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jul 2023 12:12:11 -0700 Subject: [PATCH 057/142] Sort Cargo.toml dependencies list --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e0d97b981..69dec0c2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,10 +12,10 @@ repository = "https://github.com/serde-rs/json" rust-version = "1.36" [dependencies] -serde = { version = "1.0.100", default-features = false } indexmap = { version = "2", optional = true } itoa = "1.0" ryu = "1.0" +serde = { version = "1.0.100", default-features = false } [dev-dependencies] automod = "1.0" From f482ed3d3662446914221d8c0a1efe3d7ffe8f82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jul 2023 12:33:49 -0700 Subject: [PATCH 058/142] Add CI job using minimal-versions --- .github/workflows/ci.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8365beb43..6f98235a8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,6 +69,18 @@ jobs: run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc if: matrix.target + minimal: + name: Minimal versions + needs: pre_ci + if: needs.pre_ci.outputs.continue + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo generate-lockfile -Z minimal-versions + - run: cargo check --locked + miri: name: Miri runs-on: ubuntu-latest From 8f8a2b1c47ebe4193b3a0b06805f31619e4c1cf8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jul 2023 12:35:19 -0700 Subject: [PATCH 059/142] Eliminate syn 1 from minimal-versions --- Cargo.toml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 69dec0c2a..fad94b38d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,18 +15,18 @@ rust-version = "1.36" indexmap = { version = "2", optional = true } itoa = "1.0" ryu = "1.0" -serde = { version = "1.0.100", default-features = false } +serde = { version = "1.0.166", default-features = false } [dev-dependencies] -automod = "1.0" -indoc = "2.0" -ref-cast = "1.0" -rustversion = "1.0" -serde = { version = "1.0.100", features = ["derive"] } -serde_bytes = "0.11" -serde_derive = "1.0" -serde_stacker = "0.1" -trybuild = { version = "1.0.49", features = ["diff"] } +automod = "1.0.11" +indoc = "2.0.2" +ref-cast = "1.0.18" +rustversion = "1.0.13" +serde = { version = "1.0.166", features = ["derive"] } +serde_bytes = "0.11.10" +serde_derive = "1.0.166" +serde_stacker = "0.1.8" +trybuild = { version = "1.0.81", features = ["diff"] } [lib] doc-scrape-examples = false From 897f913dd234c8cd0810fc0689d76a146aaec4ad Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jul 2023 12:36:27 -0700 Subject: [PATCH 060/142] No pre_ci in this repo --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f98235a8..9c984c0f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,8 +71,6 @@ jobs: minimal: name: Minimal versions - needs: pre_ci - if: needs.pre_ci.outputs.continue runs-on: ubuntu-latest timeout-minutes: 45 steps: From d2fce19685bf0e3d493a9d785665137654c79e6d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 4 Jul 2023 12:42:07 -0700 Subject: [PATCH 061/142] Release 1.0.100 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fad94b38d..291e6f3fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.99" # remember to update html_root_url +version = "1.0.100" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 227a87bd8..c306f05db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.99")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.100")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 5def3367b6781b3006d8f7ba6067d54416c93081 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 6 Jul 2023 14:45:48 -0700 Subject: [PATCH 062/142] Update to 2021 edition --- Cargo.toml | 4 ++-- src/map.rs | 2 +- src/value/from.rs | 1 - src/value/ser.rs | 2 -- tests/crate/Cargo.toml | 2 +- tests/regression/issue845.rs | 1 - 6 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 291e6f3fc..ccb0c6fa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,11 +5,11 @@ authors = ["Erick Tryzelaar ", "David Tolnay { diff --git a/src/value/ser.rs b/src/value/ser.rs index 875d22e24..b68048b65 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -4,8 +4,6 @@ use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; use alloc::vec::Vec; -#[cfg(not(feature = "arbitrary_precision"))] -use core::convert::TryFrom; use core::fmt::Display; use core::result; use serde::ser::{Impossible, Serialize}; diff --git a/tests/crate/Cargo.toml b/tests/crate/Cargo.toml index 03fde9c44..e13df6a8e 100644 --- a/tests/crate/Cargo.toml +++ b/tests/crate/Cargo.toml @@ -2,7 +2,7 @@ name = "serde_json_test" version = "0.0.0" authors = ["David Tolnay "] -edition = "2018" +edition = "2021" publish = false [lib] diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs index 56037ae66..e8b0c0fd0 100644 --- a/tests/regression/issue845.rs +++ b/tests/regression/issue845.rs @@ -1,7 +1,6 @@ #![allow(clippy::trait_duplication_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/8757 use serde::{Deserialize, Deserializer}; -use std::convert::TryFrom; use std::fmt::{self, Display}; use std::marker::PhantomData; use std::str::FromStr; From 451436564b610afbf9f3b027ac19fd06c9010392 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 6 Jul 2023 14:50:42 -0700 Subject: [PATCH 063/142] Fix bare trait in to_value doc example code ---- src/value/mod.rs - value::to_value (line 889) stdout ---- error[E0782]: trait objects must include the `dyn` keyword --> src/value/mod.rs:902:44 | 16 | fn compare_json_values() -> Result<(), Box> { | ^^^^^ | help: add `dyn` keyword before this trait | 16 | fn compare_json_values() -> Result<(), Box> { | +++ --- src/value/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 470b6b24d..64556278c 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -889,7 +889,6 @@ mod ser; /// ``` /// use serde::Serialize; /// use serde_json::json; -/// /// use std::error::Error; /// /// #[derive(Serialize)] @@ -898,7 +897,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(), From f74b422d647399033867a9945bba612c763b4954 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 12:53:19 -0400 Subject: [PATCH 064/142] Return error on non-finite float keys This handles NaN and both positive and negative infinity. --- src/error.rs | 7 ++++++ src/ser.rs | 12 ++++++++++ src/value/ser.rs | 16 +++++++++++-- tests/test.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7ba3a4edd..e295453db 100644 --- a/src/error.rs +++ b/src/error.rs @@ -70,6 +70,7 @@ impl Error { | ErrorCode::InvalidUnicodeCodePoint | ErrorCode::ControlCharacterWhileParsingString | ErrorCode::KeyMustBeAString + | ErrorCode::FloatKeyMustBeFinite | ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::TrailingComma | ErrorCode::TrailingCharacters @@ -282,6 +283,9 @@ pub(crate) enum ErrorCode { /// Object key is not a string. KeyMustBeAString, + /// Object key is a non-finite float value. + FloatKeyMustBeFinite, + /// Lone leading surrogate in hex escape. LoneLeadingSurrogateInHexEscape, @@ -356,6 +360,9 @@ 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::FloatKeyMustBeFinite => { + f.write_str("float key must be finite (got NaN or +/-inf)") + } ErrorCode::LoneLeadingSurrogateInHexEscape => { f.write_str("lone leading surrogate in hex escape") } diff --git a/src/ser.rs b/src/ser.rs index 1a8c58cc0..d9158aad6 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -789,6 +789,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, @@ -1003,6 +1007,10 @@ where } fn serialize_f32(self, value: f32) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); + } + tri!(self .ser .formatter @@ -1020,6 +1028,10 @@ where } fn serialize_f64(self, value: f64) -> Result<()> { + if !value.is_finite() { + return Err(float_key_must_be_finite()); + } + tri!(self .ser .formatter diff --git a/src/value/ser.rs b/src/value/ser.rs index 920294f73..8b968865c 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -451,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; @@ -518,11 +522,19 @@ impl serde::Serializer for MapKeySerializer { } fn serialize_f32(self, value: f32) -> Result { - Ok(value.to_string()) + if value.is_finite() { + Ok(value.to_string()) + } else { + Err(float_key_must_be_finite()) + } } fn serialize_f64(self, value: f64) -> Result { - Ok(value.to_string()) + if value.is_finite() { + Ok(value.to_string()) + } else { + Err(float_key_must_be_finite()) + } } #[inline] diff --git a/tests/test.rs b/tests/test.rs index 48ddd1d17..cdbe64169 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1948,6 +1948,64 @@ fn test_float_key() { )]); } +#[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, we + // would use a crate like `ordered-float` instead. + + #[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, we + // would use a crate like `ordered-float` instead. + + #[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_borrowed_key() { let map: BTreeMap<&str, ()> = from_str("{\"borrowed\":null}").unwrap(); From 996340584935e5463b4f3ac8f2229f1632e65650 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:13:52 -0400 Subject: [PATCH 065/142] Print map float keys with `CompactFormatter` This ensures consistency with the default `Formatter::write_f32` output format used elsewhere. --- src/value/ser.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index 8b968865c..b4bfe4d9d 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,5 +1,6 @@ use crate::error::{Error, ErrorCode, Result}; use crate::map::Map; +use crate::ser::{CompactFormatter, Formatter}; use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; @@ -523,7 +524,15 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f32(self, value: f32) -> Result { if value.is_finite() { - Ok(value.to_string()) + // We initialize our output buffer with a heuristic capacity. + let mut buf = Vec::with_capacity(8); + let mut formatter = CompactFormatter; + + // `Vec`'s `Write` implementation never returns an error. + formatter.write_f32(&mut buf, value).unwrap(); + + // `CompactFormatter` does not emit invalid UTF-8. + Ok(String::from_utf8(buf).unwrap()) } else { Err(float_key_must_be_finite()) } @@ -531,7 +540,15 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f64(self, value: f64) -> Result { if value.is_finite() { - Ok(value.to_string()) + // We initialize our output buffer with a heuristic capacity. + let mut buf = Vec::with_capacity(8); + let mut formatter = CompactFormatter; + + // `Vec`'s `Write` implementation never returns an error. + formatter.write_f64(&mut buf, value).unwrap(); + + // `CompactFormatter` does not emit invalid UTF-8. + Ok(String::from_utf8(buf).unwrap()) } else { Err(float_key_must_be_finite()) } From 22a5e9b75a0ae4bee6c52296cd3129768993b4c1 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:17:36 -0400 Subject: [PATCH 066/142] Deserialize float keys using `deserialize_number` `deserialize_numeric_key!` has been renamed back to `deserialize_integer_key!`, and a new float-specific `deserialize_float_key!` macro has been introduced. This new macro attempts to deserialize float keys by delegating to the equivalent parent deserializer method. --- src/de.rs | 46 +++++++++++++++++++++++++++++++++------------- src/error.rs | 5 +++++ tests/test.rs | 2 +- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/de.rs b/src/de.rs index 556fe8292..daf032a7f 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2118,7 +2118,7 @@ struct MapKey<'a, R: 'a> { de: &'a mut Deserializer, } -macro_rules! deserialize_numeric_key { +macro_rules! deserialize_integer_key { ($method:ident => $visit:ident) => { fn $method(self, visitor: V) -> Result where @@ -2136,6 +2136,25 @@ macro_rules! deserialize_numeric_key { }; } +macro_rules! deserialize_float_key { + ($method:ident) => { + fn $method(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + self.de.eat_char(); + let value = self.de.$method(visitor)?; + + match self.de.peek()? { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), + } + + Ok(value) + } + }; +} + impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R> where R: Read<'de>, @@ -2155,18 +2174,19 @@ where } } - deserialize_numeric_key!(deserialize_i8 => visit_i8); - deserialize_numeric_key!(deserialize_i16 => visit_i16); - deserialize_numeric_key!(deserialize_i32 => visit_i32); - deserialize_numeric_key!(deserialize_i64 => visit_i64); - deserialize_numeric_key!(deserialize_i128 => visit_i128); - deserialize_numeric_key!(deserialize_u8 => visit_u8); - deserialize_numeric_key!(deserialize_u16 => visit_u16); - deserialize_numeric_key!(deserialize_u32 => visit_u32); - deserialize_numeric_key!(deserialize_u64 => visit_u64); - deserialize_numeric_key!(deserialize_u128 => visit_u128); - deserialize_numeric_key!(deserialize_f32 => visit_f32); - deserialize_numeric_key!(deserialize_f64 => visit_f64); + 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_i128 => visit_i128); + 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_integer_key!(deserialize_u128 => visit_u128); + + deserialize_float_key!(deserialize_f32); + deserialize_float_key!(deserialize_f64); #[inline] fn deserialize_option(self, visitor: V) -> Result diff --git a/src/error.rs b/src/error.rs index e295453db..c7d5d5a32 100644 --- a/src/error.rs +++ b/src/error.rs @@ -64,6 +64,7 @@ impl Error { | ErrorCode::ExpectedObjectCommaOrEnd | ErrorCode::ExpectedSomeIdent | ErrorCode::ExpectedSomeValue + | ErrorCode::ExpectedDoubleQuote | ErrorCode::InvalidEscape | ErrorCode::InvalidNumber | ErrorCode::NumberOutOfRange @@ -265,6 +266,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, @@ -352,6 +356,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"), diff --git a/tests/test.rs b/tests/test.rs index cdbe64169..a10ed9294 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1944,7 +1944,7 @@ fn test_float_key() { let j = r#"{"x": null}"#; test_parse_err::>(&[( j, - "invalid type: string \"x\", expected f32 at line 1 column 4", + "expected value at line 1 column 3", )]); } From 324c5b5298bd47ef45ce61fdac4141a87619c5bc Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:22:24 -0400 Subject: [PATCH 067/142] Fix formatting --- tests/test.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index a10ed9294..f630330b0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1942,10 +1942,7 @@ fn test_float_key() { test_parse_ok(vec![(j, map)]); let j = r#"{"x": null}"#; - test_parse_err::>(&[( - j, - "expected value at line 1 column 3", - )]); + test_parse_err::>(&[(j, "expected value at line 1 column 3")]); } #[test] From 18e1b5965454a975ebdf8b0f46555e640e59813e Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:33:55 -0400 Subject: [PATCH 068/142] Fix builds without `std` feature An `unwrap` has been changed to simply discard the `Result`. We assume that writing to a `Vec` will never fail because the `std::io::Write` implementation never creates an I/O error. --- src/value/ser.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index b4bfe4d9d..40ce15c28 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -529,7 +529,7 @@ impl serde::Serializer for MapKeySerializer { let mut formatter = CompactFormatter; // `Vec`'s `Write` implementation never returns an error. - formatter.write_f32(&mut buf, value).unwrap(); + let _ = formatter.write_f32(&mut buf, value); // `CompactFormatter` does not emit invalid UTF-8. Ok(String::from_utf8(buf).unwrap()) @@ -545,7 +545,7 @@ impl serde::Serializer for MapKeySerializer { let mut formatter = CompactFormatter; // `Vec`'s `Write` implementation never returns an error. - formatter.write_f64(&mut buf, value).unwrap(); + let _ = formatter.write_f64(&mut buf, value); // `CompactFormatter` does not emit invalid UTF-8. Ok(String::from_utf8(buf).unwrap()) From 5762701ff0d2b5945f06f5c9630d89eb9ae540c8 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:37:36 -0400 Subject: [PATCH 069/142] Use `ryu` directly when formatting float keys --- src/value/ser.rs | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index 40ce15c28..f9a9b793d 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,6 +1,5 @@ use crate::error::{Error, ErrorCode, Result}; use crate::map::Map; -use crate::ser::{CompactFormatter, Formatter}; use crate::value::{to_value, Value}; use alloc::borrow::ToOwned; use alloc::string::{String, ToString}; @@ -524,15 +523,8 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f32(self, value: f32) -> Result { if value.is_finite() { - // We initialize our output buffer with a heuristic capacity. - let mut buf = Vec::with_capacity(8); - let mut formatter = CompactFormatter; - - // `Vec`'s `Write` implementation never returns an error. - let _ = formatter.write_f32(&mut buf, value); - - // `CompactFormatter` does not emit invalid UTF-8. - Ok(String::from_utf8(buf).unwrap()) + let mut buffer = ryu::Buffer::new(); + Ok(buffer.format_finite(value).to_owned()) } else { Err(float_key_must_be_finite()) } @@ -540,15 +532,8 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f64(self, value: f64) -> Result { if value.is_finite() { - // We initialize our output buffer with a heuristic capacity. - let mut buf = Vec::with_capacity(8); - let mut formatter = CompactFormatter; - - // `Vec`'s `Write` implementation never returns an error. - let _ = formatter.write_f64(&mut buf, value); - - // `CompactFormatter` does not emit invalid UTF-8. - Ok(String::from_utf8(buf).unwrap()) + let mut buffer = ryu::Buffer::new(); + Ok(buffer.format_finite(value).to_owned()) } else { Err(float_key_must_be_finite()) } From 7760a1ae8cc9b2a6f4aec9f11f368dc380ad8334 Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 15:53:09 -0400 Subject: [PATCH 070/142] Remove extra variable --- src/value/ser.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index f9a9b793d..91260ab99 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -523,8 +523,7 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f32(self, value: f32) -> Result { if value.is_finite() { - let mut buffer = ryu::Buffer::new(); - Ok(buffer.format_finite(value).to_owned()) + Ok(ryu::Buffer::new().format_finite(value).to_owned()) } else { Err(float_key_must_be_finite()) } @@ -532,8 +531,7 @@ impl serde::Serializer for MapKeySerializer { fn serialize_f64(self, value: f64) -> Result { if value.is_finite() { - let mut buffer = ryu::Buffer::new(); - Ok(buffer.format_finite(value).to_owned()) + Ok(ryu::Buffer::new().format_finite(value).to_owned()) } else { Err(float_key_must_be_finite()) } From b0dbac784d6d05182745828979439a075353ad7f Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 16:04:47 -0400 Subject: [PATCH 071/142] Parse `Value::Map` numeric keys using `crate::from_str` --- src/value/de.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/value/de.rs b/src/value/de.rs index 341d779b8..15cb67c2d 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1126,7 +1126,8 @@ macro_rules! deserialize_numeric_key { where V: Visitor<'de>, { - match (self.key.parse(), self.key) { + let parsed = crate::from_str(&self.key); + match (parsed, self.key) { (Ok(integer), _) => visitor.$visit(integer), (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), #[cfg(any(feature = "std", feature = "alloc"))] From 468a94a08c8a3d54999bf7b880ad879b9068278b Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 16:11:45 -0400 Subject: [PATCH 072/142] Use Deserializer::* methods instead of `.parse()` for integer keys --- src/de.rs | 45 +++++++++++++-------------------------------- tests/test.rs | 5 +---- 2 files changed, 14 insertions(+), 36 deletions(-) diff --git a/src/de.rs b/src/de.rs index daf032a7f..5283dd3a2 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2118,25 +2118,7 @@ struct MapKey<'a, R: 'a> { de: &'a mut Deserializer, } -macro_rules! deserialize_integer_key { - ($method:ident => $visit: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), - } - } - }; -} - -macro_rules! deserialize_float_key { +macro_rules! deserialize_numeric_key { ($method:ident) => { fn $method(self, visitor: V) -> Result where @@ -2174,19 +2156,18 @@ 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_i128 => visit_i128); - 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_integer_key!(deserialize_u128 => visit_u128); - - deserialize_float_key!(deserialize_f32); - deserialize_float_key!(deserialize_f64); + 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_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_numeric_key!(deserialize_f32); + deserialize_numeric_key!(deserialize_f64); #[inline] fn deserialize_option(self, visitor: V) -> Result diff --git a/tests/test.rs b/tests/test.rs index f630330b0..c64d053d1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1897,10 +1897,7 @@ fn test_integer_key() { 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::>(&[(j, "expected value at line 1 column 3")]); } #[test] From a4e271901eb8b19ecc95d25d34c9cf8e0aa139ae Mon Sep 17 00:00:00 2001 From: Marko Mijalkovic Date: Sun, 9 Jul 2023 22:43:32 -0400 Subject: [PATCH 073/142] Return error if numeric key starts with whitespace --- src/de.rs | 7 ++++++- src/error.rs | 7 +++++++ tests/test.rs | 9 +++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index 5283dd3a2..9c79d0f38 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2125,7 +2125,12 @@ macro_rules! deserialize_numeric_key { V: de::Visitor<'de>, { self.de.eat_char(); - let value = self.de.$method(visitor)?; + + if let Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') = tri!(self.de.peek()) { + return Err(self.de.peek_error(ErrorCode::UnexpectedWhitespaceInKey)); + } + + let value = tri!(self.de.$method(visitor)); match self.de.peek()? { Some(b'"') => self.de.eat_char(), diff --git a/src/error.rs b/src/error.rs index c7d5d5a32..8a7b59901 100644 --- a/src/error.rs +++ b/src/error.rs @@ -72,6 +72,7 @@ impl Error { | ErrorCode::ControlCharacterWhileParsingString | ErrorCode::KeyMustBeAString | ErrorCode::FloatKeyMustBeFinite + | ErrorCode::UnexpectedWhitespaceInKey | ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::TrailingComma | ErrorCode::TrailingCharacters @@ -290,6 +291,9 @@ pub(crate) enum ErrorCode { /// Object key is a non-finite float value. FloatKeyMustBeFinite, + /// Unexpected whitespace in a numeric key. + UnexpectedWhitespaceInKey, + /// Lone leading surrogate in hex escape. LoneLeadingSurrogateInHexEscape, @@ -368,6 +372,9 @@ impl Display for ErrorCode { ErrorCode::FloatKeyMustBeFinite => { f.write_str("float key must be finite (got NaN or +/-inf)") } + ErrorCode::UnexpectedWhitespaceInKey => { + f.write_str("unexpected whitespace in object key") + } ErrorCode::LoneLeadingSurrogateInHexEscape => { f.write_str("lone leading surrogate in hex escape") } diff --git a/tests/test.rs b/tests/test.rs index c64d053d1..405ba421c 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1900,6 +1900,15 @@ fn test_integer_key() { test_parse_err::>(&[(j, "expected value at line 1 column 3")]); } +#[test] +fn test_integer_key_leading_whitespace() { + let j = r#"{" 123":null}"#; + test_parse_err::>(&[( + j, + "unexpected whitespace in object key at line 1 column 3", + )]); +} + #[test] fn test_integer128_key() { let map = treemap! { From 76555ac6f7e69c21b09b2fff5f5c1f4bd9fa4f84 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 21:39:46 -0700 Subject: [PATCH 074/142] Improve error message on invalid numeric key --- src/de.rs | 24 +++++++++++++----------- src/error.rs | 7 +++++++ tests/test.rs | 10 ++++++++-- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/de.rs b/src/de.rs index 9c79d0f38..45c855594 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2126,18 +2126,20 @@ macro_rules! deserialize_numeric_key { { self.de.eat_char(); - if let Some(b' ') | Some(b'\n') | Some(b'\r') | Some(b'\t') = tri!(self.de.peek()) { - return Err(self.de.peek_error(ErrorCode::UnexpectedWhitespaceInKey)); - } - - let value = tri!(self.de.$method(visitor)); - - match self.de.peek()? { - Some(b'"') => self.de.eat_char(), - _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), + match tri!(self.de.peek()) { + Some(b'0'..=b'9' | b'-') => { + let value = tri!(self.de.$method(visitor)); + match tri!(self.de.peek()) { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), + } + Ok(value) + } + Some(b' ' | b'\n' | b'\r' | b'\t') => { + Err(self.de.peek_error(ErrorCode::UnexpectedWhitespaceInKey)) + } + _ => Err(self.de.error(ErrorCode::ExpectedNumericKey)), } - - Ok(value) } }; } diff --git a/src/error.rs b/src/error.rs index 8a7b59901..b775c8000 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,6 +71,7 @@ impl Error { | ErrorCode::InvalidUnicodeCodePoint | ErrorCode::ControlCharacterWhileParsingString | ErrorCode::KeyMustBeAString + | ErrorCode::ExpectedNumericKey | ErrorCode::FloatKeyMustBeFinite | ErrorCode::UnexpectedWhitespaceInKey | ErrorCode::LoneLeadingSurrogateInHexEscape @@ -288,6 +289,9 @@ 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, @@ -369,6 +373,9 @@ 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)") } diff --git a/tests/test.rs b/tests/test.rs index 405ba421c..cdc37db89 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1897,7 +1897,10 @@ fn test_integer_key() { test_parse_ok(vec![(j, map)]); let j = r#"{"x":null}"#; - test_parse_err::>(&[(j, "expected value at line 1 column 3")]); + test_parse_err::>(&[( + j, + "invalid value: expected key to be a number in quotes at line 1 column 2", + )]); } #[test] @@ -1948,7 +1951,10 @@ fn test_float_key() { test_parse_ok(vec![(j, map)]); let j = r#"{"x": null}"#; - test_parse_err::>(&[(j, "expected value at line 1 column 3")]); + test_parse_err::>(&[( + j, + "invalid value: expected key to be a number in quotes at line 1 column 2", + )]); } #[test] From bb609907df4178634d0716171388feafebc6ecf9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 22:26:05 -0700 Subject: [PATCH 075/142] Resolve unnested_or_patterns pedantic clippy lint warning: unnested or-patterns --> src/de.rs:251:17 | 251 | Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns = note: `-W clippy::unnested-or-patterns` implied by `-W clippy::pedantic` help: nest the patterns | 251 | Some(b' ' | b'\n' | b'\t' | b'\r') => { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: unnested or-patterns --> src/de.rs:916:13 | 916 | e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), | ^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns = note: `-W clippy::unnested-or-patterns` implied by `-W clippy::pedantic` help: nest the patterns | 916 | e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), | ~~~~~~~~~~~~~~~~~ warning: unnested or-patterns --> src/de.rs:941:13 | 941 | e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), | ^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns help: nest the patterns | 941 | e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), | ~~~~~~~~~~~~~~~~~ warning: unnested or-patterns --> src/de.rs:1062:17 | 1062 | frame @ b'[' | frame @ b'{' => { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns help: nest the patterns | 1062 | frame @ (b'[' | b'{') => { | ~~~~~~~~~~~~~~~~~~~~~ warning: unnested or-patterns --> src/de.rs:2331:13 | 2331 | / Some(b' ') | Some(b'\n') | Some(b'\t') | Some(b'\r') | Some(b'"') | Some(b'[') 2332 | | | Some(b']') | Some(b'{') | Some(b'}') | Some(b',') | Some(b':') | None => Ok(()), | |___________________________________________________________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns help: nest the patterns | 2331 ~ Some(b' ' | b'\n' | b'\t' | b'\r' | b'"' | b'[' | b']' | b'{' | b'}' | b',' | 2332 ~ b':') | None => Ok(()), | --- src/de.rs | 12 ++++++------ src/lib.rs | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/de.rs b/src/de.rs index 45c855594..f23d656b7 100644 --- a/src/de.rs +++ b/src/de.rs @@ -248,7 +248,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 => { @@ -913,7 +913,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), - e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -938,7 +938,7 @@ impl<'de, R: Read<'de>> Deserializer { } match tri!(self.peek_or_null()) { - e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), + e @ (b'e' | b'E') => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -1059,7 +1059,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) @@ -2328,8 +2328,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( diff --git a/src/lib.rs b/src/lib.rs index c306f05db..2af99d886 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -320,8 +320,6 @@ 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, )] // Ignored clippy_pedantic lints #![allow( From 666b9cd915b2be238f9cd29a858ee3085fca40a3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 22:29:30 -0700 Subject: [PATCH 076/142] Buggy ptr_arg lint has been fixed in clippy --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2af99d886..62d8847ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -315,11 +315,9 @@ clippy::match_single_binding, clippy::needless_doctest_main, clippy::needless_late_init, - // clippy bug: https://github.com/rust-lang/rust-clippy/issues/8366 - clippy::ptr_arg, clippy::return_self_not_must_use, clippy::transmute_ptr_to_ptr, - clippy::unnecessary_wraps, + clippy::unnecessary_wraps )] // Ignored clippy_pedantic lints #![allow( From 66d28a87ab76d811b1e52343e86104939fd67193 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 22:30:49 -0700 Subject: [PATCH 077/142] Buggy iter_not_returning_iterator lint has been fixed in clippy --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 62d8847ae..d4c83253b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -321,8 +321,6 @@ )] // Ignored clippy_pedantic lints #![allow( - // buggy - clippy::iter_not_returning_iterator, // https://github.com/rust-lang/rust-clippy/issues/8285 // Deserializer::from_str, into_iter clippy::should_implement_trait, // integer and float ser/de requires these sorts of casts From f80ce0f0543365df13ab293dfb53388e20f450d8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 22:32:20 -0700 Subject: [PATCH 078/142] Enforce no use of question-mark operator --- src/de.rs | 6 +++--- src/lexical/algorithm.rs | 5 ++++- src/lexical/digit.rs | 5 ++++- src/lib.rs | 2 ++ src/number.rs | 12 ++++++------ src/raw.rs | 14 +++++++------- src/value/de.rs | 8 ++++---- src/value/mod.rs | 4 ++-- src/value/ser.rs | 4 ++-- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/de.rs b/src/de.rs index f23d656b7..4cf3cea1c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -860,7 +860,7 @@ 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)); @@ -1204,9 +1204,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) } } diff --git a/src/lexical/algorithm.rs b/src/lexical/algorithm.rs index a2cbf18af..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 { 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/lib.rs b/src/lib.rs index d4c83253b..563f4463b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,6 +356,8 @@ 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)] diff --git a/src/number.rs b/src/number.rs index 5ecbde873..7ff66681d 100644 --- a/src/number.rs +++ b/src/number.rs @@ -361,8 +361,8 @@ impl Serialize for Number { { 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() } } @@ -406,11 +406,11 @@ impl<'de> Deserialize<'de> for Number { 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) } } @@ -449,7 +449,7 @@ impl<'de> de::Deserialize<'de> for NumberKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(NumberKey) } } @@ -552,7 +552,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()))) } }; } diff --git a/src/raw.rs b/src/raw.rs index 6aa4ffcb6..01d18d62d 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -178,7 +178,7 @@ impl RawValue { /// - the input has capacity equal to its length. pub fn from_string(json: String) -> Result, Error> { { - let borrowed = crate::from_str::<&Self>(&json)?; + let borrowed = tri!(crate::from_str::<&Self>(&json)); if borrowed.json.len() < json.len() { return Ok(borrowed.to_owned()); } @@ -287,7 +287,7 @@ pub fn to_raw_value(value: &T) -> Result, Error> where 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())) } @@ -298,8 +298,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() } } @@ -322,7 +322,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)); } @@ -352,7 +352,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)); } @@ -392,7 +392,7 @@ impl<'de> Deserialize<'de> for RawKey { } } - deserializer.deserialize_identifier(FieldVisitor)?; + tri!(deserializer.deserialize_identifier(FieldVisitor)); Ok(RawKey) } } diff --git a/src/value/de.rs b/src/value/de.rs index 15cb67c2d..b5487b6e3 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -106,15 +106,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)) => { @@ -1330,7 +1330,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/mod.rs b/src/value/mod.rs index 64556278c..79ffe9488 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -182,11 +182,11 @@ impl Debug for Value { Value::Number(number) => Debug::fmt(number, formatter), Value::String(string) => write!(formatter, "String({:?})", string), Value::Array(vec) => { - formatter.write_str("Array ")?; + tri!(formatter.write_str("Array ")); Debug::fmt(vec, formatter) } Value::Object(map) => { - formatter.write_str("Object ")?; + tri!(formatter.write_str("Object ")); Debug::fmt(map, formatter) } } diff --git a/src/value/ser.rs b/src/value/ser.rs index 669a8525a..6ca53d4c5 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -650,7 +650,7 @@ impl serde::ser::SerializeStruct for SerializeMap { #[cfg(feature = "arbitrary_precision")] 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()) @@ -659,7 +659,7 @@ impl serde::ser::SerializeStruct for SerializeMap { #[cfg(feature = "raw_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()) From f89990455cbeeaa6a69d2aafbb354ee1071a7d84 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 22:56:49 -0700 Subject: [PATCH 079/142] Remove pre-NLL borrow checker workarounds NLL has been in use since Rust 1.31 (for edition 2018+) or Rust 1.36 (for edition 2015). error[E0505]: cannot move out of `json` because it is borrowed --> src/raw.rs:184:29 | 180 | let borrowed = ::from_str::<&Self>(&json)?; | ---- borrow of `json` occurs here ... 184 | Ok(Self::from_owned(json.into_boxed_str())) | ^^^^ move out of `json` occurs here error[E0499]: cannot borrow `self.formatter` as mutable more than once at a time --> src/ser.rs:453:13 | 444 | formatter: &mut self.formatter, | -------------- first mutable borrow occurs here ... 453 | self.formatter | ^^^^^^^^^^^^^^ second mutable borrow occurs here ... 456 | } | - first borrow ends here error[E0499]: cannot borrow `self.writer` as mutable more than once at a time --> src/ser.rs:454:34 | 443 | writer: &mut self.writer, | ----------- first mutable borrow occurs here ... 454 | .end_string(&mut self.writer) | ^^^^^^^^^^^ second mutable borrow occurs here ... 456 | } | - first borrow ends here --- src/raw.rs | 8 +++----- src/ser.rs | 20 +++++++++----------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/raw.rs b/src/raw.rs index 01d18d62d..651f4797d 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -177,11 +177,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 = tri!(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())) } diff --git a/src/ser.rs b/src/ser.rs index dbd30c453..e15db92bf 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -439,17 +439,15 @@ 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"))); } } self.formatter From 7933880d5f7b25282450ee67ce78e0bd9e53fa1d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:13:16 -0700 Subject: [PATCH 080/142] Delete dedicated whitespace-in-key error message I don't feel this carries its weight after PR 1035. --- src/de.rs | 20 ++++++++------------ src/error.rs | 7 ------- tests/test.rs | 5 +---- 3 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/de.rs b/src/de.rs index 4cf3cea1c..190b34ec5 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2126,19 +2126,15 @@ macro_rules! deserialize_numeric_key { { self.de.eat_char(); - match tri!(self.de.peek()) { - Some(b'0'..=b'9' | b'-') => { - let value = tri!(self.de.$method(visitor)); - match tri!(self.de.peek()) { - Some(b'"') => self.de.eat_char(), - _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), - } - Ok(value) - } - Some(b' ' | b'\n' | b'\r' | b'\t') => { - Err(self.de.peek_error(ErrorCode::UnexpectedWhitespaceInKey)) + if let Some(b'0'..=b'9' | b'-') = tri!(self.de.peek()) { + let value = tri!(self.de.$method(visitor)); + match tri!(self.de.peek()) { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), } - _ => Err(self.de.error(ErrorCode::ExpectedNumericKey)), + Ok(value) + } else { + Err(self.de.error(ErrorCode::ExpectedNumericKey)) } } }; diff --git a/src/error.rs b/src/error.rs index b775c8000..03555eb4c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -73,7 +73,6 @@ impl Error { | ErrorCode::KeyMustBeAString | ErrorCode::ExpectedNumericKey | ErrorCode::FloatKeyMustBeFinite - | ErrorCode::UnexpectedWhitespaceInKey | ErrorCode::LoneLeadingSurrogateInHexEscape | ErrorCode::TrailingComma | ErrorCode::TrailingCharacters @@ -295,9 +294,6 @@ pub(crate) enum ErrorCode { /// Object key is a non-finite float value. FloatKeyMustBeFinite, - /// Unexpected whitespace in a numeric key. - UnexpectedWhitespaceInKey, - /// Lone leading surrogate in hex escape. LoneLeadingSurrogateInHexEscape, @@ -379,9 +375,6 @@ impl Display for ErrorCode { ErrorCode::FloatKeyMustBeFinite => { f.write_str("float key must be finite (got NaN or +/-inf)") } - ErrorCode::UnexpectedWhitespaceInKey => { - f.write_str("unexpected whitespace in object key") - } ErrorCode::LoneLeadingSurrogateInHexEscape => { f.write_str("lone leading surrogate in hex escape") } diff --git a/tests/test.rs b/tests/test.rs index cdc37db89..8a4a216fd 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1901,14 +1901,11 @@ fn test_integer_key() { j, "invalid value: expected key to be a number in quotes at line 1 column 2", )]); -} -#[test] -fn test_integer_key_leading_whitespace() { let j = r#"{" 123":null}"#; test_parse_err::>(&[( j, - "unexpected whitespace in object key at line 1 column 3", + "invalid value: expected key to be a number in quotes at line 1 column 2", )]); } From de897a0be58f5e0bef717799df04976b92f5b762 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:17:17 -0700 Subject: [PATCH 081/142] Straighten control flow of deserialize_numeric_key --- src/de.rs | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/de.rs b/src/de.rs index 190b34ec5..f8f5fc0ee 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2126,16 +2126,19 @@ macro_rules! deserialize_numeric_key { { self.de.eat_char(); - if let Some(b'0'..=b'9' | b'-') = tri!(self.de.peek()) { - let value = tri!(self.de.$method(visitor)); - match tri!(self.de.peek()) { - Some(b'"') => self.de.eat_char(), - _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), - } - Ok(value) - } else { - Err(self.de.error(ErrorCode::ExpectedNumericKey)) + match tri!(self.de.peek()) { + Some(b'0'..=b'9' | b'-') => {} + _ => return Err(self.de.error(ErrorCode::ExpectedNumericKey)), } + + let value = tri!(self.de.$method(visitor)); + + match tri!(self.de.peek()) { + Some(b'"') => self.de.eat_char(), + _ => return Err(self.de.peek_error(ErrorCode::ExpectedDoubleQuote)), + } + + Ok(value) } }; } From e8e534254f85471a237376735cc07912d1a5920e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:23:26 -0700 Subject: [PATCH 082/142] Deduplicate a bunch of identical deserialize_numeric_key expansions --- src/de.rs | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index f8f5fc0ee..0ac124589 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2120,6 +2120,15 @@ struct MapKey<'a, R: 'a> { 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>, @@ -2131,7 +2140,7 @@ macro_rules! deserialize_numeric_key { _ => return Err(self.de.error(ErrorCode::ExpectedNumericKey)), } - let value = tri!(self.de.$method(visitor)); + let value = tri!(self.de.$delegate(visitor)); match tri!(self.de.peek()) { Some(b'"') => self.de.eat_char(), @@ -2143,6 +2152,13 @@ macro_rules! deserialize_numeric_key { }; } +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>, @@ -2166,13 +2182,16 @@ where deserialize_numeric_key!(deserialize_i16); deserialize_numeric_key!(deserialize_i32); deserialize_numeric_key!(deserialize_i64); - deserialize_numeric_key!(deserialize_i128); + 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_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); #[inline] From 9604317e83dc8398bb7a3f380df48bead93dcdc6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:30:23 -0700 Subject: [PATCH 083/142] Rewrap PR 1027 comments to 80 columns and reword --- tests/test.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 8a4a216fd..2803739db 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1956,8 +1956,8 @@ fn test_float_key() { #[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, we - // would use a crate like `ordered-float` instead. + // 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); @@ -1985,8 +1985,8 @@ fn test_deny_non_finite_f32_key() { #[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, we - // would use a crate like `ordered-float` instead. + // 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); From d816a2e8be89e841ecd96a9b8ad390d4e6dc752a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:35:57 -0700 Subject: [PATCH 084/142] Merge adjacent integer key tests --- tests/test.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 2803739db..710b4ee76 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1896,17 +1896,16 @@ fn test_integer_key() { 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", - )]); - - let j = r#"{" 123":null}"#; - test_parse_err::>(&[( - j, - "invalid value: expected key to be a number in quotes at line 1 column 2", - )]); + 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", + ), + ]); } #[test] From af242a1d0586266367d4cfb0f8b919d3a8aaa907 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:39:11 -0700 Subject: [PATCH 085/142] Add optional trailing commas in treemap macro --- tests/test.rs | 52 +++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 710b4ee76..96068a5b9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -53,7 +53,7 @@ macro_rules! treemap { () => { BTreeMap::new() }; - ($($k:expr => $v:expr),+) => { + ($($k:expr => $v:expr),+ $(,)?) => { { let mut m = BTreeMap::new(); $( @@ -264,7 +264,7 @@ fn test_write_object() { ( treemap!( "a".to_string() => true, - "b".to_string() => false + "b".to_string() => false, ), "{\"a\":true,\"b\":false}", ), @@ -275,7 +275,7 @@ fn test_write_object() { treemap![ "a".to_string() => treemap![], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{},\"b\":{},\"c\":{}}", ), @@ -284,10 +284,10 @@ fn test_write_object() { "a".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"b\":{},\"c\":{}}", ), @@ -297,9 +297,9 @@ fn test_write_object() { "b".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "{\"a\":{},\"b\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"c\":{}}", ), @@ -310,8 +310,8 @@ fn test_write_object() { "c".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "c".to_string() => treemap![], + ], ], "{\"a\":{},\"b\":{},\"c\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}}}", ), @@ -324,7 +324,7 @@ fn test_write_object() { treemap![ "a".to_string() => treemap![], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": {}, @@ -337,10 +337,10 @@ fn test_write_object() { "a".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": { @@ -364,9 +364,9 @@ fn test_write_object() { "b".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], - "c".to_string() => treemap![] + "c".to_string() => treemap![], ], pretty_str!({ "a": {}, @@ -391,8 +391,8 @@ fn test_write_object() { "c".to_string() => treemap![ "a".to_string() => treemap!["a" => vec![1,2,3]], "b".to_string() => treemap![], - "c".to_string() => treemap![] - ] + "c".to_string() => treemap![], + ], ], pretty_str!({ "a": {}, @@ -423,7 +423,7 @@ fn test_write_object() { ( treemap!( "a".to_string() => true, - "b".to_string() => false + "b".to_string() => false, ), pretty_str!( { "a": true, @@ -1192,8 +1192,8 @@ fn test_parse_object() { treemap!( "a".to_string() => treemap!( "b".to_string() => 3u64, - "c".to_string() => 4 - ) + "c".to_string() => 4, + ), ), )]); @@ -1369,7 +1369,7 @@ fn test_parse_enum() { ), treemap!( "a".to_string() => Animal::Dog, - "b".to_string() => Animal::Frog("Henry".to_string(), vec![]) + "b".to_string() => Animal::Frog("Henry".to_string(), vec![]), ), )]); } @@ -1664,7 +1664,7 @@ fn test_deserialize_from_stream() { fn test_serialize_rejects_bool_keys() { let map = treemap!( true => 2, - false => 4 + false => 4, ); let err = to_vec(&map).unwrap_err(); @@ -1676,7 +1676,7 @@ 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(); @@ -1890,7 +1890,7 @@ 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)]); @@ -1911,7 +1911,7 @@ fn test_integer_key() { #[test] fn test_integer128_key() { let map = treemap! { - 100000000000000000000000000000000000000u128 => () + 100000000000000000000000000000000000000u128 => (), }; let j = r#"{"100000000000000000000000000000000000000":null}"#; assert_eq!(to_string(&map).unwrap(), j); @@ -2034,7 +2034,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)]); @@ -2044,7 +2044,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)]); From 51b1bd02b3ba05e1269e2636280af1d51499083d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:41:33 -0700 Subject: [PATCH 086/142] Add test of trailing whitespace in integer key --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index 96068a5b9..43007fdcb 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1905,6 +1905,7 @@ fn test_integer_key() { 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"), ]); } From 468547fc6f6fbab4c537b561efc283382154c0e1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:19:37 -0700 Subject: [PATCH 087/142] Make number deserialization usable on text that lives shorter than input data --- src/de.rs | 157 +++++++++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 74 deletions(-) diff --git a/src/de.rs b/src/de.rs index 0ac124589..eaaf6f202 100644 --- a/src/de.rs +++ b/src/de.rs @@ -309,9 +309,9 @@ impl<'de, R: Read<'de>> Deserializer { self.fix_position(err) } - fn deserialize_number(&mut self, visitor: V) -> Result + 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, @@ -335,6 +335,79 @@ impl<'de, R: Read<'de>> Deserializer { } } + #[cfg(feature = "float_roundtrip")] + 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 + } + + 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)), + } + } + + 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' => { @@ -1258,11 +1331,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) } }; } @@ -1424,77 +1501,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 - } - - 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 From 98c4db12796dbede4329d0eb9242e2fbb26e0424 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 10 Jul 2023 23:46:20 -0700 Subject: [PATCH 088/142] Add test of leading and trailing whitespace in integer key in from_value --- tests/test.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 43007fdcb..3781f8dbc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1907,6 +1907,12 @@ fn test_integer_key() { ), (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] From f1a28a3615cc049ddc4d75df9a785582bfe1155f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 00:09:35 -0700 Subject: [PATCH 089/142] Inline de::from_trait into deserialize_numeric_key --- src/value/de.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index b5487b6e3..aadc0cb0a 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1126,12 +1126,17 @@ macro_rules! deserialize_numeric_key { where V: Visitor<'de>, { - let parsed = crate::from_str(&self.key); - match (parsed, self.key) { - (Ok(integer), _) => visitor.$visit(integer), - (Err(_), Cow::Borrowed(s)) => visitor.visit_borrowed_str(s), + let mut de = crate::Deserializer::from_str(&self.key); + let parsed = Deserialize::deserialize(&mut de); + if let Ok(integer) = parsed { + if de.end().is_ok() { + return visitor.$visit(integer); + } + } + match self.key { + Cow::Borrowed(s) => visitor.visit_borrowed_str(s), #[cfg(any(feature = "std", feature = "alloc"))] - (Err(_), Cow::Owned(s)) => visitor.visit_string(s), + Cow::Owned(s) => visitor.visit_string(s), } } }; From 0770869415cc78cc800a6141e99b91014be5dd94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 00:28:16 -0700 Subject: [PATCH 090/142] Delete str fallback for Value numeric key methods --- src/value/de.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index aadc0cb0a..b7bcf8fd9 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1127,17 +1127,9 @@ macro_rules! deserialize_numeric_key { V: Visitor<'de>, { let mut de = crate::Deserializer::from_str(&self.key); - let parsed = Deserialize::deserialize(&mut de); - if let Ok(integer) = parsed { - if de.end().is_ok() { - return visitor.$visit(integer); - } - } - match self.key { - Cow::Borrowed(s) => visitor.visit_borrowed_str(s), - #[cfg(any(feature = "std", feature = "alloc"))] - Cow::Owned(s) => visitor.visit_string(s), - } + let parsed = tri!(Deserialize::deserialize(&mut de)); + tri!(de.end()); + visitor.$visit(parsed) } }; } From defa8964304e907065a2938e2d313df9576839eb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 00:30:47 -0700 Subject: [PATCH 091/142] Directly use caller's Visitor to deserialize numeric key from Value --- src/de.rs | 8 ++++---- src/value/de.rs | 38 +++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/de.rs b/src/de.rs index eaaf6f202..65429bfd0 100644 --- a/src/de.rs +++ b/src/de.rs @@ -309,7 +309,7 @@ impl<'de, R: Read<'de>> Deserializer { self.fix_position(err) } - fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result + pub(crate) fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result where V: de::Visitor<'any>, { @@ -336,7 +336,7 @@ impl<'de, R: Read<'de>> Deserializer { } #[cfg(feature = "float_roundtrip")] - fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result + pub(crate) fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result where V: de::Visitor<'any>, { @@ -346,7 +346,7 @@ impl<'de, R: Read<'de>> Deserializer { val } - fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result + pub(crate) fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result where V: de::Visitor<'any>, { @@ -378,7 +378,7 @@ impl<'de, R: Read<'de>> Deserializer { } } - fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result + pub(crate) fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result where V: de::Visitor<'any>, { diff --git a/src/value/de.rs b/src/value/de.rs index b7bcf8fd9..95205dd42 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1121,15 +1121,19 @@ struct MapKeyDeserializer<'de> { } macro_rules! deserialize_numeric_key { - ($method:ident => $visit:ident) => { + ($method:ident) => { + deserialize_numeric_key!($method, deserialize_number); + }; + + ($method:ident, $using:ident) => { fn $method(self, visitor: V) -> Result where V: Visitor<'de>, { let mut de = crate::Deserializer::from_str(&self.key); - let parsed = tri!(Deserialize::deserialize(&mut de)); + let number = tri!(de.$using(visitor)); tri!(de.end()); - visitor.$visit(parsed) + Ok(number) } }; } @@ -1144,18 +1148,22 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { BorrowedCowStrDeserializer::new(self.key).deserialize_any(visitor) } - deserialize_numeric_key!(deserialize_i8 => visit_i8); - deserialize_numeric_key!(deserialize_i16 => visit_i16); - deserialize_numeric_key!(deserialize_i32 => visit_i32); - deserialize_numeric_key!(deserialize_i64 => visit_i64); - deserialize_numeric_key!(deserialize_i128 => visit_i128); - deserialize_numeric_key!(deserialize_u8 => visit_u8); - deserialize_numeric_key!(deserialize_u16 => visit_u16); - deserialize_numeric_key!(deserialize_u32 => visit_u32); - deserialize_numeric_key!(deserialize_u64 => visit_u64); - deserialize_numeric_key!(deserialize_u128 => visit_u128); - deserialize_numeric_key!(deserialize_f32 => visit_f32); - deserialize_numeric_key!(deserialize_f64 => visit_f64); + 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); #[inline] fn deserialize_option(self, visitor: V) -> Result From 9f4c4af30ee3b0a49ba99529ffd505878e16347c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:37:03 -0700 Subject: [PATCH 092/142] Insert check for whitespace surrounding numeric map key --- src/de.rs | 2 +- src/value/de.rs | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index 65429bfd0..7f3145980 100644 --- a/src/de.rs +++ b/src/de.rs @@ -209,7 +209,7 @@ impl<'de, R: Read<'de>> Deserializer { self.disable_recursion_limit = true; } - fn peek(&mut self) -> Result> { + pub(crate) fn peek(&mut self) -> Result> { self.read.peek() } diff --git a/src/value/de.rs b/src/value/de.rs index 95205dd42..3f14abb3b 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::error::{Error, ErrorCode}; use crate::map::Map; use crate::number::Number; use crate::value::Value; @@ -1131,8 +1131,18 @@ macro_rules! deserialize_numeric_key { V: Visitor<'de>, { 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)); - tri!(de.end()); + + if tri!(de.peek()).is_some() { + return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)); + } + Ok(number) } }; From 1e1bfa8eae6677e48cdefd1a7f1ca6abcc0dffc5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:43:40 -0700 Subject: [PATCH 093/142] Release 1.0.101 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ccb0c6fa1..7fc02af3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.100" # remember to update html_root_url +version = "1.0.101" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 563f4463b..784fd2729 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.100")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.101")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 3ddda759cf4c1c9d7b210943aea6f3a58df03a2b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:54:00 -0700 Subject: [PATCH 094/142] Format PR 1037 with rustfmt --- tests/test.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 3781f8dbc..e4708bfb3 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1909,10 +1909,16 @@ fn test_integer_key() { ]); let err = from_value::>(json!({" 123":null})).unwrap_err(); - assert_eq!(err.to_string(), "invalid value: expected key to be a number in quotes"); + 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"); + assert_eq!( + err.to_string(), + "invalid value: expected key to be a number in quotes", + ); } #[test] From 55a7f5c35281feb75f27778200f2a8bca01680b3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:53:48 -0700 Subject: [PATCH 095/142] Inline Serializer::serialize_seq into serialize_bytes --- src/ser.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index e15db92bf..62f5c4f01 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -187,10 +187,27 @@ where 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()))); + tri!(self + .formatter + .begin_array(&mut self.writer) + .map_err(Error::io)); + let mut seq = if value.is_empty() { + tri!(self + .formatter + .end_array(&mut self.writer) + .map_err(Error::io)); + Compound::Map { + ser: self, + state: State::Empty, + } + } else { + Compound::Map { + ser: self, + state: State::First, + } + }; for byte in value { tri!(seq.serialize_element(byte)); } From 1b72f2b34bccd40abef5f283034540b4cb460856 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 15:59:44 -0700 Subject: [PATCH 096/142] Inline SerializeSeq::serialize_element into serialize_bytes --- src/ser.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 62f5c4f01..5d91fa8f6 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -193,7 +193,7 @@ where .formatter .begin_array(&mut self.writer) .map_err(Error::io)); - let mut seq = if value.is_empty() { + let seq = if value.is_empty() { tri!(self .formatter .end_array(&mut self.writer) @@ -203,14 +203,21 @@ where state: State::Empty, } } else { - Compound::Map { - ser: self, - state: State::First, + let mut state = State::First; + for byte in value { + tri!(self + .formatter + .begin_array_value(&mut self.writer, state == State::First) + .map_err(Error::io)); + state = State::Rest; + tri!(byte.serialize(&mut *self)); + tri!(self + .formatter + .end_array_value(&mut self.writer) + .map_err(Error::io)); } + Compound::Map { ser: self, state } }; - for byte in value { - tri!(seq.serialize_element(byte)); - } seq.end() } From 0e2c949638571990059c9783cf6f06f0d77126b2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 16:01:27 -0700 Subject: [PATCH 097/142] Inline SerializeSeq::end into serialize_bytes --- src/ser.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 5d91fa8f6..0f422d182 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -188,20 +188,15 @@ where } fn serialize_bytes(self, value: &[u8]) -> Result<()> { - use serde::ser::SerializeSeq; tri!(self .formatter .begin_array(&mut self.writer) .map_err(Error::io)); - let seq = if value.is_empty() { + if value.is_empty() { tri!(self .formatter .end_array(&mut self.writer) .map_err(Error::io)); - Compound::Map { - ser: self, - state: State::Empty, - } } else { let mut state = State::First; for byte in value { @@ -216,9 +211,12 @@ where .end_array_value(&mut self.writer) .map_err(Error::io)); } - Compound::Map { ser: self, state } - }; - seq.end() + tri!(self + .formatter + .end_array(&mut self.writer) + .map_err(Error::io)); + } + Ok(()) } #[inline] From 44b4a6c8599d8988c82e3932a7a1cf3a38f377c3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 16:03:21 -0700 Subject: [PATCH 098/142] Simplify serialize_bytes --- src/ser.rs | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 0f422d182..bcf11d4ac 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -192,31 +192,22 @@ where .formatter .begin_array(&mut self.writer) .map_err(Error::io)); - if value.is_empty() { + let mut first = true; + for byte in value { tri!(self .formatter - .end_array(&mut self.writer) + .begin_array_value(&mut self.writer, first) .map_err(Error::io)); - } else { - let mut state = State::First; - for byte in value { - tri!(self - .formatter - .begin_array_value(&mut self.writer, state == State::First) - .map_err(Error::io)); - state = State::Rest; - tri!(byte.serialize(&mut *self)); - tri!(self - .formatter - .end_array_value(&mut self.writer) - .map_err(Error::io)); - } + tri!(byte.serialize(&mut *self)); tri!(self .formatter - .end_array(&mut self.writer) + .end_array_value(&mut self.writer) .map_err(Error::io)); + first = false; } - Ok(()) + self.formatter + .end_array(&mut self.writer) + .map_err(Error::io) } #[inline] From 6ad54959060c8b87b4f7459e203d4750dcb1fd94 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 16:05:05 -0700 Subject: [PATCH 099/142] Inline u8::serialize into serialize_bytes --- src/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ser.rs b/src/ser.rs index bcf11d4ac..dbdec8543 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -198,7 +198,7 @@ where .formatter .begin_array_value(&mut self.writer, first) .map_err(Error::io)); - tri!(byte.serialize(&mut *self)); + tri!(self.serialize_u8(*byte)); tri!(self .formatter .end_array_value(&mut self.writer) From 857b010ce0dd98ba37e9c7c773b72940b7605008 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 16:05:37 -0700 Subject: [PATCH 100/142] Inline Serializer::serialize_u8 into serialize_bytes --- src/ser.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ser.rs b/src/ser.rs index dbdec8543..7657db9ca 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -198,7 +198,10 @@ where .formatter .begin_array_value(&mut self.writer, first) .map_err(Error::io)); - tri!(self.serialize_u8(*byte)); + tri!(self + .formatter + .write_u8(&mut self.writer, *byte) + .map_err(Error::io)); tri!(self .formatter .end_array_value(&mut self.writer) From a1ca32a5c759066b3d8a84267078869ce74df773 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 16:11:25 -0700 Subject: [PATCH 101/142] Factor out byte array serialization to a new Formatter method --- src/ser.rs | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 7657db9ca..6bb6fd761 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -187,29 +187,10 @@ where format_escaped_str(&mut self.writer, &mut self.formatter, value).map_err(Error::io) } + #[inline] fn serialize_bytes(self, value: &[u8]) -> Result<()> { - tri!(self - .formatter - .begin_array(&mut self.writer) - .map_err(Error::io)); - let mut first = true; - for byte in value { - tri!(self - .formatter - .begin_array_value(&mut self.writer, first) - .map_err(Error::io)); - tri!(self - .formatter - .write_u8(&mut self.writer, *byte) - .map_err(Error::io)); - tri!(self - .formatter - .end_array_value(&mut self.writer) - .map_err(Error::io)); - first = false; - } self.formatter - .end_array(&mut self.writer) + .write_byte_array(&mut self.writer, value) .map_err(Error::io) } @@ -1786,6 +1767,24 @@ pub trait Formatter { 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 /// writer. #[inline] From 658689d36bc4c4c04b6d40bca4719ee99d09a781 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 11 Jul 2023 17:20:00 -0700 Subject: [PATCH 102/142] Release 1.0.102 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fc02af3e..0227b8ea9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.101" # remember to update html_root_url +version = "1.0.102" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 784fd2729..3bf0a8f61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.101")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.102")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From d1a07e29f2a665e133a8bee4d58666bea8ea40ef Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jul 2023 13:27:01 -0700 Subject: [PATCH 103/142] Fix rustdoc::bare_urls lint in lexical code warning: this URL is not a hyperlink --> src/lexical/errors.rs:9:9 | 9 | //! https://golang.org/src/strconv/atof.go | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use an automatic link instead: `` | = note: bare URLs are not automatically turned into clickable links = note: `#[warn(rustdoc::bare_urls)]` on by default --- src/lexical/errors.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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::*; From 9c2879a848ae3117d2c99a03d415bb77a1ac7d34 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jul 2023 14:18:08 -0700 Subject: [PATCH 104/142] Opt in to generate-link-to-definition when building on docs.rs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0227b8ea9..e30500ba6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ doc-scrape-examples = false [package.metadata.docs.rs] features = ["raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--cfg", "docsrs"] +rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] [package.metadata.playground] features = ["raw_value"] From 54bcb4dc94a29a0fe67ec735c5525760823a5499 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jul 2023 14:18:32 -0700 Subject: [PATCH 105/142] Release 1.0.103 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e30500ba6..7893b2c4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.102" # remember to update html_root_url +version = "1.0.103" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 3bf0a8f61..974d673cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.102")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.103")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 8f90eacf6c6335fbd925b1d663e46f4a7592ebba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 Jul 2023 18:13:57 -0700 Subject: [PATCH 106/142] Delete inline attributes throughout test suite --- tests/test.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index e4708bfb3..acf5de480 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1452,7 +1452,6 @@ fn test_serialize_seq_with_no_len() { where T: ser::Serialize, { - #[inline] fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -1479,7 +1478,6 @@ fn test_serialize_seq_with_no_len() { formatter.write_str("array") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1487,7 +1485,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>, @@ -1538,7 +1535,6 @@ 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, @@ -1566,7 +1562,6 @@ fn test_serialize_map_with_no_len() { formatter.write_str("map") } - #[inline] fn visit_unit(self) -> Result, E> where E: de::Error, @@ -1574,7 +1569,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>, From 8e8db8ce0cf066d8ef4cbe0167c3a97d18a49674 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Wed, 26 Jul 2023 22:04:37 +0300 Subject: [PATCH 107/142] Implement IntoDeserializer for &Value --- src/value/de.rs | 8 ++++++++ tests/test.rs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/src/value/de.rs b/src/value/de.rs index 3f14abb3b..2090dd009 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -482,6 +482,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, } diff --git a/tests/test.rs b/tests/test.rs index acf5de480..8d9a5942a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2472,6 +2472,12 @@ 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"); } From ab084832926cf7d733785c8801aa4fcf96523956 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 26 Jul 2023 12:30:09 -0700 Subject: [PATCH 108/142] Release 1.0.104 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7893b2c4d..7fd2b36d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.103" # remember to update html_root_url +version = "1.0.104" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 974d673cc..c322ed8a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.103")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.104")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 8652bf2bc5dac3b5496725452cfe14ca573f6232 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 11 Aug 2023 20:11:30 -0700 Subject: [PATCH 109/142] Resolve ignored_unit_patterns pedantic clippy lint warning: matching over `()` is more explicit --> src/de.rs:2409:59 | 2409 | ... self.peek_end_of_value().map(|_| value) | ^ help: use `()` instead of `_`: `()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns = note: `-W clippy::ignored-unit-patterns` implied by `-W clippy::pedantic` --- src/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index 7f3145980..7aad50b96 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2406,7 +2406,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) => { From 58dd8d9b89ef72ebb7a510fc07ba207279649668 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Aug 2023 09:49:25 -0700 Subject: [PATCH 110/142] Add test for boolean keys in map --- tests/test.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 8d9a5942a..83f1620be 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2018,6 +2018,14 @@ fn test_deny_non_finite_f64_key() { 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] fn test_borrowed_key() { let map: BTreeMap<&str, ()> = from_str("{\"borrowed\":null}").unwrap(); From 283a68b722aeea11fa5787876fa981b999cbdc38 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Aug 2023 09:50:40 -0700 Subject: [PATCH 111/142] Support serializing bool in map keys --- src/ser.rs | 17 +++++++++++++++-- src/value/ser.rs | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 6bb6fd761..3742e0bef 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -827,8 +827,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<()> { diff --git a/src/value/ser.rs b/src/value/ser.rs index 6ca53d4c5..835fa9080 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -483,8 +483,8 @@ 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(value.to_string()) } fn serialize_i8(self, value: i8) -> Result { From 68a55823ae484df38b3f0e47fd21497ca5bb2704 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Aug 2023 09:55:01 -0700 Subject: [PATCH 112/142] Support deserializing bool in map keys --- src/de.rs | 37 ++++++++++++++++++++++++++++++++++++- src/value/de.rs | 20 ++++++++++++++++++-- 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index 7aad50b96..9975b40a5 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2203,6 +2203,41 @@ where deserialize_numeric_key!(deserialize_f32, deserialize_f32); deserialize_numeric_key!(deserialize_f64); + 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] fn deserialize_option(self, visitor: V) -> Result where @@ -2258,7 +2293,7 @@ where } forward_to_deserialize_any! { - bool char str string unit unit_struct seq tuple tuple_struct map struct + char str string unit unit_struct seq tuple tuple_struct map struct identifier ignored_any } } diff --git a/src/value/de.rs b/src/value/de.rs index 2090dd009..1e8b5acbb 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1183,6 +1183,22 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { deserialize_numeric_key!(deserialize_i128, do_deserialize_i128); deserialize_numeric_key!(deserialize_u128, do_deserialize_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] fn deserialize_option(self, visitor: V) -> Result where @@ -1219,8 +1235,8 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { } forward_to_deserialize_any! { - bool 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 } } From 9b69f16813181d0baf473ad855c1724872366d4a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Aug 2023 14:58:56 -0700 Subject: [PATCH 113/142] Delete test_serialize_rejects_bool_keys --- tests/test.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 83f1620be..e548b7dae 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1654,17 +1654,6 @@ 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!( From 0daacdd52e4abb4a47a7f7fc598c5bd4492aa104 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 15 Aug 2023 15:05:28 -0700 Subject: [PATCH 114/142] Release 1.0.105 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7fd2b36d5..335ad3baa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.104" # remember to update html_root_url +version = "1.0.105" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index c322ed8a4..83f48a031 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -300,7 +300,7 @@ //! [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.104")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.105")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From d0c979aaf12990da8c18927673356cdf6d0400aa Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 3 Sep 2023 21:25:33 +0200 Subject: [PATCH 115/142] chore: Remove no_btreemap_retain feature from build.rs --- build.rs | 6 ------ src/map.rs | 1 - tests/map.rs | 1 - 3 files changed, 8 deletions(-) diff --git a/build.rs b/build.rs index 0e12602e4..fca6c3b68 100644 --- a/build.rs +++ b/build.rs @@ -33,12 +33,6 @@ fn main() { if minor < 45 { println!("cargo:rustc-cfg=no_btreemap_remove_entry"); } - - // BTreeMap::retain - // https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html#stabilized-apis - if minor < 53 { - println!("cargo:rustc-cfg=no_btreemap_retain"); - } } fn rustc_minor_version() -> Option { diff --git a/src/map.rs b/src/map.rs index a1b622294..c088d02d0 100644 --- a/src/map.rs +++ b/src/map.rs @@ -276,7 +276,6 @@ impl Map { /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` /// returns `false`. - #[cfg(not(no_btreemap_retain))] #[inline] pub fn retain(&mut self, f: F) where diff --git a/tests/map.rs b/tests/map.rs index ae0196956..538cd16ae 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -35,7 +35,6 @@ fn test_append() { assert!(val.is_empty()); } -#[cfg(not(no_btreemap_retain))] #[test] fn test_retain() { let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); From 716cb8fb3026152ae7399dd32e8b14f1b88c4f5c Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 3 Sep 2023 21:31:19 +0200 Subject: [PATCH 116/142] chore: Remove no_btreemap_get_key_value and no_btreemap_remove_entry. --- build.rs | 12 ------------ src/map.rs | 38 -------------------------------------- 2 files changed, 50 deletions(-) diff --git a/build.rs b/build.rs index fca6c3b68..bc2878cf2 100644 --- a/build.rs +++ b/build.rs @@ -21,18 +21,6 @@ fn main() { 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"); - } - - // 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"); - } } fn rustc_minor_version() -> Option { diff --git a/src/map.rs b/src/map.rs index c088d02d0..32a8cd01d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -106,7 +106,6 @@ 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. #[inline] - #[cfg(any(feature = "preserve_order", not(no_btreemap_get_key_value)))] pub fn get_key_value(&self, key: &Q) -> Option<(&String, &Value)> where String: Borrow, @@ -153,44 +152,7 @@ impl Map { String: Borrow, Q: ?Sized + Ord + Eq + Hash, { - #[cfg(any(feature = "preserve_order", not(no_btreemap_remove_entry)))] 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, - ))] - { - use core::ops::{Bound, RangeBounds}; - - 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) - } - } - - 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)) - } } /// Moves all elements from other into self, leaving other empty. From c754f0344b61bf20fe76c205fd8d7a3d46138a96 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sun, 3 Sep 2023 22:42:32 +0200 Subject: [PATCH 117/142] Remove limb_width32 and limb_width64 features Moved dispatch directly into the type system. --- build.rs | 36 ---------------------- src/lexical/math.rs | 60 +++++++++++++++++++++++-------------- src/lexical/mod.rs | 9 ++---- src/lexical/small_powers.rs | 3 -- 4 files changed, 40 insertions(+), 68 deletions(-) delete mode 100644 build.rs diff --git a/build.rs b/build.rs deleted file mode 100644 index bc2878cf2..000000000 --- a/build.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::env; -use std::process::Command; -use std::str::{self, FromStr}; - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - - // 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, - }; -} - -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; - } - let next = pieces.next()?; - u32::from_str(next).ok() -} diff --git a/src/lexical/math.rs b/src/lexical/math.rs index d7122bffa..9a2792b18 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -6,7 +6,6 @@ //! buffers, so for a `vec![0, 1, 2, 3]`, `3` is the most significant limb, //! and `0` is the least significant limb. -use super::large_powers; use super::num::*; use super::small_powers::*; use alloc::vec::Vec; @@ -36,31 +35,48 @@ use core::{cmp, iter, mem}; // requiring software emulation. // sparc64 (`UMUL` only supported double-word arguments). -// 32-BIT LIMB -#[cfg(limb_width_32)] -pub type Limb = u32; - -#[cfg(limb_width_32)] -pub const POW5_LIMB: &[Limb] = &POW5_32; - -#[cfg(limb_width_32)] -pub const POW10_LIMB: &[Limb] = &POW10_32; +#[doc(hidden)] +pub trait LimbConfig { + type Limb: 'static; + type Wide: 'static; + const POW5_LIMB: &'static [Self::Limb]; + const POW10_LIMB: &'static [Self::Limb]; + const LARGE_POWERS: &'static [&'static [Self::Limb]]; +} -#[cfg(limb_width_32)] -type Wide = u64; +// 32-BIT LIMB +#[doc(hidden)] +pub struct LimbConfig32; + +impl LimbConfig for LimbConfig32 { + type Limb = u32; + type Wide = u64; + const POW5_LIMB: &'static [Self::Limb] = &POW5_32; + const POW10_LIMB: &'static [Self::Limb] = &POW10_32; + const LARGE_POWERS: &'static [&'static [Self::Limb]] = &super::large_powers32::POW5; +} // 64-BIT LIMB -#[cfg(limb_width_64)] -pub type Limb = u64; - -#[cfg(limb_width_64)] -pub const POW5_LIMB: &[Limb] = &POW5_64; +#[doc(hidden)] +pub struct LimbConfig64; +impl LimbConfig for LimbConfig64 { + type Limb = u64; + type Wide = u128; + const POW5_LIMB: &'static [Self::Limb] = &POW5_64; + const POW10_LIMB: &'static [Self::Limb] = &POW10_64; + const LARGE_POWERS: &'static [&'static [Self::Limb]] = &super::large_powers64::POW5; +} -#[cfg(limb_width_64)] -pub const POW10_LIMB: &[Limb] = &POW10_64; +#[cfg(any(target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64", target_arch = x86_64))] +type PlatformLimbConfig = LimbConfig64; +#[cfg(not(any(target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64", target_arch = x86_64)))] +type PlatformLimbConfig = LimbConfig32; -#[cfg(limb_width_64)] -type Wide = u128; +pub type Limb = ::Limb; +type Wide = ::Wide; +pub const POW5_LIMB: &[Limb] = PlatformLimbConfig::POW5_LIMB; +pub const POW10_LIMB: &[Limb] = PlatformLimbConfig::POW10_LIMB; +const LARGE_POWERS: &'static [&'static [Limb]] = PlatformLimbConfig::LARGE_POWERS; /// Cast to limb type. #[inline] @@ -391,7 +407,7 @@ mod small { use super::large::KARATSUBA_CUTOFF; let small_powers = POW5_LIMB; - let large_powers = large_powers::POW5; + let large_powers = LARGE_POWERS; if n == 0 { // No exponent, just return. diff --git a/src/lexical/mod.rs b/src/lexical/mod.rs index b1a45e218..22784144a 100644 --- a/src/lexical/mod.rs +++ b/src/lexical/mod.rs @@ -20,7 +20,8 @@ mod digit; mod errors; pub(crate) mod exponent; pub(crate) mod float; -mod large_powers; +mod large_powers32; +mod large_powers64; pub(crate) mod math; pub(crate) mod num; pub(crate) mod parse; @@ -28,11 +29,5 @@ pub(crate) mod rounding; mod shift; mod small_powers; -#[cfg(limb_width_32)] -mod large_powers32; - -#[cfg(limb_width_64)] -mod large_powers64; - // API pub use self::parse::{parse_concise_float, parse_truncated_float}; diff --git a/src/lexical/small_powers.rs b/src/lexical/small_powers.rs index 219d82611..ac3f3aad7 100644 --- a/src/lexical/small_powers.rs +++ b/src/lexical/small_powers.rs @@ -3,19 +3,16 @@ //! Pre-computed small powers. // 32 BIT -#[cfg(limb_width_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)] pub(crate) const POW10_32: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // 64 BIT -#[cfg(limb_width_64)] pub(crate) const POW5_64: [u64; 28] = [ 1, 5, From 16e04ceeddfdfad18fb1ae8530695a318fa9bc02 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com~> Date: Sun, 3 Sep 2023 23:17:27 +0200 Subject: [PATCH 118/142] fixup! Remove limb_width32 and limb_width64 features --- src/lexical/math.rs | 28 ++++++++++++++++++++++++---- tests/lexical/math.rs | 14 ++++++++++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 9a2792b18..3c0ea0c34 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -67,9 +67,19 @@ impl LimbConfig for LimbConfig64 { const LARGE_POWERS: &'static [&'static [Self::Limb]] = &super::large_powers64::POW5; } -#[cfg(any(target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64", target_arch = x86_64))] +#[cfg(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +))] type PlatformLimbConfig = LimbConfig64; -#[cfg(not(any(target_arch = "aarch64", target_arch = "mips64", target_arch = "powerpc64", target_arch = x86_64)))] +#[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +)))] type PlatformLimbConfig = LimbConfig32; pub type Limb = ::Limb; @@ -95,14 +105,24 @@ fn as_wide(t: T) -> Wide { /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(limb_width_32)] +#[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +)))] 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(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +))] fn split_u64(x: u64) -> [Limb; 1] { [as_limb(x)] } diff --git a/tests/lexical/math.rs b/tests/lexical/math.rs index 79d3ef3ee..ce8ae502d 100644 --- a/tests/lexical/math.rs +++ b/tests/lexical/math.rs @@ -18,12 +18,22 @@ impl Math for Bigint { } } -#[cfg(limb_width_32)] +#[cfg(not(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +)))] pub(crate) fn from_u32(x: &[u32]) -> Vec { x.iter().cloned().collect() } -#[cfg(limb_width_64)] +#[cfg(any( + target_arch = "aarch64", + target_arch = "mips64", + target_arch = "powerpc64", + target_arch = "x86_64" +))] pub(crate) fn from_u32(x: &[u32]) -> Vec { let mut v = Vec::::default(); for xi in x.chunks(2) { From 6525ffa3640a1c6d902aaced7ac73a1ff44c6cc3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Sep 2023 22:35:02 -0700 Subject: [PATCH 119/142] Update actions/checkout@v3 -> v4 --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c984c0f8..dee0bfee1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: os: [ubuntu, windows] timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo test - run: cargo test --features preserve_order --tests -- --skip ui --exact @@ -48,7 +48,7 @@ jobs: os: windows timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} @@ -74,7 +74,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo generate-lockfile -Z minimal-versions - run: cargo check --locked @@ -86,7 +86,7 @@ jobs: MIRIFLAGS: -Zmiri-strict-provenance timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri - run: cargo miri setup - run: cargo miri test @@ -98,7 +98,7 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic @@ -108,7 +108,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - run: cargo doc --features raw_value,unbounded_depth env: @@ -119,7 +119,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - uses: dtolnay/install@cargo-fuzz - run: cargo fuzz check @@ -130,7 +130,7 @@ jobs: if: github.event_name != 'pull_request' timeout-minutes: 45 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: dtolnay/install@cargo-outdated - run: cargo outdated --exit-code 1 - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 From 4cc9ea77abfbf8d7fe934a1726163d3821fb36f2 Mon Sep 17 00:00:00 2001 From: Chance Date: Wed, 6 Sep 2023 15:03:40 -0400 Subject: [PATCH 120/142] adds `as_str` to `Number` if `arbitrary_precision` is enabled --- src/number.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/number.rs b/src/number.rs index 7ff66681d..9254284dc 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,6 +279,16 @@ impl Number { } } + /// Returns the `&str` representation of the `Number`. + /// ``` + /// # use serde_json::Number; + /// + /// assert_eq!(Number::from_f64(256.0).unwrap().as_str(), "256.0"); + /// assert_eq!(Number::from_f64(34.0).unwrap().as_str(), "34.0"); + pub fn as_str(&self) -> &str { + &self.n + } + pub(crate) fn as_f32(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { From 99bc2df40b47931f776083bcdec47f281531fc55 Mon Sep 17 00:00:00 2001 From: Chance Date: Wed, 6 Sep 2023 15:13:42 -0400 Subject: [PATCH 121/142] adds missing cfg attr to as_str --- src/number.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/number.rs b/src/number.rs index 9254284dc..2bd2c8e42 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,6 +279,7 @@ impl Number { } } + #[cfg(feature = "arbitrary_precision")] /// Returns the `&str` representation of the `Number`. /// ``` /// # use serde_json::Number; From 029fda06fad9a4b2da7e5f0b2d0c5151b33e1a7b Mon Sep 17 00:00:00 2001 From: Chance Date: Thu, 7 Sep 2023 13:21:51 -0400 Subject: [PATCH 122/142] improves `Number::as_str` doc example --- src/number.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/number.rs b/src/number.rs index 2bd2c8e42..dfd16f720 100644 --- a/src/number.rs +++ b/src/number.rs @@ -283,9 +283,18 @@ impl Number { /// Returns the `&str` representation of the `Number`. /// ``` /// # use serde_json::Number; - /// - /// assert_eq!(Number::from_f64(256.0).unwrap().as_str(), "256.0"); - /// assert_eq!(Number::from_f64(34.0).unwrap().as_str(), "34.0"); + /// for value in [ + /// "7", + /// "12.34", + /// "34e-56789", + /// "0.0123456789000000012345678900000001234567890000123456789", + /// "343412345678910111213141516171819202122232425262728293034", + /// "-343412345678910111213141516171819202122232425262728293031", + /// ] { + /// println!("{value}"); + /// let number: Number = serde_json::from_str(value).unwrap(); + /// assert_eq!(number.as_str(), value); + /// } pub fn as_str(&self) -> &str { &self.n } From 1786de244fd5ec13c35936e40e917bbc63bb297a Mon Sep 17 00:00:00 2001 From: Chance Date: Thu, 7 Sep 2023 13:22:21 -0400 Subject: [PATCH 123/142] removes `println!` from example --- src/number.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index dfd16f720..f4925a0b4 100644 --- a/src/number.rs +++ b/src/number.rs @@ -291,7 +291,6 @@ impl Number { /// "343412345678910111213141516171819202122232425262728293034", /// "-343412345678910111213141516171819202122232425262728293031", /// ] { - /// println!("{value}"); /// let number: Number = serde_json::from_str(value).unwrap(); /// assert_eq!(number.as_str(), value); /// } From b438004775bdc340b819f03bc73fb731b03533f6 Mon Sep 17 00:00:00 2001 From: Chance Date: Thu, 7 Sep 2023 15:04:28 -0400 Subject: [PATCH 124/142] adds `as_number` to `Value` --- src/value/mod.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/value/mod.rs b/src/value/mod.rs index 79ffe9488..713f995c8 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -495,6 +495,34 @@ impl Value { } } + /// If the `Value` is an 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" }); + /// + /// // The number `1` is an u64. + /// assert_eq!(v["a"].as_number().unwrap(), &Number::from(1u64)); + /// + /// // The number `2.2` is an f64. + /// assert_eq!(v["b"].as_number().unwrap(), &Number::from_f64(2.2f64).unwrap()); + /// + /// // The number `-3` is an i64. + /// assert_eq!(v["c"].as_number().unwrap(), &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 a Number. Returns false otherwise. /// /// ``` From 2cd5d59cd1066b824c7f55fccd31d04ecdaf140e Mon Sep 17 00:00:00 2001 From: Chance Date: Thu, 7 Sep 2023 15:16:22 -0400 Subject: [PATCH 125/142] minor cleanup of documentation for `Value::as_number` --- src/value/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 713f995c8..7729834ef 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -507,7 +507,7 @@ impl Value { /// assert_eq!(v["a"].as_number().unwrap(), &Number::from(1u64)); /// /// // The number `2.2` is an f64. - /// assert_eq!(v["b"].as_number().unwrap(), &Number::from_f64(2.2f64).unwrap()); + /// assert_eq!(v["b"].as_number().unwrap(), &Number::from_f64(2.2).unwrap()); /// /// // The number `-3` is an i64. /// assert_eq!(v["c"].as_number().unwrap(), &Number::from(-3i64)); From cf433e9efd9fbb04ba7e35c51e56bee50b916770 Mon Sep 17 00:00:00 2001 From: Chance Date: Thu, 7 Sep 2023 15:19:09 -0400 Subject: [PATCH 126/142] removes `unwrap` from assertions in `Value::as_number` --- src/value/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 7729834ef..7924d969f 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -504,13 +504,13 @@ impl Value { /// let v = json!({ "a": 1, "b": 2.2, "c": -3, "d": "4" }); /// /// // The number `1` is an u64. - /// assert_eq!(v["a"].as_number().unwrap(), &Number::from(1u64)); + /// assert_eq!(v["a"].as_number(), Some(&Number::from(1u64))); /// /// // The number `2.2` is an f64. - /// assert_eq!(v["b"].as_number().unwrap(), &Number::from_f64(2.2).unwrap()); + /// assert_eq!(v["b"].as_number(), Some(&Number::from_f64(2.2).unwrap())); /// /// // The number `-3` is an i64. - /// assert_eq!(v["c"].as_number().unwrap(), &Number::from(-3i64)); + /// assert_eq!(v["c"].as_number(), Some(&Number::from(-3i64))); /// /// // The string `"4"` is not a number. /// assert_eq!(v["d"].as_number(), None); From de39b2a1aaac8334ae885da37c591bd022fd339d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:01:48 -0700 Subject: [PATCH 127/142] Delete trailing whitespace from PR 1069 --- src/value/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index d12d4cfbe..d8946506e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -509,7 +509,7 @@ impl Value { /// /// // The string `"4"` is not a number. /// assert_eq!(v["d"].as_number(), None); - /// + /// /// ``` pub fn as_number(&self) -> Option<&Number> { match self { From 6a5fef919092c11a9a4eb827ac51001761795fe0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:02:13 -0700 Subject: [PATCH 128/142] Wrap as_number documentation to 80 columns --- src/value/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index d8946506e..4f1d4878e 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -495,8 +495,8 @@ impl Value { } } - /// If the `Value` is an Number, returns the associated [`Number`]. Returns None - /// otherwise. + /// If the `Value` is an Number, returns the associated [`Number`]. Returns + /// None otherwise. /// /// ``` /// # use serde_json::{json, Number}; @@ -509,7 +509,6 @@ impl Value { /// /// // The string `"4"` is not a number. /// assert_eq!(v["d"].as_number(), None); - /// /// ``` pub fn as_number(&self) -> Option<&Number> { match self { From 5a39516161ea9efd7957e2b6c6c8fa76792026a9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:02:30 -0700 Subject: [PATCH 129/142] Reorder Value::as_number after is_number The other as_* methods all come after the corresponding is_* method. --- src/value/mod.rs | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 4f1d4878e..d109747e4 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -495,6 +495,25 @@ impl Value { } } + /// Returns true if the `Value` is a Number. Returns false otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let v = json!({ "a": 1, "b": "2" }); + /// + /// assert!(v["a"].is_number()); + /// + /// // The string `"2"` is a string, not a number. + /// assert!(!v["b"].is_number()); + /// ``` + pub fn is_number(&self) -> bool { + match *self { + Value::Number(_) => true, + _ => false, + } + } + /// If the `Value` is an Number, returns the associated [`Number`]. Returns /// None otherwise. /// @@ -517,25 +536,6 @@ impl Value { } } - /// Returns true if the `Value` is a Number. Returns false otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 1, "b": "2" }); - /// - /// assert!(v["a"].is_number()); - /// - /// // The string `"2"` is a string, not a number. - /// assert!(!v["b"].is_number()); - /// ``` - pub fn is_number(&self) -> bool { - match *self { - Value::Number(_) => true, - _ => false, - } - } - /// Returns true if the `Value` is an integer between `i64::MIN` and /// `i64::MAX`. /// From 95c5d6c8be64634d9bf408ef9679003e6b6f0220 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:04:49 -0700 Subject: [PATCH 130/142] Fix documentation typo from PR 1069 --- src/value/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index d109747e4..a565b2998 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -514,7 +514,7 @@ impl Value { } } - /// If the `Value` is an Number, returns the associated [`Number`]. Returns + /// If the `Value` is a Number, returns the associated [`Number`]. Returns /// None otherwise. /// /// ``` From 11b603cf07b6298cdec9803ef0b32085e43c335c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:05:48 -0700 Subject: [PATCH 131/142] Resolve rustdoc::redundant_explicit_links lint warning: redundant explicit link target --> src/lib.rs:59:20 | 59 | //! [`from_slice`][from_slice] for parsing from a byte slice &[u8] and | ------------ ^^^^^^^^^^ explicit target is redundant | | | because label contains path that resolves to same destination | note: referenced explicit link target defined here --> src/lib.rs:295:19 | 295 | //! [from_slice]: crate::de::from_slice | ^^^^^^^^^^^^^^^^^^^^^ = note: when a link's destination is not specified, the label is used to resolve intra-doc links = note: `#[warn(rustdoc::redundant_explicit_links)]` on by default help: remove explicit link target | 59 | //! [`from_slice`] for parsing from a byte slice &[u8] and | ~~~~~~~~~~~~~~ warning: redundant explicit link target --> src/lib.rs:60:21 | 60 | //! [`from_reader`][from_reader] for parsing from any `io::Read` like a File or | ------------- ^^^^^^^^^^^ explicit target is redundant | | | because label contains path that resolves to same destination | note: referenced explicit link target defined here --> src/lib.rs:296:20 | 296 | //! [from_reader]: crate::de::from_reader | ^^^^^^^^^^^^^^^^^^^^^^ = note: when a link's destination is not specified, the label is used to resolve intra-doc links help: remove explicit link target | 60 | //! [`from_reader`] for parsing from any `io::Read` like a File or | ~~~~~~~~~~~~~~~ --- src/lib.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 83f48a031..d2e5e0b62 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}; From db75c22990f58c77d380c9dc02caa43e42b5b098 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:07:12 -0700 Subject: [PATCH 132/142] Fix unintended u8 link inferred by intra doc link --- README.md | 2 +- src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d70497924..a3ba288f1 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ 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_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. diff --git a/src/lib.rs b/src/lib.rs index d2e5e0b62..109de45c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,8 +56,8 @@ //! //! 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`] -//! for parsing from a byte slice &[u8] and [`from_reader`] for parsing from any -//! `io::Read` like a File or a TCP stream. +//! 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}; From fc8dd13aa284d255b79504ed4ee15a27aba6228f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:15:00 -0700 Subject: [PATCH 133/142] Touch up PR 1067 --- src/number.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index f4925a0b4..6b3003f42 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,8 +279,8 @@ impl Number { } } - #[cfg(feature = "arbitrary_precision")] /// Returns the `&str` representation of the `Number`. + /// /// ``` /// # use serde_json::Number; /// for value in [ @@ -294,6 +294,8 @@ impl Number { /// let number: Number = serde_json::from_str(value).unwrap(); /// assert_eq!(number.as_str(), value); /// } + /// ``` + #[cfg(feature = "arbitrary_precision")] pub fn as_str(&self) -> &str { &self.n } From f16cad635d2aadb2ef6610e765f49eaabc1c9bf1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:15:56 -0700 Subject: [PATCH 134/142] Add cfg banner to documentation of Number::as_str --- src/number.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/number.rs b/src/number.rs index 6b3003f42..9e319e8f5 100644 --- a/src/number.rs +++ b/src/number.rs @@ -296,6 +296,7 @@ impl Number { /// } /// ``` #[cfg(feature = "arbitrary_precision")] + #[cfg_attr(docsrs, doc(cfg(feature = "arbitrary_precision")))] pub fn as_str(&self) -> &str { &self.n } From f346308cda4cd5a7dae0327a757fe41f064a2161 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:19:07 -0700 Subject: [PATCH 135/142] Elaborate on documentation of Number::as_str --- src/number.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index 9e319e8f5..b0231a87b 100644 --- a/src/number.rs +++ b/src/number.rs @@ -279,7 +279,12 @@ impl Number { } } - /// Returns the `&str` representation of the `Number`. + /// 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; From 45f10ec816e3f2765ac08f7ca73752326b0475d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Sep 2023 12:19:41 -0700 Subject: [PATCH 136/142] Release 1.0.106 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 335ad3baa..9f046b559 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.105" # remember to update html_root_url +version = "1.0.106" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 109de45c3..7dc197ea0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -299,7 +299,7 @@ //! [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.105")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.106")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 89a274195680d3ea6a2b442ff633b81ccf60bbe4 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:19:34 +0200 Subject: [PATCH 137/142] Revert "Remove limb_width32 and limb_width64 features" This reverts commit c754f0344b61bf20fe76c205fd8d7a3d46138a96. --- build.rs | 36 ++++++++++++++++ src/lexical/math.rs | 86 +++++++++++-------------------------- src/lexical/mod.rs | 9 +++- src/lexical/small_powers.rs | 3 ++ tests/lexical/math.rs | 14 +----- 5 files changed, 73 insertions(+), 75 deletions(-) create mode 100644 build.rs diff --git a/build.rs b/build.rs new file mode 100644 index 000000000..bc2878cf2 --- /dev/null +++ b/build.rs @@ -0,0 +1,36 @@ +use std::env; +use std::process::Command; +use std::str::{self, FromStr}; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + // 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, + }; +} + +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; + } + let next = pieces.next()?; + u32::from_str(next).ok() +} diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 3c0ea0c34..d7122bffa 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -6,6 +6,7 @@ //! buffers, so for a `vec![0, 1, 2, 3]`, `3` is the most significant limb, //! and `0` is the least significant limb. +use super::large_powers; use super::num::*; use super::small_powers::*; use alloc::vec::Vec; @@ -35,58 +36,31 @@ use core::{cmp, iter, mem}; // requiring software emulation. // sparc64 (`UMUL` only supported double-word arguments). -#[doc(hidden)] -pub trait LimbConfig { - type Limb: 'static; - type Wide: 'static; - const POW5_LIMB: &'static [Self::Limb]; - const POW10_LIMB: &'static [Self::Limb]; - const LARGE_POWERS: &'static [&'static [Self::Limb]]; -} - // 32-BIT LIMB -#[doc(hidden)] -pub struct LimbConfig32; - -impl LimbConfig for LimbConfig32 { - type Limb = u32; - type Wide = u64; - const POW5_LIMB: &'static [Self::Limb] = &POW5_32; - const POW10_LIMB: &'static [Self::Limb] = &POW10_32; - const LARGE_POWERS: &'static [&'static [Self::Limb]] = &super::large_powers32::POW5; -} +#[cfg(limb_width_32)] +pub type Limb = u32; + +#[cfg(limb_width_32)] +pub const POW5_LIMB: &[Limb] = &POW5_32; + +#[cfg(limb_width_32)] +pub const POW10_LIMB: &[Limb] = &POW10_32; + +#[cfg(limb_width_32)] +type Wide = u64; // 64-BIT LIMB -#[doc(hidden)] -pub struct LimbConfig64; -impl LimbConfig for LimbConfig64 { - type Limb = u64; - type Wide = u128; - const POW5_LIMB: &'static [Self::Limb] = &POW5_64; - const POW10_LIMB: &'static [Self::Limb] = &POW10_64; - const LARGE_POWERS: &'static [&'static [Self::Limb]] = &super::large_powers64::POW5; -} +#[cfg(limb_width_64)] +pub type Limb = u64; + +#[cfg(limb_width_64)] +pub const POW5_LIMB: &[Limb] = &POW5_64; + +#[cfg(limb_width_64)] +pub const POW10_LIMB: &[Limb] = &POW10_64; -#[cfg(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -))] -type PlatformLimbConfig = LimbConfig64; -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -)))] -type PlatformLimbConfig = LimbConfig32; - -pub type Limb = ::Limb; -type Wide = ::Wide; -pub const POW5_LIMB: &[Limb] = PlatformLimbConfig::POW5_LIMB; -pub const POW10_LIMB: &[Limb] = PlatformLimbConfig::POW10_LIMB; -const LARGE_POWERS: &'static [&'static [Limb]] = PlatformLimbConfig::LARGE_POWERS; +#[cfg(limb_width_64)] +type Wide = u128; /// Cast to limb type. #[inline] @@ -105,24 +79,14 @@ fn as_wide(t: T) -> Wide { /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -)))] +#[cfg(limb_width_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(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -))] +#[cfg(limb_width_64)] fn split_u64(x: u64) -> [Limb; 1] { [as_limb(x)] } @@ -427,7 +391,7 @@ mod small { use super::large::KARATSUBA_CUTOFF; let small_powers = POW5_LIMB; - let large_powers = LARGE_POWERS; + let large_powers = large_powers::POW5; if n == 0 { // No exponent, just return. diff --git a/src/lexical/mod.rs b/src/lexical/mod.rs index 22784144a..b1a45e218 100644 --- a/src/lexical/mod.rs +++ b/src/lexical/mod.rs @@ -20,8 +20,7 @@ mod digit; mod errors; pub(crate) mod exponent; pub(crate) mod float; -mod large_powers32; -mod large_powers64; +mod large_powers; pub(crate) mod math; pub(crate) mod num; pub(crate) mod parse; @@ -29,5 +28,11 @@ pub(crate) mod rounding; mod shift; mod small_powers; +#[cfg(limb_width_32)] +mod large_powers32; + +#[cfg(limb_width_64)] +mod large_powers64; + // API pub use self::parse::{parse_concise_float, parse_truncated_float}; diff --git a/src/lexical/small_powers.rs b/src/lexical/small_powers.rs index ac3f3aad7..219d82611 100644 --- a/src/lexical/small_powers.rs +++ b/src/lexical/small_powers.rs @@ -3,16 +3,19 @@ //! Pre-computed small powers. // 32 BIT +#[cfg(limb_width_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)] pub(crate) const POW10_32: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // 64 BIT +#[cfg(limb_width_64)] pub(crate) const POW5_64: [u64; 28] = [ 1, 5, diff --git a/tests/lexical/math.rs b/tests/lexical/math.rs index ce8ae502d..79d3ef3ee 100644 --- a/tests/lexical/math.rs +++ b/tests/lexical/math.rs @@ -18,22 +18,12 @@ impl Math for Bigint { } } -#[cfg(not(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -)))] +#[cfg(limb_width_32)] pub(crate) fn from_u32(x: &[u32]) -> Vec { x.iter().cloned().collect() } -#[cfg(any( - target_arch = "aarch64", - target_arch = "mips64", - target_arch = "powerpc64", - target_arch = "x86_64" -))] +#[cfg(limb_width_64)] pub(crate) fn from_u32(x: &[u32]) -> Vec { let mut v = Vec::::default(); for xi in x.chunks(2) { From 83bdc5fd4213d94201a3d9ad0f2943da7eba1dd6 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:21:00 +0200 Subject: [PATCH 138/142] Omit return keyword in `remove_entry` Co-authored-by: David Tolnay --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 32a8cd01d..675058ba1 100644 --- a/src/map.rs +++ b/src/map.rs @@ -152,7 +152,7 @@ impl Map { String: Borrow, Q: ?Sized + Ord + Eq + Hash, { - return self.map.remove_entry(key); + self.map.remove_entry(key) } /// Moves all elements from other into self, leaving other empty. From 04f7758b6eae935237574b25a1e63cf5e281e19e Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Mon, 11 Sep 2023 11:23:47 +0200 Subject: [PATCH 139/142] fixup! chore: Remove no_btreemap_get_key_value and no_btreemap_remove_entry. --- build.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/build.rs b/build.rs index bc2878cf2..1a8c89828 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,4 @@ use std::env; -use std::process::Command; -use std::str::{self, FromStr}; fn main() { println!("cargo:rerun-if-changed=build.rs"); @@ -16,21 +14,4 @@ fn main() { println!("cargo:rustc-cfg=limb_width_32"); } } - - let minor = match rustc_minor_version() { - Some(minor) => minor, - None => return, - }; -} - -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; - } - let next = pieces.next()?; - u32::from_str(next).ok() } From fe30766ae5c79bfb670b2de1c5596e6e11e22f8c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Sep 2023 16:33:00 -0600 Subject: [PATCH 140/142] Support deserializing from &RawValue --- src/raw.rs | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 240 insertions(+) diff --git a/src/raw.rs b/src/raw.rs index 651f4797d..879452370 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -527,3 +527,243 @@ impl<'de> MapAccess<'de> for BorrowedRawDeserializer<'de> { seed.deserialize(BorrowedStrDeserializer::new(self.raw_value.take().unwrap())) } } + +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) + } +} From b9d296f87d6081afdd590d5a6006737db961302b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Sep 2023 16:44:25 -0600 Subject: [PATCH 141/142] IntoDeserializer for &RawValue --- src/raw.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/raw.rs b/src/raw.rs index 879452370..a2bf0ecbb 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -528,6 +528,14 @@ impl<'de> MapAccess<'de> for BorrowedRawDeserializer<'de> { } } +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; From b6e113f2036c52e994ca805e530ee4ffae791f71 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 13 Sep 2023 16:53:39 -0600 Subject: [PATCH 142/142] Release 1.0.107 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f046b559..c79f6a79f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.106" # remember to update html_root_url +version = "1.0.107" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 7dc197ea0..347bbaf0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -299,7 +299,7 @@ //! [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.106")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.107")] // Ignored clippy lints #![allow( clippy::collapsible_else_if,