From 5a815a52bb09c44c4e8d0dfa62d33a122bbc1025 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 21 Dec 2020 19:08:28 -0800 Subject: [PATCH 001/508] Ignore redundant_else pedantic clippy lint --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 33612c616..2ab7f9a71 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -339,6 +339,7 @@ // things are often more readable this way clippy::cast_lossless, clippy::module_name_repetitions, + clippy::redundant_else, clippy::shadow_unrelated, clippy::single_match_else, clippy::too_many_lines, From 5df9aeda41d4ab848191d004ab864fdf8ac6488c Mon Sep 17 00:00:00 2001 From: Cyril Plisko Date: Mon, 28 Dec 2020 16:05:06 +0200 Subject: [PATCH 002/508] Add impl From for Value Addresses #736 --- src/value/from.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/value/from.rs b/src/value/from.rs index 4423a02fb..2fe8037bb 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -136,6 +136,22 @@ impl<'a> From> for Value { } } +impl From for Value { + /// Convert `Number` to `Value` + /// + /// # Examples + /// + /// ``` + /// use serde_json::{Number, Value}; + /// + /// let n = Number::from(7); + /// let x: Value = n.into(); + /// ``` + fn from(f: Number) -> Self { + Value::Number(f) + } +} + impl From> for Value { /// Convert map (with string keys) to `Value` /// From 8225a8b917052b06f1a8d4d60388e775cc65b041 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 28 Dec 2020 11:35:08 -0800 Subject: [PATCH 003/508] Format with rustfmt 1.4.30-nightly --- src/value/from.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/value/from.rs b/src/value/from.rs index 2fe8037bb..59e09fd8f 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -243,7 +243,11 @@ impl, V: Into> FromIterator<(K, V)> for Value { /// let x: Value = v.into_iter().collect(); /// ``` fn from_iter>(iter: I) -> Self { - Value::Object(iter.into_iter().map(|(k, v)| (k.into(), v.into())).collect()) + Value::Object( + iter.into_iter() + .map(|(k, v)| (k.into(), v.into())) + .collect(), + ) } } From 613d66e9c5bec9b6eb07e296958553ab1f65522d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 28 Dec 2020 11:34:26 -0800 Subject: [PATCH 004/508] Release 1.0.61 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 587fb206f..8e409121d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.60" # remember to update html_root_url +version = "1.0.61" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 2ab7f9a71..fba86e06c 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://japaric.github.io/serde-json-core/serde_json_core/ -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.60")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.61")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From 347674c3dd42e0df0debd91998bf10c92be6b20d Mon Sep 17 00:00:00 2001 From: Rory McCann Date: Sat, 2 Jan 2021 10:33:05 +0100 Subject: [PATCH 005/508] make a html link in the doccomments --- 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 07f4cc27e..7d92c47a7 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -106,7 +106,7 @@ pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. /// -/// See the `serde_json::value` module documentation for usage examples. +/// See the [`serde_json::value` module documentation](./index.html) for usage examples. #[derive(Clone, Eq, PartialEq)] pub enum Value { /// Represents a JSON null value. From 44d7fe33fcd1adfb952384367840d706b7278d9e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 2 Jan 2021 13:12:08 -0800 Subject: [PATCH 006/508] Intra-doc link for the crate::value link --- 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 7d92c47a7..67f06a8a4 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -106,7 +106,7 @@ pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. /// -/// See the [`serde_json::value` module documentation](./index.html) for usage examples. +/// See the [`serde_json::value` module documentation](self) for usage examples. #[derive(Clone, Eq, PartialEq)] pub enum Value { /// Represents a JSON null value. From 418507e475fc42fb1cd754601ac00081e6fe7cf1 Mon Sep 17 00:00:00 2001 From: Matthias Bussonnier Date: Sun, 10 Jan 2021 09:53:41 -0800 Subject: [PATCH 007/508] Update example to use Result> This seem to be what is now expected on stable; as the compiler warnings are saying; and also consistent with what `read_user_from_stream` example is using. --- src/de.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index 4f406bd7c..4abe7ef1f 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2432,7 +2432,7 @@ where /// location: String, /// } /// -/// fn read_user_from_file>(path: P) -> Result> { +/// fn read_user_from_file>(path: P) -> Result> { /// // Open the file in read-only mode with buffer. /// let file = File::open(path)?; /// let reader = BufReader::new(file); From e0747b9979e4d4f9734028eef24858e1cfb34916 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 5 Feb 2021 21:06:47 +0100 Subject: [PATCH 008/508] use from_utf8_unchecked in the fmt::Display implementation of Value `WriterFormatter` has been moved into it's own little module to enforce using it's `new` method to convey the unsafe requirements. the safety requirements that the serializer only emits valid utf8 is already assumed by the existing `to_string` methods. this speeds up formatting large `Value`s by about 33% in my testing --- src/value/mod.rs | 50 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 67f06a8a4..5c0e38d3a 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -91,7 +91,6 @@ //! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html use crate::error::Error; -use crate::io; use crate::lib::*; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -191,27 +190,43 @@ impl Debug for Value { } } -struct WriterFormatter<'a, 'b: 'a> { - inner: &'a mut fmt::Formatter<'b>, -} +// WriterFormatter is in it's own little module to enforce using the unsafe constructor +mod writer_formatter { + use crate::io; + use crate::lib::*; + + pub struct WriterFormatter<'a, 'b: 'a> { + inner: &'a mut fmt::Formatter<'b>, + } -impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { - fn write(&mut self, buf: &[u8]) -> io::Result { - fn io_error(_: E) -> io::Error { - // Error value does not matter because fmt::Display impl below just - // maps it to fmt::Error - io::Error::new(io::ErrorKind::Other, "fmt error") + impl<'a, 'b: 'a> WriterFormatter<'a, 'b> { + /// Safety: the caller needs to ensure that only valid utf8 bytes are written into this writer + pub unsafe fn new(inner: &'a mut fmt::Formatter<'b>) -> Self { + Self { inner } } - let s = tri!(str::from_utf8(buf).map_err(io_error)); - tri!(self.inner.write_str(s).map_err(io_error)); - Ok(buf.len()) } - fn flush(&mut self) -> io::Result<()> { - Ok(()) + impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { + fn write(&mut self, buf: &[u8]) -> io::Result { + fn io_error(_: E) -> io::Error { + // Error value does not matter because fmt::Display impl below just + // maps it to fmt::Error + io::Error::new(io::ErrorKind::Other, "fmt error") + } + // The safety requirements of passing in valid utf8 is passed trough to the caller trough the unsafe `new` method + let s = unsafe { str::from_utf8_unchecked(buf) }; + tri!(self.inner.write_str(s).map_err(io_error)); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } } } +use writer_formatter::WriterFormatter; + impl fmt::Display for Value { /// Display a JSON value as a string. /// @@ -239,7 +254,10 @@ impl fmt::Display for Value { /// ``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let alternate = f.alternate(); - let mut wr = WriterFormatter { inner: f }; + let mut wr = unsafe { + // The serializer only emits valid json with the default formatters + WriterFormatter::new(f) + }; if alternate { // {:#} super::ser::to_writer_pretty(&mut wr, self).map_err(|_| fmt::Error) From b6c855dc09bb384955652da0216deaf6c0d2d27a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 5 Feb 2021 13:56:27 -0800 Subject: [PATCH 009/508] Touch up PR 751 --- src/value/mod.rs | 67 +++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 41 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 5c0e38d3a..88d2ef6ee 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -91,6 +91,7 @@ //! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html use crate::error::Error; +use crate::io; use crate::lib::*; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -190,43 +191,6 @@ impl Debug for Value { } } -// WriterFormatter is in it's own little module to enforce using the unsafe constructor -mod writer_formatter { - use crate::io; - use crate::lib::*; - - pub struct WriterFormatter<'a, 'b: 'a> { - inner: &'a mut fmt::Formatter<'b>, - } - - impl<'a, 'b: 'a> WriterFormatter<'a, 'b> { - /// Safety: the caller needs to ensure that only valid utf8 bytes are written into this writer - pub unsafe fn new(inner: &'a mut fmt::Formatter<'b>) -> Self { - Self { inner } - } - } - - impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { - fn write(&mut self, buf: &[u8]) -> io::Result { - fn io_error(_: E) -> io::Error { - // Error value does not matter because fmt::Display impl below just - // maps it to fmt::Error - io::Error::new(io::ErrorKind::Other, "fmt error") - } - // The safety requirements of passing in valid utf8 is passed trough to the caller trough the unsafe `new` method - let s = unsafe { str::from_utf8_unchecked(buf) }; - tri!(self.inner.write_str(s).map_err(io_error)); - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } -} - -use writer_formatter::WriterFormatter; - impl fmt::Display for Value { /// Display a JSON value as a string. /// @@ -253,11 +217,32 @@ impl fmt::Display for Value { /// "{\n \"city\": \"London\",\n \"street\": \"10 Downing Street\"\n}"); /// ``` fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + struct WriterFormatter<'a, 'b: 'a> { + inner: &'a mut fmt::Formatter<'b>, + } + + impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { + fn write(&mut self, buf: &[u8]) -> io::Result { + // Safety: the serializer below only emits valid utf8 when using + // the default formatter. + let s = unsafe { str::from_utf8_unchecked(buf) }; + tri!(self.inner.write_str(s).map_err(io_error)); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + fn io_error(_: fmt::Error) -> io::Error { + // Error value does not matter because Display impl just maps it + // back to fmt::Error. + io::Error::new(io::ErrorKind::Other, "fmt error") + } + let alternate = f.alternate(); - let mut wr = unsafe { - // The serializer only emits valid json with the default formatters - WriterFormatter::new(f) - }; + let mut wr = WriterFormatter { inner: f }; if alternate { // {:#} super::ser::to_writer_pretty(&mut wr, self).map_err(|_| fmt::Error) From 8d78020522b4d3001be5f9c613bf4b5a6e63618a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 5 Feb 2021 14:05:08 -0800 Subject: [PATCH 010/508] Release 1.0.62 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e409121d..c837c78e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.61" # remember to update html_root_url +version = "1.0.62" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index fba86e06c..c3766b5fd 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://japaric.github.io/serde-json-core/serde_json_core/ -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.61")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.62")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From cf1118de1c4298e2d0ad7cc397e319d3494eeb9b Mon Sep 17 00:00:00 2001 From: QZQ <928221136@qq.com> Date: Thu, 11 Feb 2021 17:14:03 +0800 Subject: [PATCH 011/508] Fix method document in de.rs --- src/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de.rs b/src/de.rs index 4abe7ef1f..15c8236b6 100644 --- a/src/de.rs +++ b/src/de.rs @@ -41,7 +41,7 @@ where /// Typically it is more convenient to use one of these methods instead: /// /// - Deserializer::from_str - /// - Deserializer::from_bytes + /// - Deserializer::from_slice /// - Deserializer::from_reader pub fn new(read: R) -> Self { Deserializer { @@ -2250,7 +2250,7 @@ where /// Typically it is more convenient to use one of these methods instead: /// /// - Deserializer::from_str(...).into_iter() - /// - Deserializer::from_bytes(...).into_iter() + /// - Deserializer::from_slice(...).into_iter() /// - Deserializer::from_reader(...).into_iter() pub fn new(read: R) -> Self { let offset = read.byte_offset(); From 6e140a91926dc149824ae64c81bf3790538ea722 Mon Sep 17 00:00:00 2001 From: Krouton Date: Thu, 25 Feb 2021 21:04:14 +0900 Subject: [PATCH 012/508] Add Entry::and_modify --- src/map.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/map.rs b/src/map.rs index f09d84059..2bc35107d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -542,6 +542,40 @@ impl<'a> Entry<'a> { Entry::Occupied(entry) => entry.into_mut(), } } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// # use serde_json::json; + /// # + /// let mut map = serde_json::Map::new(); + /// map.entry("serde") + /// .and_modify(|e| { *e = json!("rust") }) + /// .or_insert(json!("cpp")); + /// + /// assert_eq!(map["serde"], "cpp".to_owned()); + /// + /// map.entry("serde") + /// .and_modify(|e| { *e = json!("rust") }) + /// .or_insert(json!("cpp")); + /// + /// assert_eq!(map["serde"], "rust".to_owned()); + /// ``` + pub fn and_modify(self, f: F) -> Self + where + F: FnOnce(&mut Value), + { + match self { + Entry::Occupied(mut entry) => { + f(entry.get_mut()); + Entry::Occupied(entry) + } + Entry::Vacant(entry) => Entry::Vacant(entry), + } + } } impl<'a> VacantEntry<'a> { From d400899cbd6d50817106d313f1da997fa41d4e2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 25 Feb 2021 09:38:33 -0800 Subject: [PATCH 013/508] Release 1.0.63 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c837c78e0..6432f10ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.62" # remember to update html_root_url +version = "1.0.63" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index c3766b5fd..1d19d1bba 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://japaric.github.io/serde-json-core/serde_json_core/ -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.62")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.63")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From 9bcb08fd926c1c727cb1343363d41c25f2e6025d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 27 Feb 2021 20:44:47 -0800 Subject: [PATCH 014/508] Fix unnecessary trailing semicolon lints in test suite warning: unnecessary trailing semicolon --> tests/test.rs:2133:6 | 2133 | }; | ^ help: remove this semicolon | = note: `#[warn(redundant_semicolons)]` on by default warning: unnecessary trailing semicolon --> tests/test.rs:2166:6 | 2166 | }; | ^ help: remove this semicolon --- tests/test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index ac6436dbd..ffe58c979 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2130,7 +2130,7 @@ fn test_borrowed_raw_value() { #[serde(borrow)] b: &'a RawValue, c: i8, - }; + } let wrapper_from_str: Wrapper = serde_json::from_str(r#"{"a": 1, "b": {"foo": 2}, "c": 3}"#).unwrap(); @@ -2163,7 +2163,7 @@ fn test_boxed_raw_value() { a: i8, b: Box, c: i8, - }; + } let wrapper_from_str: Wrapper = serde_json::from_str(r#"{"a": 1, "b": {"foo": 2}, "c": 3}"#).unwrap(); From 2f812d0363034efc0f65ffbd35168d99d458aedb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 27 Feb 2021 20:44:05 -0800 Subject: [PATCH 015/508] Add test of RawValue deserialization from invalid utf-8 --- tests/test.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index ffe58c979..2fe7c560a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2201,6 +2201,25 @@ fn test_boxed_raw_value() { assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); } +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_invalid_utf8() { + use serde_json::value::RawValue; + + let j = &[b'"', b'\xCE', b'\xF8', b'"']; + let value_err = serde_json::from_slice::(j).unwrap_err(); + let raw_value_err = serde_json::from_slice::>(j).unwrap_err(); + + assert_eq!( + value_err.to_string(), + "invalid unicode code point at line 1 column 4", + ); + assert_eq!( + raw_value_err.to_string(), + "invalid unicode code point at line 1 column 4", + ); +} + #[test] fn test_borrow_in_map_key() { #[derive(Deserialize, Debug)] From c64c1d7429d32eaef3a501c730906656a9606f5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 27 Feb 2021 20:40:51 -0800 Subject: [PATCH 016/508] Fix panic deserializing RawValue from invalid utf-8 bytes --- src/read.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/read.rs b/src/read.rs index 522c0e011..574857cd8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -587,7 +587,10 @@ impl<'a> Read<'a> for SliceRead<'a> { V: Visitor<'a>, { let raw = &self.slice[self.raw_buffering_start_index..self.index]; - let raw = str::from_utf8(raw).unwrap(); + let raw = match str::from_utf8(raw) { + Ok(raw) => raw, + Err(_) => return error(self, ErrorCode::InvalidUnicodeCodePoint), + }; visitor.visit_map(BorrowedRawDeserializer { raw_value: Some(raw), }) From 8d3b890ef85b795890fc05e194599cbd8d70e70b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 27 Feb 2021 20:58:24 -0800 Subject: [PATCH 017/508] Fix IoRead's raw buffering on invalid utf-8 as well --- src/read.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/read.rs b/src/read.rs index 574857cd8..d247c3054 100644 --- a/src/read.rs +++ b/src/read.rs @@ -377,7 +377,10 @@ where V: Visitor<'de>, { let raw = self.raw_buffer.take().unwrap(); - let raw = String::from_utf8(raw).unwrap(); + let raw = match String::from_utf8(raw) { + Ok(raw) => raw, + Err(_) => return error(self, ErrorCode::InvalidUnicodeCodePoint), + }; visitor.visit_map(OwnedRawDeserializer { raw_value: Some(raw), }) From d0d80783d31161c869922fad933b3623715506d2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 27 Feb 2021 20:58:47 -0800 Subject: [PATCH 018/508] Release 1.0.64 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6432f10ec..6ecb159f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.63" # remember to update html_root_url +version = "1.0.64" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 1d19d1bba..1d750834a 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://japaric.github.io/serde-json-core/serde_json_core/ -#![doc(html_root_url = "https://docs.rs/serde_json/1.0.63")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.64")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From ff03ed0894d201ceffc90a57a82f6864a4780344 Mon Sep 17 00:00:00 2001 From: Krouton Date: Mon, 1 Mar 2021 15:09:57 +0900 Subject: [PATCH 019/508] use Iterator::try_fold instead of for loop in Value::pointer_functions --- src/value/mod.rs | 51 +++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 37 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 88d2ef6ee..1dafaafe4 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -758,25 +758,16 @@ impl Value { if !pointer.starts_with('/') { return None; } - let tokens = pointer + let mut tokens = pointer .split('/') .skip(1) .map(|x| x.replace("~1", "/").replace("~0", "~")); - let mut target = self; - - for token in tokens { - let target_opt = match *target { - Value::Object(ref map) => map.get(&token), - Value::Array(ref list) => parse_index(&token).and_then(|x| list.get(x)), - _ => return None, - }; - if let Some(t) = target_opt { - target = t; - } else { - return None; - } - } - Some(target) + + tokens.try_fold(self, |target, token| match target { + Value::Object(map) => map.get(&token), + Value::Array(list) => parse_index(&token).and_then(|x| list.get(x)), + _ => None, + }) } /// Looks up a value by a JSON Pointer and returns a mutable reference to @@ -823,30 +814,16 @@ impl Value { if !pointer.starts_with('/') { return None; } - let tokens = pointer + let mut tokens = pointer .split('/') .skip(1) .map(|x| x.replace("~1", "/").replace("~0", "~")); - let mut target = self; - - for token in tokens { - // borrow checker gets confused about `target` being mutably borrowed too many times because of the loop - // this once-per-loop binding makes the scope clearer and circumvents the error - let target_once = target; - let target_opt = match *target_once { - Value::Object(ref mut map) => map.get_mut(&token), - Value::Array(ref mut list) => { - parse_index(&token).and_then(move |x| list.get_mut(x)) - } - _ => return None, - }; - if let Some(t) = target_opt { - target = t; - } else { - return None; - } - } - Some(target) + + tokens.try_fold(self, |target, token| match target { + Value::Object(ref mut map) => map.get_mut(&token), + Value::Array(ref mut list) => parse_index(&token).and_then(move |x| list.get_mut(x)), + _ => None, + }) } /// Takes the value out of the `Value`, leaving a `Null` in its place. From 2cd354a8279719f46720daf185c528919b11630d Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Thu, 8 Apr 2021 15:36:40 +0100 Subject: [PATCH 020/508] Fix breaking OSS-Fuzz coverage build --- fuzz/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 4823b077b..809e7a3d5 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" cargo-fuzz = true [dependencies] -libfuzzer-sys = "0.3" +libfuzzer-sys = "0.4.0" serde_json = { path = ".." } [[bin]] From 7ca63e7d5acac348db55c4345cb199f9d658669f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 8 Apr 2021 19:43:41 -0700 Subject: [PATCH 021/508] Resolve branches_sharing_code clippy lint error: all if blocks contain the same code at the start --> src/ser.rs:319:9 | 319 | / if len == Some(0) { 320 | | tri!(self 321 | | .formatter 322 | | .begin_array(&mut self.writer) 323 | | .map_err(Error::io)); | |_____________________________________^ | note: the lint level is defined here --> src/lib.rs:304:9 | 304 | #![deny(clippy::all, clippy::pedantic)] | ^^^^^^^^^^^ = note: `#[deny(clippy::branches_sharing_code)]` implied by `#[deny(clippy::all)]` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code help: consider moving the start statements out like this | 319 | tri!(self 320 | .formatter 321 | .begin_array(&mut self.writer) 322 | .map_err(Error::io)); 323 | if len == Some(0) { | error: all if blocks contain the same code at the start --> src/ser.rs:388:9 | 388 | / if len == Some(0) { 389 | | tri!(self 390 | | .formatter 391 | | .begin_object(&mut self.writer) 392 | | .map_err(Error::io)); | |_____________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code help: consider moving the start statements out like this | 388 | tri!(self 389 | .formatter 390 | .begin_object(&mut self.writer) 391 | .map_err(Error::io)); 392 | if len == Some(0) { | --- src/ser.rs | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index b9b83cd62..0bf48b6bf 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -316,11 +316,11 @@ where #[inline] fn serialize_seq(self, len: Option) -> Result { + tri!(self + .formatter + .begin_array(&mut self.writer) + .map_err(Error::io)); if len == Some(0) { - tri!(self - .formatter - .begin_array(&mut self.writer) - .map_err(Error::io)); tri!(self .formatter .end_array(&mut self.writer) @@ -330,10 +330,6 @@ where state: State::Empty, }) } else { - tri!(self - .formatter - .begin_array(&mut self.writer) - .map_err(Error::io)); Ok(Compound::Map { ser: self, state: State::First, @@ -385,11 +381,11 @@ where #[inline] fn serialize_map(self, len: Option) -> Result { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(Error::io)); if len == Some(0) { - tri!(self - .formatter - .begin_object(&mut self.writer) - .map_err(Error::io)); tri!(self .formatter .end_object(&mut self.writer) @@ -399,10 +395,6 @@ where state: State::Empty, }) } else { - tri!(self - .formatter - .begin_object(&mut self.writer) - .map_err(Error::io)); Ok(Compound::Map { ser: self, state: State::First, From fdaddf2931bef381a3bf08bbd31235bbca4447e4 Mon Sep 17 00:00:00 2001 From: ratijas Date: Thu, 15 Apr 2021 17:36:51 +0300 Subject: [PATCH 022/508] Update link to serde-json-core --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f2db0de47..ed11e61c2 100644 --- a/README.md +++ b/README.md @@ -350,7 +350,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] } For JSON support in Serde without a memory allocator, please see the [`serde-json-core`] crate. -[`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/ +[`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core [value]: https://docs.serde.rs/serde_json/value/enum.Value.html [from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html From 6c6823a6b3f97e4482b07eca02a991f30b19dfee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 15 Apr 2021 07:46:13 -0700 Subject: [PATCH 023/508] Update rustdoc link to serde-json-core --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 1d750834a..33665ddd5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -298,7 +298,7 @@ //! [to_vec]: https://docs.serde.rs/serde_json/ser/fn.to_vec.html //! [to_writer]: https://docs.serde.rs/serde_json/ser/fn.to_writer.html //! [macro]: https://docs.serde.rs/serde_json/macro.json.html -//! [`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/ +//! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core #![doc(html_root_url = "https://docs.rs/serde_json/1.0.64")] #![deny(clippy::all, clippy::pedantic)] From 892e2a82644e8472209769d70e71b94018de5217 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 13 May 2021 19:15:50 -0700 Subject: [PATCH 024/508] Update ui test suite to nightly-2021-05-14 --- tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 0402397d1..2f96ccf5f 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -4,4 +4,4 @@ error: unexpected end of macro invocation 4 | json!({ "a" }); | ^^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 0cf37fc6b..7d1027a96 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -4,4 +4,4 @@ error: unexpected end of macro invocation 4 | json!({ "a" : }); | ^^^^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) From a56df5c07c74efdff083359040f877113615943d Mon Sep 17 00:00:00 2001 From: Dirk Stolle Date: Sat, 15 May 2021 15:11:39 +0200 Subject: [PATCH 025/508] fix typos --- src/error.rs | 2 +- src/iter.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7dcdd81c9..4219d324f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -234,7 +234,7 @@ pub(crate) enum ErrorCode { /// JSON has non-whitespace trailing characters after the value. TrailingCharacters, - /// Unexpected end of hex excape. + /// Unexpected end of hex escape. UnexpectedEndOfHexEscape, /// Encountered nesting of JSON maps and arrays more than 128 layers deep. diff --git a/src/iter.rs b/src/iter.rs index c14026f73..9792916dc 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -12,7 +12,7 @@ pub struct LineColIterator { /// The column is 0 immediately after a newline character has been read. col: usize, - /// Byte offset of the start of the current line. This is the sum of lenghts + /// Byte offset of the start of the current line. This is the sum of lengths /// of all previous lines. Keeping track of things this way allows efficient /// computation of the current line, column, and byte offset while only /// updating one of the counters in `next()` in the common case. From 9cbdfc8464591af874c3dcee8dd4a5e1213b5026 Mon Sep 17 00:00:00 2001 From: Jayesh Mann <36336898+jayeshmann@users.noreply.github.com> Date: Sun, 16 May 2021 20:26:00 +0530 Subject: [PATCH 026/508] Overlapping button fix Run-in-playground and Copy-to-clipboard were overlapping. --- README.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ed11e61c2..ef3d0bb3f 100644 --- a/README.md +++ b/README.md @@ -81,10 +81,14 @@ A string of JSON data can be parsed into a `serde_json::Value` by the [`from_reader`][from_reader] for parsing from any `io::Read` like a File or a TCP stream. + + + + ```rust use serde_json::{Result, Value}; @@ -139,9 +143,13 @@ in one of the dozens of places it is used in your code. Serde provides a powerful way of mapping JSON data into Rust data structures largely automatically. + + + ```rust use serde::{Deserialize, Serialize}; @@ -206,9 +214,11 @@ derive]* page of the Serde site. Serde JSON provides a [`json!` macro][macro] to build `serde_json::Value` objects with very natural JSON syntax. + ```rust use serde_json::json; @@ -239,9 +249,11 @@ be interpolated directly into the JSON value as you are building it. Serde will check at compile time that the value you are interpolating is able to be represented as JSON. + ```rust let full_name = "John Doe"; @@ -270,9 +282,11 @@ A data structure can be converted to a JSON string by [`serde_json::to_writer`][to_writer] which serializes to any `io::Write` such as a File or a TCP stream. + ```rust use serde::{Deserialize, Serialize}; From e4057c7d1f0501efa76a41a4c6f0d6aa3dca13bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 16 May 2021 09:53:35 -0700 Subject: [PATCH 027/508] Change readme run buttons to tab style --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2d206c66d..96587a0f6 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ a TCP stream. @@ -143,7 +143,7 @@ largely automatically. @@ -212,7 +212,7 @@ objects with very natural JSON syntax. @@ -247,7 +247,7 @@ be represented as JSON. @@ -280,7 +280,7 @@ such as a File or a TCP stream. From df1fb717badea8bda80f7e104d80265da0686166 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 4 Jun 2021 20:52:01 -0700 Subject: [PATCH 028/508] Resolve semicolon_if_nothing_returned clippy lints error: consider adding a `;` to the last statement for consistent formatting --> src/map.rs:55:9 | 55 | self.map.clear() | ^^^^^^^^^^^^^^^^ help: add a `;` here: `self.map.clear();` | note: the lint level is defined here --> src/lib.rs:304:22 | 304 | #![deny(clippy::all, clippy::pedantic)] | ^^^^^^^^^^^^^^^^ = note: `#[deny(clippy::semicolon_if_nothing_returned)]` implied by `#[deny(clippy::pedantic)]` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> src/read.rs:719:9 | 719 | R::discard(self) | ^^^^^^^^^^^^^^^^ help: add a `;` here: `R::discard(self);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> src/read.rs:769:9 | 769 | R::set_failed(self, failed) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `R::set_failed(self, failed);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned --- src/map.rs | 2 +- src/read.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/map.rs b/src/map.rs index b5644461a..c8fb50341 100644 --- a/src/map.rs +++ b/src/map.rs @@ -52,7 +52,7 @@ impl Map { /// Clears the map, removing all values. #[inline] pub fn clear(&mut self) { - self.map.clear() + self.map.clear(); } /// Returns a reference to the value corresponding to the key. diff --git a/src/read.rs b/src/read.rs index d247c3054..5a0e8f612 100644 --- a/src/read.rs +++ b/src/read.rs @@ -716,7 +716,7 @@ where } fn discard(&mut self) { - R::discard(self) + R::discard(self); } fn position(&self) -> Position { @@ -766,7 +766,7 @@ where const should_early_return_if_failed: bool = R::should_early_return_if_failed; fn set_failed(&mut self, failed: &mut bool) { - R::set_failed(self, failed) + R::set_failed(self, failed); } } From ea39063f9c8c7dde4ac43bbd9843287bece59b1a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 30 Jun 2021 10:25:33 -0700 Subject: [PATCH 029/508] Update preserve_order required compiler to 1.38.0 for hashbrown Required by indexmap introducing a dependency on hashbrown 0.11, which does not build with rustc 1.36.0. error[E0658]: use of unstable library feature 'ptr_cast' --> github.com-1ecc6299db9ec823/hashbrown-0.11.2/src/raw/mod.rs:504:57 | 504 | NonNull::new_unchecked(self.table.ctrl.as_ptr().cast()) | ^^^^ error[E0658]: use of unstable library feature 'ptr_cast' --> github.com-1ecc6299db9ec823/hashbrown-0.11.2/src/raw/mod.rs:1275:51 | 1275 | NonNull::new_unchecked(self.ctrl.as_ptr().cast()) | ^^^^ error[E0658]: use of unstable library feature 'ptr_cast' --> github.com-1ecc6299db9ec823/hashbrown-0.11.2/src/raw/sse2.rs:49:40 | 49 | Group(x86::_mm_loadu_si128(ptr.cast())) | ^^^^ error[E0658]: use of unstable library feature 'ptr_cast' --> github.com-1ecc6299db9ec823/hashbrown-0.11.2/src/raw/sse2.rs:59:39 | 59 | Group(x86::_mm_load_si128(ptr.cast())) | ^^^^ error[E0658]: use of unstable library feature 'ptr_cast' --> github.com-1ecc6299db9ec823/hashbrown-0.11.2/src/raw/sse2.rs:69:34 | 69 | x86::_mm_store_si128(ptr.cast(), self.0); | ^^^^ --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d05f283d..4d042de7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.40.0, 1.36.0, 1.31.0] + rust: [beta, stable, 1.45.0, 1.40.0, 1.38.0, 1.31.0] os: [ubuntu] include: - rust: stable @@ -43,6 +43,8 @@ jobs: - run: cargo check - run: cargo check --features preserve_order if: matrix.rust != '1.31.0' + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order + if: matrix.rust != '1.31.0' - run: cargo check --features float_roundtrip - run: cargo check --features arbitrary_precision - run: cargo check --features raw_value @@ -64,7 +66,6 @@ jobs: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@1.36.0 - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc - - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc,preserve_order clippy: name: Clippy From 21523246e529ae02cbecb8ab64e869a46e8ee69a Mon Sep 17 00:00:00 2001 From: Ruifeng Xie Date: Sat, 17 Jul 2021 11:22:29 +0800 Subject: [PATCH 030/508] Make arbitrary_precision preserve the exact string representation --- src/de.rs | 9 +++++---- tests/test.rs | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/de.rs b/src/de.rs index 15c8236b6..1824117a0 100644 --- a/src/de.rs +++ b/src/de.rs @@ -898,7 +898,7 @@ impl<'de, R: Read<'de>> Deserializer { fn scan_number(&mut self, buf: &mut String) -> Result<()> { match tri!(self.peek_or_null()) { b'.' => self.scan_decimal(buf), - b'e' | b'E' => self.scan_exponent(buf), + c @ (b'e' | b'E') => self.scan_exponent(c as char, buf), _ => Ok(()), } } @@ -923,19 +923,20 @@ impl<'de, R: Read<'de>> Deserializer { } match tri!(self.peek_or_null()) { - b'e' | b'E' => self.scan_exponent(buf), + c @ (b'e' | b'E') => self.scan_exponent(c as char, buf), _ => Ok(()), } } #[cfg(feature = "arbitrary_precision")] - fn scan_exponent(&mut self, buf: &mut String) -> Result<()> { + fn scan_exponent(&mut self, e: char, buf: &mut String) -> Result<()> { self.eat_char(); - buf.push('e'); + buf.push(e); match tri!(self.peek_or_null()) { b'+' => { self.eat_char(); + buf.push('+'); } b'-' => { self.eat_char(); diff --git a/tests/test.rs b/tests/test.rs index 2fe7c560a..3d701d827 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1007,8 +1007,14 @@ fn test_parse_number() { #[cfg(feature = "arbitrary_precision")] test_parse_ok(vec![ ("1e999", Number::from_string_unchecked("1e999".to_owned())), + ("1e+999", Number::from_string_unchecked("1e+999".to_owned())), ("-1e999", Number::from_string_unchecked("-1e999".to_owned())), ("1e-999", Number::from_string_unchecked("1e-999".to_owned())), + ("1E999", Number::from_string_unchecked("1E999".to_owned())), + ("1E+999", Number::from_string_unchecked("1E+999".to_owned())), + ("-1E999", Number::from_string_unchecked("-1E999".to_owned())), + ("1E-999", Number::from_string_unchecked("1E-999".to_owned())), + ("1E+000", Number::from_string_unchecked("1E+000".to_owned())), ( "2.3e999", Number::from_string_unchecked("2.3e999".to_owned()), From e2978b6a774c2aef2c5fbf0a38a96e88dff64eff Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Jul 2021 18:51:45 +0200 Subject: [PATCH 031/508] Document unbounded_depth, raw_value feature-gated API via doc_cfg --- Cargo.toml | 1 + src/de.rs | 4 +--- src/lib.rs | 1 + src/raw.rs | 12 ++---------- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6ecb159f4..f2a6d3f44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = ["tests/crate"] [package.metadata.docs.rs] features = ["raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] +rustdoc-args = ["--cfg", "docsrs"] [package.metadata.playground] features = ["raw_value"] diff --git a/src/de.rs b/src/de.rs index 15c8236b6..2041f537c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -163,9 +163,6 @@ impl<'de, R: Read<'de>> Deserializer { /// completed, including, but not limited to, Display and Debug and Drop /// impls. /// - /// *This method is only available if serde_json is built with the - /// `"unbounded_depth"` feature.* - /// /// # Examples /// /// ``` @@ -196,6 +193,7 @@ impl<'de, R: Read<'de>> Deserializer { /// } /// ``` #[cfg(feature = "unbounded_depth")] + #[cfg_attr(docsrs, doc(cfg(feature = "unbounded_depth")))] pub fn disable_recursion_limit(&mut self) { self.disable_recursion_limit = true; } diff --git a/src/lib.rs b/src/lib.rs index 33665ddd5..737ad833e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -357,6 +357,7 @@ #![allow(non_upper_case_globals)] #![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] +#![cfg_attr(docsrs, feature(doc_cfg))] //////////////////////////////////////////////////////////////////////////////// diff --git a/src/raw.rs b/src/raw.rs index c373b4ded..114de1ba6 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -18,16 +18,6 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// When serializing, a value of this type will retain its original formatting /// and will not be minified or pretty-printed. /// -/// # Note -/// -/// `RawValue` is only available if serde\_json is built with the `"raw_value"` -/// feature. -/// -/// ```toml -/// [dependencies] -/// serde_json = { version = "1.0", features = ["raw_value"] } -/// ``` -/// /// # Example /// /// ``` @@ -109,6 +99,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// } /// ``` #[repr(C)] +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub struct RawValue { json: str, } @@ -267,6 +258,7 @@ impl RawValue { /// /// println!("{}", serde_json::value::to_raw_value(&map).unwrap_err()); /// ``` +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub fn to_raw_value(value: &T) -> Result, Error> where T: Serialize, From d8f70a386167c3d1f3150b66de58a82f874d0a2b Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Jul 2021 18:55:50 +0200 Subject: [PATCH 032/508] Add CI job to ensure documentation can be built --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d042de7a..f443d428e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,16 @@ jobs: - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy + docs: + name: Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo doc --features raw_value,unbounded_depth + env: + RUSTDOCFLAGS: --cfg docsrs + fuzz: name: Fuzz runs-on: ubuntu-latest From 6f15a0e1b2a0ed23ffedf44c4d6a3b52f2727d8e Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Tue, 27 Jul 2021 19:09:38 +0200 Subject: [PATCH 033/508] Add doc(cfg(feature = "std")) attribute where appropriate --- src/de.rs | 1 + src/lib.rs | 1 + src/read.rs | 1 + src/ser.rs | 3 +++ 4 files changed, 6 insertions(+) diff --git a/src/de.rs b/src/de.rs index 2041f537c..c2245724b 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2492,6 +2492,7 @@ where /// the JSON map or some number is too big to fit in the expected primitive /// type. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn from_reader(rdr: R) -> Result where R: crate::io::Read, diff --git a/src/lib.rs b/src/lib.rs index 737ad833e..b705f1c74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -454,6 +454,7 @@ pub mod de; pub mod error; pub mod map; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub mod ser; #[cfg(not(feature = "std"))] mod ser; diff --git a/src/read.rs b/src/read.rs index 5a0e8f612..8ed3d7d83 100644 --- a/src/read.rs +++ b/src/read.rs @@ -140,6 +140,7 @@ where /// JSON input source that reads from a std::io input stream. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct IoRead where R: io::Read, diff --git a/src/ser.rs b/src/ser.rs index 0bf48b6bf..54b0bf220 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -8,6 +8,7 @@ use serde::ser::{self, Impossible, Serialize}; use serde::serde_if_integer128; /// A structure for serializing Rust values into JSON. +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct Serializer { writer: W, formatter: F, @@ -2141,6 +2142,7 @@ static ESCAPE: [u8; 256] = [ /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. #[inline] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_writer(writer: W, value: &T) -> Result<()> where W: io::Write, @@ -2159,6 +2161,7 @@ where /// Serialization can fail if `T`'s implementation of `Serialize` decides to /// fail, or if `T` contains a map with non-string keys. #[inline] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn to_writer_pretty(writer: W, value: &T) -> Result<()> where W: io::Write, From c9193d4ed91176898310b9eb0a6878621f1f7000 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 28 Jul 2021 14:17:56 -0700 Subject: [PATCH 034/508] Release 1.0.65 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2a6d3f44..dd335588e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.64" # remember to update html_root_url +version = "1.0.65" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index b705f1c74..c02f875ee 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.64")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.65")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From 53e6ce4a677e34be3e60a277721c7e49a358fb3f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 29 Jul 2021 14:32:50 -0700 Subject: [PATCH 035/508] Restore compatiblity with older rustc after PR 786 --- src/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de.rs b/src/de.rs index c03491c62..7cc9f9b8e 100644 --- a/src/de.rs +++ b/src/de.rs @@ -899,7 +899,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), - c @ (b'e' | b'E') => self.scan_exponent(c as char, buf), + e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), _ => Ok(()), } } @@ -924,7 +924,7 @@ impl<'de, R: Read<'de>> Deserializer { } match tri!(self.peek_or_null()) { - c @ (b'e' | b'E') => self.scan_exponent(c as char, buf), + e @ b'e' | e @ b'E' => self.scan_exponent(e as char, buf), _ => Ok(()), } } From 6346bb30037f31ea69b9a4eb029875e23521681c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 29 Jul 2021 14:39:16 -0700 Subject: [PATCH 036/508] Release 1.0.66 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dd335588e..a664e7565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.65" # remember to update html_root_url +version = "1.0.66" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index c02f875ee..a7b781bdd 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.65")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.66")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From acfd0a7419e058325a64b97b77cf1a63b6cd8c00 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 10:27:30 -0700 Subject: [PATCH 037/508] Delete Deserializer impl from value::de::MapDeserializer --- src/value/de.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index a66d69239..da216831b 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -534,9 +534,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { V: Visitor<'de>, { match self.value { - Some(Value::Object(v)) => { - serde::Deserializer::deserialize_any(MapDeserializer::new(v), visitor) - } + Some(Value::Object(v)) => visitor.visit_map(MapDeserializer::new(v)), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -665,24 +663,6 @@ impl<'de> MapAccess<'de> for MapDeserializer { } } -impl<'de> serde::Deserializer<'de> for MapDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - macro_rules! deserialize_value_ref_number { ($method:ident) => { #[cfg(not(feature = "arbitrary_precision"))] From fcd94f0a90582e1684d2b1a8f92bf52bffbeeccd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 10:37:47 -0700 Subject: [PATCH 038/508] Delete Deserializer impl from value::de::SeqDeserializer --- src/value/de.rs | 49 ++++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index da216831b..94fdecd9b 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -512,7 +512,22 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { { match self.value { Some(Value::Array(v)) => { - serde::Deserializer::deserialize_any(SeqDeserializer::new(v), visitor) + let len = v.len(); + if len == 0 { + visitor.visit_unit() + } else { + let mut seq = SeqDeserializer::new(v); + let ret = tri!(visitor.visit_seq(&mut seq)); + let remaining = seq.iter.len(); + if remaining == 0 { + Ok(ret) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in array", + )) + } + } } Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), @@ -559,38 +574,6 @@ impl SeqDeserializer { } } -impl<'de> serde::Deserializer<'de> for SeqDeserializer { - type Error = Error; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = tri!(visitor.visit_seq(&mut self)); - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - impl<'de> SeqAccess<'de> for SeqDeserializer { type Error = Error; From cb1e7301d62fca70e406cc2afdef14cf426d5011 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 10:39:55 -0700 Subject: [PATCH 039/508] Delete Deserializer impl from value::de::SeqRefDeserializer --- src/value/de.rs | 49 ++++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index 94fdecd9b..675036645 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1006,7 +1006,22 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { { match self.value { Some(&Value::Array(ref v)) => { - serde::Deserializer::deserialize_any(SeqRefDeserializer::new(v), visitor) + let len = v.len(); + if len == 0 { + visitor.visit_unit() + } else { + let mut seq = SeqRefDeserializer::new(v); + let ret = tri!(visitor.visit_seq(&mut seq)); + let remaining = seq.iter.len(); + if remaining == 0 { + Ok(ret) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in array", + )) + } + } } Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), @@ -1053,38 +1068,6 @@ impl<'de> SeqRefDeserializer<'de> { } } -impl<'de> serde::Deserializer<'de> for SeqRefDeserializer<'de> { - type Error = Error; - - #[inline] - fn deserialize_any(mut self, visitor: V) -> Result - where - V: Visitor<'de>, - { - let len = self.iter.len(); - if len == 0 { - visitor.visit_unit() - } else { - let ret = tri!(visitor.visit_seq(&mut self)); - let remaining = self.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } - } - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - impl<'de> SeqAccess<'de> for SeqRefDeserializer<'de> { type Error = Error; From cdc2ad37819bbf7b4a34815b82e3d496155b115e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 10:40:49 -0700 Subject: [PATCH 040/508] Delete Deserializer impl from value::de::MapRefDeserializer --- src/value/de.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index 675036645..f5f87bcbb 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1043,9 +1043,7 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Object(ref v)) => { - serde::Deserializer::deserialize_any(MapRefDeserializer::new(v), visitor) - } + Some(&Value::Object(ref v)) => visitor.visit_map(MapRefDeserializer::new(v)), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -1140,24 +1138,6 @@ impl<'de> MapAccess<'de> for MapRefDeserializer<'de> { } } -impl<'de> serde::Deserializer<'de> for MapRefDeserializer<'de> { - type Error = Error; - - #[inline] - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_map(self) - } - - forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string - bytes byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } -} - struct MapKeyDeserializer<'de> { key: Cow<'de, str>, } From 89cfb456ffc0749e2e30321e2196b1c8c29e1e00 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 11:03:27 -0700 Subject: [PATCH 041/508] Use existing helpers for running visit_seq over a Vec/slice --- src/value/de.rs | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index f5f87bcbb..5ff011961 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -512,21 +512,10 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { { match self.value { Some(Value::Array(v)) => { - let len = v.len(); - if len == 0 { + if v.is_empty() { visitor.visit_unit() } else { - let mut seq = SeqDeserializer::new(v); - let ret = tri!(visitor.visit_seq(&mut seq)); - let remaining = seq.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } + visit_array(v, visitor) } } Some(other) => Err(serde::de::Error::invalid_type( @@ -1006,21 +995,10 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { { match self.value { Some(&Value::Array(ref v)) => { - let len = v.len(); - if len == 0 { + if v.is_empty() { visitor.visit_unit() } else { - let mut seq = SeqRefDeserializer::new(v); - let ret = tri!(visitor.visit_seq(&mut seq)); - let remaining = seq.iter.len(); - if remaining == 0 { - Ok(ret) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in array", - )) - } + visit_array_ref(v, visitor) } } Some(other) => Err(serde::de::Error::invalid_type( From 12207ed2a3dff29e695222a27ebb22767c6c44e1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 11:21:40 -0700 Subject: [PATCH 042/508] Add regression test for issue 795 --- tests/regression/issue795.rs | 57 ++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/regression/issue795.rs diff --git a/tests/regression/issue795.rs b/tests/regression/issue795.rs new file mode 100644 index 000000000..43198a33d --- /dev/null +++ b/tests/regression/issue795.rs @@ -0,0 +1,57 @@ +use serde::de::{ + Deserialize, Deserializer, EnumAccess, IgnoredAny, MapAccess, VariantAccess, Visitor, +}; +use serde_json::json; +use std::fmt; + +#[derive(Debug)] +enum Enum { + Variant { x: u8 }, +} + +impl<'de> Deserialize<'de> for Enum { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EnumVisitor; + + impl<'de> Visitor<'de> for EnumVisitor { + type Value = Enum; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("enum Enum") + } + + fn visit_enum(self, data: A) -> Result + where + A: EnumAccess<'de>, + { + let (IgnoredAny, variant) = data.variant()?; + variant.struct_variant(&["x"], self) + } + + fn visit_map(self, mut data: A) -> Result + where + A: MapAccess<'de>, + { + let mut x = 0; + if let Some((IgnoredAny, value)) = data.next_entry()? { + x = value; + } + Ok(Enum::Variant { x }) + } + } + + deserializer.deserialize_enum("Enum", &["Variant"], EnumVisitor) + } +} + +#[test] +fn test() { + let s = r#" {"Variant":{"x":0,"y":0}} "#; + assert!(serde_json::from_str::(s).is_err()); + + let j = json!({"Variant":{"x":0,"y":0}}); + assert!(serde_json::from_value::(j).is_err()); +} From c7b71869571ad1825a5983df84d05760769ec71f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 11:21:54 -0700 Subject: [PATCH 043/508] Detect unknown struct variant fields deserializing from Value --- 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 5ff011961..24ca82696 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -538,7 +538,7 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { V: Visitor<'de>, { match self.value { - Some(Value::Object(v)) => visitor.visit_map(MapDeserializer::new(v)), + Some(Value::Object(v)) => visit_object(v, visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -1021,7 +1021,7 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { V: Visitor<'de>, { match self.value { - Some(&Value::Object(ref v)) => visitor.visit_map(MapRefDeserializer::new(v)), + Some(&Value::Object(ref v)) => visit_object_ref(v, visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", From f6bbab4835275565ece6d492683b23657f53df8b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 28 Aug 2021 11:36:59 -0700 Subject: [PATCH 044/508] Release 1.0.67 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a664e7565..9de7be1cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.66" # remember to update html_root_url +version = "1.0.67" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index a7b781bdd..db10cad87 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.66")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.67")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From 133bb61574c5d1aa2f60dbf967ebc05e0363b0b1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 9 Sep 2021 19:26:06 -0700 Subject: [PATCH 045/508] Ignore derivable_impls clippy false positive https://github.com/rust-lang/rust-clippy/issues/7655 error: this `impl` can be derived --> src/map.rs:254:1 | 254 | / impl Default for Map { 255 | | #[inline] 256 | | fn default() -> Self { 257 | | Map { ... | 260 | | } 261 | | } | |_^ | = note: `#[deny(clippy::derivable_impls)]` implied by `#[deny(clippy::all)]` = help: try annotating `map::Map` with `#[derive(Default)]` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls --- src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map.rs b/src/map.rs index c8fb50341..716f12852 100644 --- a/src/map.rs +++ b/src/map.rs @@ -251,6 +251,7 @@ impl Map { } } +#[allow(clippy::derivable_impls)] // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7655 impl Default for Map { #[inline] fn default() -> Self { From f4fc150c45d915426f05739a6b77859983c4f412 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 12:49:17 -0700 Subject: [PATCH 046/508] Fix dead code warning in issue 795 regression test warning: field is never read: `x` --> tests/regression/issue795.rs:9:15 | 9 | Variant { x: u8 }, | ^^^^^ | = note: `#[warn(dead_code)]` on by default --- tests/regression/issue795.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/issue795.rs b/tests/regression/issue795.rs index 43198a33d..06b68724e 100644 --- a/tests/regression/issue795.rs +++ b/tests/regression/issue795.rs @@ -5,7 +5,7 @@ use serde_json::json; use std::fmt; #[derive(Debug)] -enum Enum { +pub enum Enum { Variant { x: u8 }, } From ef823af7d5d6c67b06988c50d9a2dfcba9c9e247 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 12:49:58 -0700 Subject: [PATCH 047/508] Suppress unused field in test_borrow_in_map_key warning: field is never read: `map` --> tests/test.rs:2233:9 | 2233 | map: BTreeMap, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index 3d701d827..2df6d42b4 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2230,6 +2230,7 @@ fn test_raw_invalid_utf8() { fn test_borrow_in_map_key() { #[derive(Deserialize, Debug)] struct Outer { + #[allow(dead_code)] map: BTreeMap, } From b419f2e0650ab959a3db0b0ca471269e81abf27f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 12:51:27 -0700 Subject: [PATCH 048/508] Test negative zero parsing to f32 --- tests/test.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 2df6d42b4..9fdf26cc7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -811,10 +811,15 @@ fn test_parse_negative_zero() { "-1e-400", "-1e-4000000000000000000000000000000000000000000000000", ] { + assert!( + from_str::(negative_zero).unwrap().is_sign_negative(), + "should have been negative: {:?}", + negative_zero, + ); assert!( from_str::(negative_zero).unwrap().is_sign_negative(), "should have been negative: {:?}", - negative_zero + negative_zero, ); } } From f5219be867e11c81f463c769e01caa2d79702926 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 12:52:55 -0700 Subject: [PATCH 049/508] Add test parsing `-0` --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index 9fdf26cc7..d6a0e81c1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -805,6 +805,7 @@ fn test_parse_u64() { #[test] fn test_parse_negative_zero() { for negative_zero in &[ + "-0", "-0.0", "-0e2", "-0.0e2", From 51a4db1631d2746f28fe3b6f2eafda3aff03af4b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 12:58:27 -0700 Subject: [PATCH 050/508] Parse -0 as float -0.0 instead of integer 0 --- src/de.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/de.rs b/src/de.rs index 7cc9f9b8e..a2f34b908 100644 --- a/src/de.rs +++ b/src/de.rs @@ -434,8 +434,8 @@ impl<'de, R: Read<'de>> Deserializer { } else { let neg = (significand as i64).wrapping_neg(); - // Convert into a float if we underflow. - if neg > 0 { + // Convert into a float if we underflow, or on `-0`. + if neg >= 0 { ParserNumber::F64(-(significand as f64)) } else { ParserNumber::I64(neg) From 9420d3a0a4ebf17ef1deeecee07851f58ff6148f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 14 Sep 2021 13:12:44 -0700 Subject: [PATCH 051/508] Release 1.0.68 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9de7be1cf..5c647a115 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.67" # remember to update html_root_url +version = "1.0.68" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index db10cad87..319ce7165 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.67")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.68")] #![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( From 5bae82d2d2003c4a0f4f792132510f510713f5ce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 25 Sep 2021 21:15:19 -0700 Subject: [PATCH 052/508] Disable broken fuzz build in CI https://github.com/rust-fuzz/cargo-fuzz/issues/276 --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f443d428e..420b2b6ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,3 +93,5 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - run: cargo install cargo-fuzz --debug - run: cargo fuzz build -O + # Currently broken. https://github.com/rust-fuzz/cargo-fuzz/issues/276 + continue-on-error: true From 5a6af19aae543dfab3b0942d2d554d498662afa0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 00:39:06 -0400 Subject: [PATCH 053/508] Skip clippy job on pull requests --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 420b2b6ac..185f1a867 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,6 +70,7 @@ jobs: clippy: name: Clippy runs-on: ubuntu-latest + if: github.event_name != 'pull_request' steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy From cf15614994cc9af7b82cd499d9c04e40eee4fe5d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 00:40:33 -0400 Subject: [PATCH 054/508] Move clippy lint level to CI job --- .github/workflows/ci.yml | 2 +- src/lib.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 185f1a867..3dd1a4021 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,7 +74,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy + - run: cargo clippy -- -Dclippy::all -Dclippy::pedantic docs: name: Documentation diff --git a/src/lib.rs b/src/lib.rs index 319ce7165..942333b54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -301,7 +301,6 @@ //! [`serde-json-core`]: https://github.com/rust-embedded-community/serde-json-core #![doc(html_root_url = "https://docs.rs/serde_json/1.0.68")] -#![deny(clippy::all, clippy::pedantic)] // Ignored clippy lints #![allow( clippy::comparison_chain, From fc4db0306acf62ba480c4ae13a8b37637321200c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 00:41:44 -0400 Subject: [PATCH 055/508] Run clippy on test suite too --- .github/workflows/ci.yml | 2 +- tests/lexical.rs | 19 ++++++++++++++++++- tests/test.rs | 11 ++++++++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3dd1a4021..e718d974c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -74,7 +74,7 @@ jobs: steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - - run: cargo clippy -- -Dclippy::all -Dclippy::pedantic + - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic docs: name: Documentation diff --git a/tests/lexical.rs b/tests/lexical.rs index 11b870abb..29dfc3bb7 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -1,7 +1,24 @@ #![allow( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::cast_possible_wrap, + clippy::cast_precision_loss, + clippy::cast_sign_loss, clippy::comparison_chain, + clippy::doc_markdown, clippy::excessive_precision, - clippy::float_cmp + clippy::float_cmp, + clippy::if_not_else, + clippy::module_name_repetitions, + clippy::needless_borrow, + clippy::semicolon_if_nothing_returned, + clippy::shadow_unrelated, + clippy::similar_names, + clippy::single_match_else, + clippy::too_many_lines, + clippy::unreadable_literal, + clippy::unseparated_literal_suffix, + clippy::wildcard_imports )] #[path = "../src/lexical/mod.rs"] diff --git a/tests/test.rs b/tests/test.rs index d6a0e81c1..2a792d9bf 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,8 +1,17 @@ #![cfg(not(feature = "preserve_order"))] #![allow( + clippy::cast_precision_loss, clippy::excessive_precision, clippy::float_cmp, - clippy::unreadable_literal + clippy::items_after_statements, + clippy::let_underscore_drop, + clippy::manual_str_repeat, + clippy::shadow_unrelated, + clippy::too_many_lines, + clippy::unreadable_literal, + clippy::unseparated_literal_suffix, + clippy::vec_init_then_push, + clippy::zero_sized_map_values )] #![cfg_attr(feature = "trace-macros", feature(trace_macros))] #[cfg(feature = "trace-macros")] From 0b5931550ad06aee9000ac81c559e87de58308a3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:07:24 -0400 Subject: [PATCH 056/508] Resolve needless_borrow clippy lint error: this expression borrows a reference (`&lexical::float::ExtendedFloat`) that is immediately dereferenced by the compiler --> tests/../src/lexical/algorithm.rs:136:45 | 136 | u64::error_is_accurate::(errors, &fp) | ^^^ help: change this to: `fp` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow --- src/lexical/algorithm.rs | 2 +- tests/lexical.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lexical/algorithm.rs b/src/lexical/algorithm.rs index d01e5b950..a2cbf18af 100644 --- a/src/lexical/algorithm.rs +++ b/src/lexical/algorithm.rs @@ -133,7 +133,7 @@ where let shift = fp.normalize(); errors <<= shift; - u64::error_is_accurate::(errors, &fp) + u64::error_is_accurate::(errors, fp) } } diff --git a/tests/lexical.rs b/tests/lexical.rs index 29dfc3bb7..c6aad68bb 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -10,7 +10,6 @@ clippy::float_cmp, clippy::if_not_else, clippy::module_name_repetitions, - clippy::needless_borrow, clippy::semicolon_if_nothing_returned, clippy::shadow_unrelated, clippy::similar_names, From 019041e4d5e87d0691db85f54ff850e59947bc91 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:08:59 -0400 Subject: [PATCH 057/508] Resolve semicolon_if_nothing_returned pedantic clippy lint error: consider adding a `;` to the last statement for consistent formatting --> tests/../src/lexical/float.rs:111:9 | 111 | round_to_native::(self, algorithm) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `round_to_native::(self, algorithm);` | = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D clippy::pedantic` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> tests/../src/lexical/math.rs:616:9 | 616 | iadd_impl(x, y, 0) | ^^^^^^^^^^^^^^^^^^ help: add a `;` here: `iadd_impl(x, y, 0);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> tests/../src/lexical/math.rs:862:9 | 862 | self.ishl(n as usize) | ^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.ishl(n as usize);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> tests/../src/lexical/math.rs:868:9 | 868 | small::imul_pow5(self.data_mut(), n) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `small::imul_pow5(self.data_mut(), n);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> tests/lexical/float.rs:184:5 | 184 | check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned --- src/lexical/float.rs | 2 +- src/lexical/math.rs | 6 +++--- tests/lexical.rs | 1 - tests/lexical/float.rs | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/lexical/float.rs b/src/lexical/float.rs index e320ead16..2d434a29c 100644 --- a/src/lexical/float.rs +++ b/src/lexical/float.rs @@ -108,7 +108,7 @@ impl ExtendedFloat { F: Float, Algorithm: FnOnce(&mut ExtendedFloat, i32), { - round_to_native::(self, algorithm) + round_to_native::(self, algorithm); } // FROM diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 4890c0fa3..45fb00d15 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -613,7 +613,7 @@ mod large { /// AddAssign bigint to bigint. #[inline] pub fn iadd(x: &mut Vec, y: &[Limb]) { - iadd_impl(x, y, 0) + iadd_impl(x, y, 0); } /// Add bigint to bigint. @@ -859,13 +859,13 @@ pub(crate) trait Math: Clone + Sized + Default { /// Multiply by a power of 2. #[inline] fn imul_pow2(&mut self, n: u32) { - self.ishl(n as usize) + self.ishl(n as usize); } /// Multiply by a power of 5. #[inline] fn imul_pow5(&mut self, n: u32) { - small::imul_pow5(self.data_mut(), n) + small::imul_pow5(self.data_mut(), n); } /// MulAssign by a power of 10. diff --git a/tests/lexical.rs b/tests/lexical.rs index c6aad68bb..be730420f 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -10,7 +10,6 @@ clippy::float_cmp, clippy::if_not_else, clippy::module_name_repetitions, - clippy::semicolon_if_nothing_returned, clippy::shadow_unrelated, clippy::similar_names, clippy::single_match_else, diff --git a/tests/lexical/float.rs b/tests/lexical/float.rs index 554c4c49e..c87e7e1ed 100644 --- a/tests/lexical/float.rs +++ b/tests/lexical/float.rs @@ -181,7 +181,7 @@ fn round_to_f64_test() { // Bug fixes // 1.2345e-308 - check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074) + check_round_to_f64(10234494226754558294, -1086, 2498655817078750, -1074); } fn assert_normalized_eq(mut x: ExtendedFloat, mut y: ExtendedFloat) { From 9298f54aced47b536aee356a069c3b388317d882 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:11:06 -0400 Subject: [PATCH 058/508] Resolve manual_str_repeat clippy lint --- tests/test.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 2a792d9bf..c8df23dba 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -5,7 +5,6 @@ clippy::float_cmp, clippy::items_after_statements, clippy::let_underscore_drop, - clippy::manual_str_repeat, clippy::shadow_unrelated, clippy::too_many_lines, clippy::unreadable_literal, @@ -1827,7 +1826,7 @@ fn test_stack_overflow() { .collect(); let _: Value = from_str(&brackets).unwrap(); - let brackets: String = iter::repeat('[').take(129).collect(); + let brackets = "[".repeat(129); test_parse_err::(&[(&brackets, "recursion limit exceeded at line 1 column 128")]); } From 28fc9b4ddf8aa0d2b1ba858ecc67954760ed9a7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:14:18 -0400 Subject: [PATCH 059/508] Run clippy also with features enabled --- .github/workflows/ci.yml | 1 + src/lib.rs | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e718d974c..d18bb53f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,6 +75,7 @@ jobs: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic + - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic docs: name: Documentation diff --git a/src/lib.rs b/src/lib.rs index 942333b54..e590b76b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -316,6 +316,8 @@ clippy::unnecessary_wraps, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 clippy::unnested_or_patterns, + // TODO + clippy::needless_borrow, )] // Ignored clippy_pedantic lints #![allow( @@ -352,6 +354,9 @@ // noisy clippy::missing_errors_doc, clippy::must_use_candidate, + // TODO + clippy::redundant_closure_for_method_calls, + clippy::semicolon_if_nothing_returned, )] #![allow(non_upper_case_globals)] #![deny(missing_docs)] From f5f23f9b7f9f0022e66a80bbd881d6edfc161f2c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:16:00 -0400 Subject: [PATCH 060/508] Resolve needless_borrow clippy lint error: this expression borrows a reference (`&mut ser::Serializer`) that is immediately dereferenced by the compiler --> src/ser.rs:759:59 | 759 | tri!(value.serialize(NumberStrEmitter(&mut *ser))); | ^^^^^^^^^ help: change this to: `*ser` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: this expression borrows a reference (`&mut ser::Serializer`) that is immediately dereferenced by the compiler --> src/ser.rs:768:61 | 768 | tri!(value.serialize(RawValueStrEmitter(&mut *ser))); | ^^^^^^^^^ help: change this to: `*ser` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow --- src/lib.rs | 2 -- src/ser.rs | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e590b76b9..65072e9c7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -316,8 +316,6 @@ clippy::unnecessary_wraps, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 clippy::unnested_or_patterns, - // TODO - clippy::needless_borrow, )] // Ignored clippy_pedantic lints #![allow( diff --git a/src/ser.rs b/src/ser.rs index 54b0bf220..6637981f5 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -756,7 +756,7 @@ where #[cfg(feature = "arbitrary_precision")] Compound::Number { ref mut ser, .. } => { if key == crate::number::TOKEN { - tri!(value.serialize(NumberStrEmitter(&mut *ser))); + tri!(value.serialize(NumberStrEmitter(ser))); Ok(()) } else { Err(invalid_number()) @@ -765,7 +765,7 @@ where #[cfg(feature = "raw_value")] Compound::RawValue { ref mut ser, .. } => { if key == crate::raw::TOKEN { - tri!(value.serialize(RawValueStrEmitter(&mut *ser))); + tri!(value.serialize(RawValueStrEmitter(ser))); Ok(()) } else { Err(invalid_raw_value()) From 9a6dcf03de5a744f4efefe0863a657226bfb05bd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:17:28 -0400 Subject: [PATCH 061/508] Resolve redundant_closure_for_method_calls pedantic clippy lint error: redundant closure --> src/number.rs:133:69 | 133 | return self.n.parse::().ok().map_or(false, |f| f.is_finite()); | ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `f64::is_finite` | = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D clippy::pedantic` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls --- src/lib.rs | 1 - src/number.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 65072e9c7..89e85ea91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -353,7 +353,6 @@ clippy::missing_errors_doc, clippy::must_use_candidate, // TODO - clippy::redundant_closure_for_method_calls, clippy::semicolon_if_nothing_returned, )] #![allow(non_upper_case_globals)] diff --git a/src/number.rs b/src/number.rs index c1476189b..b6b617f64 100644 --- a/src/number.rs +++ b/src/number.rs @@ -130,7 +130,7 @@ impl Number { { for c in self.n.chars() { if c == '.' || c == 'e' || c == 'E' { - return self.n.parse::().ok().map_or(false, |f| f.is_finite()); + return self.n.parse::().ok().map_or(false, f64::is_finite); } } false From 552a6ee83e42bcee01ade77f38c5022e29c914c1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Oct 2021 01:18:12 -0400 Subject: [PATCH 062/508] Resolve semicolon_if_nothing_returned pedantic clippy lint error: consider adding a `;` to the last statement for consistent formatting --> src/read.rs:680:9 | 680 | self.delegate.begin_raw_buffering() | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.delegate.begin_raw_buffering();` | = note: `-D clippy::semicolon-if-nothing-returned` implied by `-D clippy::pedantic` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned error: consider adding a `;` to the last statement for consistent formatting --> src/read.rs:756:9 | 756 | R::begin_raw_buffering(self) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `R::begin_raw_buffering(self);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned --- src/lib.rs | 2 -- src/read.rs | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 89e85ea91..942333b54 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -352,8 +352,6 @@ // noisy clippy::missing_errors_doc, clippy::must_use_candidate, - // TODO - clippy::semicolon_if_nothing_returned, )] #![allow(non_upper_case_globals)] #![deny(missing_docs)] diff --git a/src/read.rs b/src/read.rs index 8ed3d7d83..85514db03 100644 --- a/src/read.rs +++ b/src/read.rs @@ -677,7 +677,7 @@ impl<'a> Read<'a> for StrRead<'a> { #[cfg(feature = "raw_value")] fn begin_raw_buffering(&mut self) { - self.delegate.begin_raw_buffering() + self.delegate.begin_raw_buffering(); } #[cfg(feature = "raw_value")] @@ -753,7 +753,7 @@ where #[cfg(feature = "raw_value")] fn begin_raw_buffering(&mut self) { - R::begin_raw_buffering(self) + R::begin_raw_buffering(self); } #[cfg(feature = "raw_value")] From f7a25e062aa45b1f38283e0bc89f14e8b99297c8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 2 Oct 2021 02:31:21 -0400 Subject: [PATCH 063/508] Declare minimum Rust version in Cargo metadata --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 5c647a115..6dadf1cce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ categories = ["encoding"] readme = "README.md" include = ["build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2018" +rust-version = "1.31" [dependencies] serde = { version = "1.0.100", default-features = false } From 733b353394c3319670c0204d5000112eb2f22ef2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 7 Oct 2021 00:19:07 -0400 Subject: [PATCH 064/508] Update ui test files --- tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_comma.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- tests/ui/not_found.stderr | 2 +- tests/ui/parse_expr.stderr | 2 +- tests/ui/parse_key.stderr | 2 +- 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 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 2f96ccf5f..eecba9e36 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -1,5 +1,5 @@ error: unexpected end of macro invocation - --> $DIR/missing_colon.rs:4:5 + --> $DIR/tests/ui/missing_colon.rs:4:5 | 4 | json!({ "a" }); | ^^^^^^^^^^^^^^^ missing tokens in macro arguments diff --git a/tests/ui/missing_comma.stderr b/tests/ui/missing_comma.stderr index ab25b740e..4b28fbe4c 100644 --- a/tests/ui/missing_comma.stderr +++ b/tests/ui/missing_comma.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `"2"` - --> $DIR/missing_comma.rs:4:21 + --> $DIR/tests/ui/missing_comma.rs:4:21 | 4 | json!({ "1": "" "2": "" }); | -^^^ no rules expected this token in macro call diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 7d1027a96..66e63b8cf 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -1,5 +1,5 @@ error: unexpected end of macro invocation - --> $DIR/missing_value.rs:4:5 + --> $DIR/tests/ui/missing_value.rs:4:5 | 4 | json!({ "a" : }); | ^^^^^^^^^^^^^^^^^ missing tokens in macro arguments diff --git a/tests/ui/not_found.stderr b/tests/ui/not_found.stderr index b38f1a3ed..642cd450d 100644 --- a/tests/ui/not_found.stderr +++ b/tests/ui/not_found.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `x` in this scope - --> $DIR/not_found.rs:4:19 + --> $DIR/tests/ui/not_found.rs:4:19 | 4 | json!({ "a" : x }); | ^ not found in this scope diff --git a/tests/ui/parse_expr.stderr b/tests/ui/parse_expr.stderr index d3240df35..47c5d849b 100644 --- a/tests/ui/parse_expr.stderr +++ b/tests/ui/parse_expr.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `~` - --> $DIR/parse_expr.rs:4:19 + --> $DIR/tests/ui/parse_expr.rs:4:19 | 4 | json!({ "a" : ~ }); | ^ no rules expected this token in macro call diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr index ea09836c9..618c8c51b 100644 --- a/tests/ui/parse_key.stderr +++ b/tests/ui/parse_key.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `s` on type `&'static str` - --> $DIR/parse_key.rs:4:16 + --> $DIR/tests/ui/parse_key.rs:4:16 | 4 | json!({ "".s : true }); | ^ diff --git a/tests/ui/unexpected_after_array_element.stderr b/tests/ui/unexpected_after_array_element.stderr index 3708992c9..30cc238f1 100644 --- a/tests/ui/unexpected_after_array_element.stderr +++ b/tests/ui/unexpected_after_array_element.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `=>` - --> $DIR/unexpected_after_array_element.rs:4:18 + --> $DIR/tests/ui/unexpected_after_array_element.rs:4:18 | 4 | json!([ true => ]); | ^^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_after_map_entry.stderr b/tests/ui/unexpected_after_map_entry.stderr index 60f9815bd..d71ee121e 100644 --- a/tests/ui/unexpected_after_map_entry.stderr +++ b/tests/ui/unexpected_after_map_entry.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `=>` - --> $DIR/unexpected_after_map_entry.rs:4:23 + --> $DIR/tests/ui/unexpected_after_map_entry.rs:4:23 | 4 | json!({ "k": true => }); | ^^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_colon.stderr b/tests/ui/unexpected_colon.stderr index 2708b0883..6a82e94e1 100644 --- a/tests/ui/unexpected_colon.stderr +++ b/tests/ui/unexpected_colon.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `:` - --> $DIR/unexpected_colon.rs:4:13 + --> $DIR/tests/ui/unexpected_colon.rs:4:13 | 4 | json!({ : true }); | ^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_comma.stderr b/tests/ui/unexpected_comma.stderr index 65e045304..bc1ef7b6d 100644 --- a/tests/ui/unexpected_comma.stderr +++ b/tests/ui/unexpected_comma.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `,` - --> $DIR/unexpected_comma.rs:4:17 + --> $DIR/tests/ui/unexpected_comma.rs:4:17 | 4 | json!({ "a" , "b": true }); | ^ no rules expected this token in macro call From 019b179cf7a426572225fc6e67e577940b98ffd4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Oct 2021 03:00:04 -0400 Subject: [PATCH 065/508] Ui test changes for trybuild 1.0.49 --- Cargo.toml | 2 +- tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_comma.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- tests/ui/not_found.stderr | 2 +- tests/ui/parse_expr.stderr | 2 +- tests/ui/parse_key.stderr | 2 +- 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 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6dadf1cce..0b09caeae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ rustversion = "1.0" serde_bytes = "0.11" serde_derive = "1.0" serde_stacker = "0.1" -trybuild = { version = "1.0.19", features = ["diff"] } +trybuild = { version = "1.0.49", features = ["diff"] } [workspace] members = ["tests/crate"] diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index eecba9e36..e3a1541bd 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -1,5 +1,5 @@ error: unexpected end of macro invocation - --> $DIR/tests/ui/missing_colon.rs:4:5 + --> tests/ui/missing_colon.rs:4:5 | 4 | json!({ "a" }); | ^^^^^^^^^^^^^^^ missing tokens in macro arguments diff --git a/tests/ui/missing_comma.stderr b/tests/ui/missing_comma.stderr index 4b28fbe4c..bd911d035 100644 --- a/tests/ui/missing_comma.stderr +++ b/tests/ui/missing_comma.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `"2"` - --> $DIR/tests/ui/missing_comma.rs:4:21 + --> tests/ui/missing_comma.rs:4:21 | 4 | json!({ "1": "" "2": "" }); | -^^^ no rules expected this token in macro call diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 66e63b8cf..89dba0b1f 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -1,5 +1,5 @@ error: unexpected end of macro invocation - --> $DIR/tests/ui/missing_value.rs:4:5 + --> tests/ui/missing_value.rs:4:5 | 4 | json!({ "a" : }); | ^^^^^^^^^^^^^^^^^ missing tokens in macro arguments diff --git a/tests/ui/not_found.stderr b/tests/ui/not_found.stderr index 642cd450d..6fec18040 100644 --- a/tests/ui/not_found.stderr +++ b/tests/ui/not_found.stderr @@ -1,5 +1,5 @@ error[E0425]: cannot find value `x` in this scope - --> $DIR/tests/ui/not_found.rs:4:19 + --> tests/ui/not_found.rs:4:19 | 4 | json!({ "a" : x }); | ^ not found in this scope diff --git a/tests/ui/parse_expr.stderr b/tests/ui/parse_expr.stderr index 47c5d849b..6959673d9 100644 --- a/tests/ui/parse_expr.stderr +++ b/tests/ui/parse_expr.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `~` - --> $DIR/tests/ui/parse_expr.rs:4:19 + --> tests/ui/parse_expr.rs:4:19 | 4 | json!({ "a" : ~ }); | ^ no rules expected this token in macro call diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr index 618c8c51b..f10c21800 100644 --- a/tests/ui/parse_key.stderr +++ b/tests/ui/parse_key.stderr @@ -1,5 +1,5 @@ error[E0609]: no field `s` on type `&'static str` - --> $DIR/tests/ui/parse_key.rs:4:16 + --> tests/ui/parse_key.rs:4:16 | 4 | json!({ "".s : true }); | ^ diff --git a/tests/ui/unexpected_after_array_element.stderr b/tests/ui/unexpected_after_array_element.stderr index 30cc238f1..f745a212d 100644 --- a/tests/ui/unexpected_after_array_element.stderr +++ b/tests/ui/unexpected_after_array_element.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `=>` - --> $DIR/tests/ui/unexpected_after_array_element.rs:4:18 + --> tests/ui/unexpected_after_array_element.rs:4:18 | 4 | json!([ true => ]); | ^^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_after_map_entry.stderr b/tests/ui/unexpected_after_map_entry.stderr index d71ee121e..a18c9b4cd 100644 --- a/tests/ui/unexpected_after_map_entry.stderr +++ b/tests/ui/unexpected_after_map_entry.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `=>` - --> $DIR/tests/ui/unexpected_after_map_entry.rs:4:23 + --> tests/ui/unexpected_after_map_entry.rs:4:23 | 4 | json!({ "k": true => }); | ^^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_colon.stderr b/tests/ui/unexpected_colon.stderr index 6a82e94e1..ed038f608 100644 --- a/tests/ui/unexpected_colon.stderr +++ b/tests/ui/unexpected_colon.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `:` - --> $DIR/tests/ui/unexpected_colon.rs:4:13 + --> tests/ui/unexpected_colon.rs:4:13 | 4 | json!({ : true }); | ^ no rules expected this token in macro call diff --git a/tests/ui/unexpected_comma.stderr b/tests/ui/unexpected_comma.stderr index bc1ef7b6d..a4309c4e5 100644 --- a/tests/ui/unexpected_comma.stderr +++ b/tests/ui/unexpected_comma.stderr @@ -1,5 +1,5 @@ error: no rules expected the token `,` - --> $DIR/tests/ui/unexpected_comma.rs:4:17 + --> tests/ui/unexpected_comma.rs:4:17 | 4 | json!({ "a" , "b": true }); | ^ no rules expected this token in macro call From 2e1c46c89501cbe9edc41d78469ca690bf79ff65 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 16 Oct 2021 19:20:52 -0700 Subject: [PATCH 066/508] Update ui test suite to nightly-2021-10-17 --- tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index e3a1541bd..9b83c1777 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -2,6 +2,6 @@ error: unexpected end of macro invocation --> tests/ui/missing_colon.rs:4:5 | 4 | json!({ "a" }); - | ^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^ missing tokens in macro arguments | = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 89dba0b1f..d538d9613 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -2,6 +2,6 @@ error: unexpected end of macro invocation --> tests/ui/missing_value.rs:4:5 | 4 | json!({ "a" : }); - | ^^^^^^^^^^^^^^^^^ missing tokens in macro arguments + | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) From 40ace5add9df2031819f9dbf52ee258f347d02c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 13:19:37 +0200 Subject: [PATCH 067/508] Impl`Eq+Hash` for `Number` using `ordered_float`. --- Cargo.toml | 1 + src/number.rs | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b09caeae..057a2297c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ serde = { version = "1.0.100", default-features = false } indexmap = { version = "1.5", optional = true } itoa = { version = "0.4.3", default-features = false } ryu = "1.0" +ordered-float = "2.8" [dev-dependencies] automod = "1.0" diff --git a/src/number.rs b/src/number.rs index b6b617f64..80dc528bc 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,6 +1,7 @@ use crate::de::ParserNumber; use crate::error::Error; use crate::lib::*; +use ordered_float::NotNan; use serde::de::{self, Unexpected, Visitor}; use serde::{ forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, @@ -16,25 +17,21 @@ use serde::de::{IntoDeserializer, MapAccess}; pub(crate) const TOKEN: &str = "$serde_json::private::Number"; /// Represents a JSON number, whether integer or floating point. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, PartialEq, Eq, Hash)] pub struct Number { n: N, } #[cfg(not(feature = "arbitrary_precision"))] -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] enum N { PosInt(u64), /// Always less than zero. NegInt(i64), /// Always finite. - Float(f64), + Float(NotNan), } -// Implementing Eq is fine since any float values are always finite. -#[cfg(not(feature = "arbitrary_precision"))] -impl Eq for N {} - #[cfg(feature = "arbitrary_precision")] type N = String; @@ -208,7 +205,7 @@ impl Number { match self.n { N::PosInt(n) => Some(n as f64), N::NegInt(n) => Some(n as f64), - N::Float(n) => Some(n), + N::Float(n) => Some(n.into_inner()), } #[cfg(feature = "arbitrary_precision")] self.n.parse::().ok().filter(|float| float.is_finite()) @@ -232,7 +229,11 @@ impl Number { let n = { #[cfg(not(feature = "arbitrary_precision"))] { - N::Float(f) + N::Float(unsafe { + // This is safe because `f.is_finite()` ensures + // that `f` is not `NaN`. + NotNan::new_unchecked(f) + }) } #[cfg(feature = "arbitrary_precision")] { @@ -307,7 +308,7 @@ impl Serialize for Number { match self.n { N::PosInt(u) => serializer.serialize_u64(u), N::NegInt(i) => serializer.serialize_i64(i), - N::Float(f) => serializer.serialize_f64(f), + N::Float(f) => serializer.serialize_f64(f.into_inner()), } } @@ -461,7 +462,7 @@ macro_rules! deserialize_any { match self.n { N::PosInt(u) => visitor.visit_u64(u), N::NegInt(i) => visitor.visit_i64(i), - N::Float(f) => visitor.visit_f64(f), + N::Float(f) => visitor.visit_f64(f.into_inner()), } } @@ -625,7 +626,11 @@ impl From for Number { ParserNumber::F64(f) => { #[cfg(not(feature = "arbitrary_precision"))] { - N::Float(f) + N::Float(unsafe { + // This is safe because `ParserNumber` ensures + // that `f` is not `NaN`. + NotNan::new_unchecked(f) + }) } #[cfg(feature = "arbitrary_precision")] { @@ -736,7 +741,7 @@ impl Number { match self.n { N::PosInt(u) => Unexpected::Unsigned(u), N::NegInt(i) => Unexpected::Signed(i), - N::Float(f) => Unexpected::Float(f), + N::Float(f) => Unexpected::Float(f.into_inner()), } } From be2f5738bacf9e149af276bcc702533700d0be51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 13:31:18 +0200 Subject: [PATCH 068/508] Do not show `NotNan` in `Number`'s `Debug` impl. --- src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index 80dc528bc..b3ae205cc 100644 --- a/src/number.rs +++ b/src/number.rs @@ -283,7 +283,7 @@ impl Debug for Number { debug.field(&i); } N::Float(f) => { - debug.field(&f); + debug.field(&f.into_inner()); } } debug.finish() From b59c0c0e556adc69d0d7981fec0dee77ee8be0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 13:41:42 +0200 Subject: [PATCH 069/508] Do not import always `NotNan`. Skip it when `arbitrary_precision` is enabled. --- src/number.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index b3ae205cc..97a17458e 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,7 +1,6 @@ use crate::de::ParserNumber; use crate::error::Error; use crate::lib::*; -use ordered_float::NotNan; use serde::de::{self, Unexpected, Visitor}; use serde::{ forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, @@ -13,6 +12,9 @@ use crate::error::ErrorCode; #[cfg(feature = "arbitrary_precision")] use serde::de::{IntoDeserializer, MapAccess}; +#[cfg(not(feature = "arbitrary_precision"))] +use ordered_float::NotNan; + #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; From eec4e9d272d2b30943d05705118d37da06169fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 13:42:01 +0200 Subject: [PATCH 070/508] Do not import `ordered_float` default features. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 057a2297c..aaa8aeca6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ serde = { version = "1.0.100", default-features = false } indexmap = { version = "1.5", optional = true } itoa = { version = "0.4.3", default-features = false } ryu = "1.0" -ordered-float = "2.8" +ordered-float = { version = "2.8", default-features = false } [dev-dependencies] automod = "1.0" From d153b886e8370c03c39fe1ae937487d337abc455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:02:21 +0200 Subject: [PATCH 071/508] Discard `ordered_float`, use custom `Hash` impl. --- Cargo.toml | 1 - src/number.rs | 47 +++++++++++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aaa8aeca6..0b09caeae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ serde = { version = "1.0.100", default-features = false } indexmap = { version = "1.5", optional = true } itoa = { version = "0.4.3", default-features = false } ryu = "1.0" -ordered-float = { version = "2.8", default-features = false } [dev-dependencies] automod = "1.0" diff --git a/src/number.rs b/src/number.rs index 97a17458e..be6a1ece8 100644 --- a/src/number.rs +++ b/src/number.rs @@ -12,9 +12,6 @@ use crate::error::ErrorCode; #[cfg(feature = "arbitrary_precision")] use serde::de::{IntoDeserializer, MapAccess}; -#[cfg(not(feature = "arbitrary_precision"))] -use ordered_float::NotNan; - #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; @@ -25,13 +22,31 @@ pub struct Number { } #[cfg(not(feature = "arbitrary_precision"))] -#[derive(Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, PartialEq)] enum N { PosInt(u64), /// Always less than zero. NegInt(i64), /// Always finite. - Float(NotNan), + Float(f64), +} + +// Implementing Eq is fine since any float values are always finite. +#[cfg(not(feature = "arbitrary_precision"))] +impl Eq for N {} + +#[cfg(not(feature = "arbitrary_precision"))] +impl std::hash::Hash for N { + fn hash(&self, h: &mut H) { + match self { + Self::PosInt(i) => i.hash(h), + Self::NegInt(i) => i.hash(h), + Self::Float(f) => { + // Using `f64::to_bits` here is fine since any float values are always finite. + f.to_bits().hash(h) + } + } + } } #[cfg(feature = "arbitrary_precision")] @@ -207,7 +222,7 @@ impl Number { match self.n { N::PosInt(n) => Some(n as f64), N::NegInt(n) => Some(n as f64), - N::Float(n) => Some(n.into_inner()), + N::Float(n) => Some(n), } #[cfg(feature = "arbitrary_precision")] self.n.parse::().ok().filter(|float| float.is_finite()) @@ -231,11 +246,7 @@ impl Number { let n = { #[cfg(not(feature = "arbitrary_precision"))] { - N::Float(unsafe { - // This is safe because `f.is_finite()` ensures - // that `f` is not `NaN`. - NotNan::new_unchecked(f) - }) + N::Float(f) } #[cfg(feature = "arbitrary_precision")] { @@ -285,7 +296,7 @@ impl Debug for Number { debug.field(&i); } N::Float(f) => { - debug.field(&f.into_inner()); + debug.field(&f); } } debug.finish() @@ -310,7 +321,7 @@ impl Serialize for Number { match self.n { N::PosInt(u) => serializer.serialize_u64(u), N::NegInt(i) => serializer.serialize_i64(i), - N::Float(f) => serializer.serialize_f64(f.into_inner()), + N::Float(f) => serializer.serialize_f64(f), } } @@ -464,7 +475,7 @@ macro_rules! deserialize_any { match self.n { N::PosInt(u) => visitor.visit_u64(u), N::NegInt(i) => visitor.visit_i64(i), - N::Float(f) => visitor.visit_f64(f.into_inner()), + N::Float(f) => visitor.visit_f64(f), } } @@ -628,11 +639,7 @@ impl From for Number { ParserNumber::F64(f) => { #[cfg(not(feature = "arbitrary_precision"))] { - N::Float(unsafe { - // This is safe because `ParserNumber` ensures - // that `f` is not `NaN`. - NotNan::new_unchecked(f) - }) + N::Float(f) } #[cfg(feature = "arbitrary_precision")] { @@ -743,7 +750,7 @@ impl Number { match self.n { N::PosInt(u) => Unexpected::Unsigned(u), N::NegInt(i) => Unexpected::Signed(i), - N::Float(f) => Unexpected::Float(f.into_inner()), + N::Float(f) => Unexpected::Float(f), } } From 10655c76395e24297754bac8fd63839a5ebc8efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:05:45 +0200 Subject: [PATCH 072/508] Use `core::hash` instead of `std::hash`. --- src/number.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/number.rs b/src/number.rs index be6a1ece8..93ac2a54f 100644 --- a/src/number.rs +++ b/src/number.rs @@ -36,8 +36,8 @@ enum N { impl Eq for N {} #[cfg(not(feature = "arbitrary_precision"))] -impl std::hash::Hash for N { - fn hash(&self, h: &mut H) { +impl core::hash::Hash for N { + fn hash(&self, h: &mut H) { match self { Self::PosInt(i) => i.hash(h), Self::NegInt(i) => i.hash(h), From c3ac6c00770934a0f0d655f941e97caedfeed504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:13:14 +0200 Subject: [PATCH 073/508] Manual implementation of `Number`'s `PartialEq`. To please clippy. --- src/number.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index 93ac2a54f..381dfb728 100644 --- a/src/number.rs +++ b/src/number.rs @@ -22,7 +22,7 @@ pub struct Number { } #[cfg(not(feature = "arbitrary_precision"))] -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone)] enum N { PosInt(u64), /// Always less than zero. @@ -31,6 +31,18 @@ enum N { Float(f64), } +#[cfg(not(feature = "arbitrary_precision"))] +impl PartialEq for N { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::PosInt(a), Self::PosInt(b)) => a == b, + (Self::NegInt(a), Self::NegInt(b)) => a == b, + (Self::Float(a), Self::Float(b)) => a == b, + _ => false, + } + } +} + // Implementing Eq is fine since any float values are always finite. #[cfg(not(feature = "arbitrary_precision"))] impl Eq for N {} From 9046e3fa869c04554c39f46a5ed571420d86cf10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:16:49 +0200 Subject: [PATCH 074/508] Avoid `Self::` for older Rust versions. Also fix a clippy warning. --- src/number.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/number.rs b/src/number.rs index 381dfb728..b3685f397 100644 --- a/src/number.rs +++ b/src/number.rs @@ -35,9 +35,9 @@ enum N { impl PartialEq for N { fn eq(&self, other: &Self) -> bool { match (self, other) { - (Self::PosInt(a), Self::PosInt(b)) => a == b, - (Self::NegInt(a), Self::NegInt(b)) => a == b, - (Self::Float(a), Self::Float(b)) => a == b, + (N::PosInt(a), N::PosInt(b)) => a == b, + (N::NegInt(a), N::NegInt(b)) => a == b, + (N::Float(a), N::Float(b)) => a == b, _ => false, } } @@ -51,11 +51,11 @@ impl Eq for N {} impl core::hash::Hash for N { fn hash(&self, h: &mut H) { match self { - Self::PosInt(i) => i.hash(h), - Self::NegInt(i) => i.hash(h), - Self::Float(f) => { + N::PosInt(i) => i.hash(h), + N::NegInt(i) => i.hash(h), + N::Float(f) => { // Using `f64::to_bits` here is fine since any float values are always finite. - f.to_bits().hash(h) + f.to_bits().hash(h); } } } From 48393aba4075ea1cb7e08e1a7b0bd03a78bd47e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:24:58 +0200 Subject: [PATCH 075/508] Better justification for the use of `f64::to_bits` --- src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index b3685f397..4c9dad682 100644 --- a/src/number.rs +++ b/src/number.rs @@ -54,7 +54,7 @@ impl core::hash::Hash for N { N::PosInt(i) => i.hash(h), N::NegInt(i) => i.hash(h), N::Float(f) => { - // Using `f64::to_bits` here is fine since any float values are always finite. + // Using `f64::to_bits` here is fine since any float values are never `Nan`. f.to_bits().hash(h); } } From d12fbb9520bc6d3a5aac6d233bb0ffaae831f956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 20 Oct 2021 14:25:51 +0200 Subject: [PATCH 076/508] Fix `NaN` typo. --- src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index 4c9dad682..ad59ae6dd 100644 --- a/src/number.rs +++ b/src/number.rs @@ -54,7 +54,7 @@ impl core::hash::Hash for N { N::PosInt(i) => i.hash(h), N::NegInt(i) => i.hash(h), N::Float(f) => { - // Using `f64::to_bits` here is fine since any float values are never `Nan`. + // Using `f64::to_bits` here is fine since any float values are never `NaN`. f.to_bits().hash(h); } } From 8b355175407f801cb133034dbcee57bce35c3098 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 22 Oct 2021 19:19:15 -0700 Subject: [PATCH 077/508] Revert "Disable broken fuzz build in CI" This reverts commit 5bae82d2d2003c4a0f4f792132510f510713f5ce. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d18bb53f3..b29f1f46d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,5 +95,3 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - run: cargo install cargo-fuzz --debug - run: cargo fuzz build -O - # Currently broken. https://github.com/rust-fuzz/cargo-fuzz/issues/276 - continue-on-error: true From f53ae31df61f7e321276bfcfffe3c1562013d338 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Fri, 5 Nov 2021 12:57:41 +0100 Subject: [PATCH 078/508] Use the same hash for +0 and -0. --- src/number.rs | 9 ++++++++- tests/test.rs | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index ad59ae6dd..b39e164aa 100644 --- a/src/number.rs +++ b/src/number.rs @@ -55,7 +55,14 @@ impl core::hash::Hash for N { N::NegInt(i) => i.hash(h), N::Float(f) => { // Using `f64::to_bits` here is fine since any float values are never `NaN`. - f.to_bits().hash(h); + if *f == 0.0f64 { + // The IEEE 754 standard has two representations for zero, +0 and -0, + // such that +0 == -0. + // In both cases we use the +0 hash so that hash(+0) == hash(-0). + 0.0f64.to_bits().hash(h); + } else { + f.to_bits().hash(h); + } } } } diff --git a/tests/test.rs b/tests/test.rs index c8df23dba..ea7759ce7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2284,3 +2284,19 @@ fn test_value_into_deserializer() { let outer = Outer::deserialize(map.into_deserializer()).unwrap(); assert_eq!(outer.inner.string, "Hello World"); } + +#[test] +fn hash_positive_and_negative_zero() { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + fn hash(obj: impl Hash) -> u64 { + let mut hasher = DefaultHasher::new(); + obj.hash(&mut hasher); + hasher.finish() + } + + let k1 = serde_json::from_str::("0.0").unwrap(); + let k2 = serde_json::from_str::("-0.0").unwrap(); + assert!(k1 != k2 || hash(k1) == hash(k2)); +} From 0035947522dbad7a123ffd3e9b1d48a77103ec93 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 5 Nov 2021 12:12:59 -0700 Subject: [PATCH 079/508] Touch up PR 814 --- src/lib.rs | 2 +- src/number.rs | 15 +++++++-------- tests/test.rs | 13 +++++++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 942333b54..cf54ddb35 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -379,7 +379,7 @@ mod lib { pub use self::core::convert::{self, From, Into}; pub use self::core::default::{self, Default}; pub use self::core::fmt::{self, Debug, Display}; - pub use self::core::hash::{self, Hash}; + pub use self::core::hash::{self, Hash, Hasher}; pub use self::core::iter::FusedIterator; pub use self::core::marker::{self, PhantomData}; pub use self::core::ops::{Bound, RangeBounds}; diff --git a/src/number.rs b/src/number.rs index b39e164aa..dfac7535e 100644 --- a/src/number.rs +++ b/src/number.rs @@ -48,17 +48,16 @@ impl PartialEq for N { impl Eq for N {} #[cfg(not(feature = "arbitrary_precision"))] -impl core::hash::Hash for N { - fn hash(&self, h: &mut H) { - match self { +impl Hash for N { + fn hash(&self, h: &mut H) { + match *self { N::PosInt(i) => i.hash(h), N::NegInt(i) => i.hash(h), N::Float(f) => { - // Using `f64::to_bits` here is fine since any float values are never `NaN`. - if *f == 0.0f64 { - // The IEEE 754 standard has two representations for zero, +0 and -0, - // such that +0 == -0. - // In both cases we use the +0 hash so that hash(+0) == hash(-0). + if f == 0.0f64 { + // There are 2 zero representations, +0 and -0, which + // compare equal but have different bits. We use the +0 hash + // for both so that hash(+0) == hash(-0). 0.0f64.to_bits().hash(h); } else { f.to_bits().hash(h); diff --git a/tests/test.rs b/tests/test.rs index ea7759ce7..4b7540540 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -27,8 +27,10 @@ use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, to_vec, to_writer, Deserializer, Number, Value, }; +use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; use std::fmt::{self, Debug}; +use std::hash::{Hash, Hasher}; use std::io; use std::iter; use std::marker::PhantomData; @@ -2287,9 +2289,6 @@ fn test_value_into_deserializer() { #[test] fn hash_positive_and_negative_zero() { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - fn hash(obj: impl Hash) -> u64 { let mut hasher = DefaultHasher::new(); obj.hash(&mut hasher); @@ -2298,5 +2297,11 @@ fn hash_positive_and_negative_zero() { let k1 = serde_json::from_str::("0.0").unwrap(); let k2 = serde_json::from_str::("-0.0").unwrap(); - assert!(k1 != k2 || hash(k1) == hash(k2)); + if cfg!(feature = "arbitrary_precision") { + assert_ne!(k1, k2); + assert_ne!(hash(k1), hash(k2)); + } else { + assert_eq!(k1, k2); + assert_eq!(hash(k1), hash(k2)); + } } From 5c4f8b225448c3dd3b90003d51a03dcee05da300 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 5 Nov 2021 12:38:49 -0700 Subject: [PATCH 080/508] Release 1.0.69 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0b09caeae..e14e9b145 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.68" # remember to update html_root_url +version = "1.0.69" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index cf54ddb35..11ce1dc7c 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.68")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.69")] // Ignored clippy lints #![allow( clippy::comparison_chain, From c4b6b97ccb808283cbe8ca98006bfdb98e21e4df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Thu, 11 Nov 2021 14:00:55 +0100 Subject: [PATCH 081/508] Add `Map::get_key_value` method. --- src/map.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/map.rs b/src/map.rs index 716f12852..2cd5e56f9 100644 --- a/src/map.rs +++ b/src/map.rs @@ -94,6 +94,19 @@ impl Map { self.map.get_mut(key) } + /// Returns the key-value pair matching the given key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + #[inline] + pub fn get_key_value(&self, key: &Q) -> Option<(&String, &Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.get_key_value(key) + } + /// Inserts a key-value pair into the map. /// /// If the map did not have this key present, `None` is returned. From d275e04a772ad452f3a59b8f32952a5fc35e1b40 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 20:24:37 -0800 Subject: [PATCH 082/508] Add retain to Map This adds the `retain` function to the Map type which calls the inner maps, Either indexmap or std BTreeMap's, retain. I'm trying to mutate a Value::Object's Map in-place and could't find a nice way without the retain function. --- src/map.rs | 26 ++++++++++++++++++++++++++ tests/map.rs | 12 ++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/map.rs b/src/map.rs index 716f12852..4613cc89d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,6 +234,32 @@ impl Map { } } + #[cfg(feature = "preserve_order")] + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. + /// The elements are visited in ascending key order. + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&String, &mut Value) -> bool, + { + self.map.retain(f); + } + + #[cfg(not(feature = "preserve_order"))] + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. + /// The elements are visited in ascending key order. + #[inline] + pub fn retain(&mut self, f: F) + where + F: FnMut(&String, &mut Value) -> bool, + { + self.map.retain(f); + } + /// Gets an iterator over the values of the map. #[inline] pub fn values(&self) -> Values { diff --git a/tests/map.rs b/tests/map.rs index 387a72cdd..3abdf3ec4 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -34,3 +34,15 @@ fn test_append() { assert_eq!(keys, EXPECTED); assert!(val.is_empty()); } + +#[test] +fn test_retain() { + const EXPECTED: &[&str] = &["a", "c"]; + + let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); + let val = v.as_object_mut().unwrap(); + val.retain(|k, _| k.as_str() != "b"); + + let keys: Vec<_> = val.keys().collect(); + assert_eq!(keys, EXPECTED); +} From 0992b447213a33abde2765476feaf033996593f4 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 21:19:51 -0800 Subject: [PATCH 083/508] only include retain for BTreeMap if Rust 1.53.0+ --- build.rs | 6 ++++++ src/map.rs | 2 +- tests/map.rs | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 04ff4a0a1..7f10111d7 100644 --- a/build.rs +++ b/build.rs @@ -31,6 +31,12 @@ 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#library-changes + 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 4613cc89d..14b5b29c8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,7 +234,7 @@ impl Map { } } - #[cfg(feature = "preserve_order")] + #[cfg(all(feature = "preserve_order", not(no_btreemap_remove_entry)))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. diff --git a/tests/map.rs b/tests/map.rs index 3abdf3ec4..adf00623f 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -35,6 +35,7 @@ fn test_append() { assert!(val.is_empty()); } +#[cfg(not(no_btreemap_retain))] #[test] fn test_retain() { const EXPECTED: &[&str] = &["a", "c"]; From 09c589243374d8eb10c885031c0f6ff29ac377e0 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 21:29:42 -0800 Subject: [PATCH 084/508] test order --- src/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.rs b/src/map.rs index 14b5b29c8..b150908a1 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,12 +234,12 @@ impl Map { } } - #[cfg(all(feature = "preserve_order", not(no_btreemap_remove_entry)))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] + #[cfg(all(feature = "preserve_order", not(no_btreemap_remove_entry)))] pub fn retain(&mut self, f: F) where F: FnMut(&String, &mut Value) -> bool, @@ -247,12 +247,12 @@ impl Map { self.map.retain(f); } - #[cfg(not(feature = "preserve_order"))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] + #[cfg(not(feature = "preserve_order"))] pub fn retain(&mut self, f: F) where F: FnMut(&String, &mut Value) -> bool, From eaa1a319c0a4712d3e8669775dd4880efbcfbcdf Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 21:36:52 -0800 Subject: [PATCH 085/508] fix no_btreemap_retain name --- src/map.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/map.rs b/src/map.rs index b150908a1..739643fb2 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,12 +234,12 @@ impl Map { } } + #[cfg(all(feature = "preserve_order", not(no_btreemap_retain)))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] - #[cfg(all(feature = "preserve_order", not(no_btreemap_remove_entry)))] pub fn retain(&mut self, f: F) where F: FnMut(&String, &mut Value) -> bool, @@ -247,12 +247,12 @@ impl Map { self.map.retain(f); } + #[cfg(not(feature = "preserve_order"))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] - #[cfg(not(feature = "preserve_order"))] pub fn retain(&mut self, f: F) where F: FnMut(&String, &mut Value) -> bool, From 29a58a3077456600da25ab079476f1248ed9d6d3 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 21:54:09 -0800 Subject: [PATCH 086/508] don't include indexmap retain without BTreeMap retain --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 739643fb2..665102b00 100644 --- a/src/map.rs +++ b/src/map.rs @@ -247,7 +247,7 @@ impl Map { self.map.retain(f); } - #[cfg(not(feature = "preserve_order"))] + #[cfg(all(not(feature = "preserve_order"), not(no_btreemap_retain)))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. From 68d3b45c9766f8a36b278eabc870e8232128f83d Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Thu, 11 Nov 2021 22:00:21 -0800 Subject: [PATCH 087/508] fix function signature --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 665102b00..a6bdb6af3 100644 --- a/src/map.rs +++ b/src/map.rs @@ -240,7 +240,7 @@ impl Map { /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. /// The elements are visited in ascending key order. #[inline] - pub fn retain(&mut self, f: F) + pub fn retain(&mut self, f: F) where F: FnMut(&String, &mut Value) -> bool, { From 9e36f611dbc37498a89fcbf43e01b2a9a2312c18 Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Fri, 12 Nov 2021 18:49:45 -0800 Subject: [PATCH 088/508] Update build.rs Co-authored-by: David Tolnay --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 7f10111d7..e9ec7d56a 100644 --- a/build.rs +++ b/build.rs @@ -33,7 +33,7 @@ fn main() { } // BTreeMap::retain - // https://blog.rust-lang.org/2021/06/17/Rust-1.53.0.html#library-changes + // 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"); } From debd7d07fc7bfb064b8a704cd9ef2f6f207f66db Mon Sep 17 00:00:00 2001 From: Dean Karn Date: Fri, 12 Nov 2021 18:58:00 -0800 Subject: [PATCH 089/508] remove duplicate function --- src/map.rs | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/map.rs b/src/map.rs index a6bdb6af3..5ab4a39c6 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,20 +234,7 @@ impl Map { } } - #[cfg(all(feature = "preserve_order", not(no_btreemap_retain)))] - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. - /// The elements are visited in ascending key order. - #[inline] - pub fn retain(&mut self, f: F) - where - F: FnMut(&String, &mut Value) -> bool, - { - self.map.retain(f); - } - - #[cfg(all(not(feature = "preserve_order"), not(no_btreemap_retain)))] + #[cfg(not(no_btreemap_retain))] /// Retains only the elements specified by the predicate. /// /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`. From 24f85d2ecf3701a9c4aceae0700ef691325fb04a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 12 Nov 2021 19:07:26 -0800 Subject: [PATCH 090/508] Reorder Map::retain to keep keys() and values() adjacent --- src/map.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/map.rs b/src/map.rs index f847c21dd..6fbc94427 100644 --- a/src/map.rs +++ b/src/map.rs @@ -234,19 +234,6 @@ impl Map { } } - /// Retains only the elements specified by the predicate. - /// - /// 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 - F: FnMut(&String, &mut Value) -> bool, - { - self.map.retain(f); - } - /// Gets an iterator over the values of the map. #[inline] pub fn values(&self) -> Values { @@ -262,6 +249,19 @@ impl Map { iter: self.map.values_mut(), } } + + /// Retains only the elements specified by the predicate. + /// + /// 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 + F: FnMut(&String, &mut Value) -> bool, + { + self.map.retain(f); + } } #[allow(clippy::derivable_impls)] // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7655 From a050481b852b643ba4992a60eee8e51dd22314ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 12 Nov 2021 19:08:47 -0800 Subject: [PATCH 091/508] Release 1.0.70 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e14e9b145..6a579135f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.69" # remember to update html_root_url +version = "1.0.70" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 11ce1dc7c..f8b93aa07 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.69")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.70")] // Ignored clippy lints #![allow( clippy::comparison_chain, From 37fe6b5fa85dba6c66782531e18b9c009c58466d Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sat, 13 Nov 2021 23:13:29 +0100 Subject: [PATCH 092/508] Add impl From> for Box Similar to the std impl From> for Box<[u8]>: https://doc.rust-lang.org/std/boxed/struct.Box.html#impl-From%3CBox%3Cstr%2C%20A%3E%3E --- src/raw.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/raw.rs b/src/raw.rs index b8d04ee19..a0607bc5b 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -216,6 +216,12 @@ impl RawValue { } } +impl From> for Box { + fn from(val: Box) -> Self { + unsafe { mem::transmute::, Box>(val) } + } +} + /// Convert a `T` into a boxed `RawValue`. /// /// # Example From 1ae5566f01542a315199107e90a695e25adb9a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Wed, 17 Nov 2021 14:21:56 +0100 Subject: [PATCH 093/508] Disable `get_key_value` with older Rust versions. --- src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map.rs b/src/map.rs index 2cd5e56f9..6d8afd70f 100644 --- a/src/map.rs +++ b/src/map.rs @@ -99,6 +99,7 @@ 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, From 412556071126d4ae6a3f429393abfe3373731ef1 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 17 Nov 2021 12:36:54 -0800 Subject: [PATCH 094/508] Consolidate unsafe RawValue transmutes in one place --- src/raw.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/raw.rs b/src/raw.rs index a0607bc5b..a0e16305c 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -122,6 +122,10 @@ impl RawValue { fn from_owned(json: Box) -> Box { unsafe { mem::transmute::, Box>(json) } } + + fn into_owned(raw_value: Box) -> Box { + unsafe { mem::transmute::, Box>(raw_value) } + } } impl Clone for Box { @@ -217,8 +221,8 @@ impl RawValue { } impl From> for Box { - fn from(val: Box) -> Self { - unsafe { mem::transmute::, Box>(val) } + fn from(raw_value: Box) -> Self { + RawValue::into_owned(raw_value) } } From 379412b13875cfb2e440937c4508944d48ee6c96 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 17 Nov 2021 12:41:32 -0800 Subject: [PATCH 095/508] Release 1.0.71 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a579135f..c4eb2ab52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.70" # remember to update html_root_url +version = "1.0.71" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index f8b93aa07..de55ef589 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.70")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.71")] // Ignored clippy lints #![allow( clippy::comparison_chain, From 33c3134cd2b96a955c16335131161d7889e98dd6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 23 Nov 2021 10:41:18 -0800 Subject: [PATCH 096/508] Elaborate on StrRead::parse_str safety comment --- src/read.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/read.rs b/src/read.rs index 85514db03..4e883c68b 100644 --- a/src/read.rs +++ b/src/read.rs @@ -654,8 +654,9 @@ impl<'a> Read<'a> for StrRead<'a> { fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec) -> Result> { self.delegate.parse_str_bytes(scratch, true, |_, bytes| { - // The input is assumed to be valid UTF-8 and the \u-escapes are - // checked along the way, so don't need to check here. + // The deserialization input came in as &str with a UTF-8 guarantee, + // and the \u-escapes are checked along the way, so don't need to + // check here. Ok(unsafe { str::from_utf8_unchecked(bytes) }) }) } From 849c6845b4c5b5076897bedeb9d0912e95e50517 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 24 Nov 2021 14:50:33 +0100 Subject: [PATCH 097/508] Deserialize lone surrogates into byte bufs This commit deserializes lone surrogates in strings that are encoded in escape sequences instead of erroring on them. --- src/de.rs | 13 ++++++------ src/read.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++------ tests/test.rs | 7 ++++++ 3 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/de.rs b/src/de.rs index a2f34b908..0766edf31 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1580,20 +1580,21 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// ``` /// /// Backslash escape sequences like `\n` are still interpreted and required - /// to be valid, and `\u` escape sequences are required to represent valid - /// Unicode code points. + /// to be valid. `\u` escape sequences are required to represent valid + /// Unicode code points, except in the case of lone surrogate pairs. /// /// ``` /// use serde_bytes::ByteBuf; /// /// fn look_at_bytes() { - /// let json_data = b"\"invalid unicode surrogate: \\uD801\""; + /// let json_data = b"\"lone surrogate pair: \\uD801\""; /// let parsed: Result = serde_json::from_slice(json_data); /// - /// assert!(parsed.is_err()); + /// assert!(parsed.is_ok()); /// - /// let expected_msg = "unexpected end of hex escape at line 1 column 35"; - /// assert_eq!(expected_msg, parsed.unwrap_err().to_string()); + /// let expected = b"lone surrogate pair: \xED\xA0\x81"; + /// let bytes: ByteBuf = parsed.unwrap(); + /// assert_eq!(expected, &bytes[..]); /// } /// # /// # look_at_bytes(); diff --git a/src/read.rs b/src/read.rs index 4e883c68b..329a6df3c 100644 --- a/src/read.rs +++ b/src/read.rs @@ -225,7 +225,7 @@ where return result(self, scratch); } b'\\' => { - tri!(parse_escape(self, scratch)); + tri!(parse_escape(self, validate, scratch)); } _ => { if validate { @@ -465,7 +465,7 @@ impl<'a> SliceRead<'a> { b'\\' => { scratch.extend_from_slice(&self.slice[start..self.index]); self.index += 1; - tri!(parse_escape(self, scratch)); + tri!(parse_escape(self, validate, scratch)); start = self.index; } _ => { @@ -817,6 +817,16 @@ where } } +fn peek_or_eof<'de, R>(read: &mut R) -> Result +where + R: ?Sized + Read<'de>, +{ + match tri!(read.peek()) { + Some(b) => Ok(b), + None => error(read, ErrorCode::EofWhileParsingString), + } +} + fn error<'de, R, T>(read: &R, reason: ErrorCode) -> Result where R: ?Sized + Read<'de>, @@ -831,7 +841,11 @@ fn as_str<'de, 's, R: Read<'de>>(read: &R, slice: &'s [u8]) -> Result<&'s str> { /// Parses a JSON escape sequence and appends it into the scratch space. Assumes /// the previous byte read was a backslash. -fn parse_escape<'de, R: Read<'de>>(read: &mut R, scratch: &mut Vec) -> Result<()> { +fn parse_escape<'de, R: Read<'de>>( + read: &mut R, + validate: bool, + scratch: &mut Vec, +) -> Result<()> { let ch = tri!(next_or_eof(read)); match ch { @@ -851,13 +865,44 @@ fn parse_escape<'de, R: Read<'de>>(read: &mut R, scratch: &mut Vec) -> Resul // Non-BMP characters are encoded as a sequence of // two hex escapes, representing UTF-16 surrogates. + // If `validate` is false and we only find a single + // hex escape that is a surrogate, then we'll accept + // it instead of erroring. n1 @ 0xD800..=0xDBFF => { - if tri!(next_or_eof(read)) != b'\\' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); + if tri!(peek_or_eof(read)) != b'\\' { + if validate { + tri!(next_or_eof(read)); + return error(read, ErrorCode::UnexpectedEndOfHexEscape); + } + + let utf8_bytes = [ + (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, + (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, + (n1 & 0x3F) as u8 | 0b1000_0000, + ]; + + scratch.extend_from_slice(&utf8_bytes); + + return Ok(()); } - if tri!(next_or_eof(read)) != b'u' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); + tri!(next_or_eof(read)); + if tri!(peek_or_eof(read)) != b'u' { + if validate { + tri!(next_or_eof(read)); + return error(read, ErrorCode::UnexpectedEndOfHexEscape); + } + + let utf8_bytes = [ + (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, + (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, + (n1 & 0x3F) as u8 | 0b1000_0000, + ]; + + scratch.extend_from_slice(&utf8_bytes); + + return Ok(()); } + tri!(next_or_eof(read)); let n2 = tri!(read.decode_hex_escape()); diff --git a/tests/test.rs b/tests/test.rs index 4b7540540..bf19532b0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1714,6 +1714,13 @@ fn test_byte_buf_de() { assert_eq!(v, bytes); } +#[test] +fn test_byte_buf_de_invalid_escape_sequence() { + let bytes = ByteBuf::from(vec![237, 160, 188]); + let v: ByteBuf = from_str(r#""\ud83c""#).unwrap(); + assert_eq!(v, bytes); +} + #[test] fn test_byte_buf_de_multiple() { let s: Vec = from_str(r#"["ab\nc", "cd\ne"]"#).unwrap(); From 4c28c5737b26f15bc7457d53eff8517b33cb4a43 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 24 Nov 2021 23:29:17 +0100 Subject: [PATCH 098/508] fix wording --- src/de.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/de.rs b/src/de.rs index 0766edf31..d9a5fee8c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1581,18 +1581,18 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// /// Backslash escape sequences like `\n` are still interpreted and required /// to be valid. `\u` escape sequences are required to represent valid - /// Unicode code points, except in the case of lone surrogate pairs. + /// Unicode code points, except in the case of lone surrogates. /// /// ``` /// use serde_bytes::ByteBuf; /// /// fn look_at_bytes() { - /// let json_data = b"\"lone surrogate pair: \\uD801\""; + /// let json_data = b"\"lone surrogate: \\uD801\""; /// let parsed: Result = serde_json::from_slice(json_data); /// /// assert!(parsed.is_ok()); /// - /// let expected = b"lone surrogate pair: \xED\xA0\x81"; + /// let expected = b"lone surrogate: \xED\xA0\x81"; /// let bytes: ByteBuf = parsed.unwrap(); /// assert_eq!(expected, &bytes[..]); /// } From 07c740c2ffa2e81c9c3dc7982b67572e990e2cdd Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Wed, 24 Nov 2021 23:48:42 +0100 Subject: [PATCH 099/508] fix parsing escape sequences after lone surrogates --- src/read.rs | 29 +++++++++++++++++++++++------ tests/test.rs | 23 ++++++++++++++++++++++- 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/read.rs b/src/read.rs index 329a6df3c..034cc6557 100644 --- a/src/read.rs +++ b/src/read.rs @@ -859,8 +859,20 @@ fn parse_escape<'de, R: Read<'de>>( b't' => scratch.push(b'\t'), b'u' => { let c = match tri!(read.decode_hex_escape()) { - 0xDC00..=0xDFFF => { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + n @ 0xDC00..=0xDFFF => { + if validate { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } + + let utf8_bytes = [ + (n >> 12 & 0x0F) as u8 | 0b1110_0000, + (n >> 6 & 0x3F) as u8 | 0b1000_0000, + (n & 0x3F) as u8 | 0b1000_0000, + ]; + + scratch.extend_from_slice(&utf8_bytes); + + return Ok(()); } // Non-BMP characters are encoded as a sequence of @@ -871,7 +883,7 @@ fn parse_escape<'de, R: Read<'de>>( n1 @ 0xD800..=0xDBFF => { if tri!(peek_or_eof(read)) != b'\\' { if validate { - tri!(next_or_eof(read)); + read.discard(); return error(read, ErrorCode::UnexpectedEndOfHexEscape); } @@ -885,10 +897,11 @@ fn parse_escape<'de, R: Read<'de>>( return Ok(()); } - tri!(next_or_eof(read)); + read.discard(); + if tri!(peek_or_eof(read)) != b'u' { if validate { - tri!(next_or_eof(read)); + read.discard(); return error(read, ErrorCode::UnexpectedEndOfHexEscape); } @@ -900,9 +913,13 @@ fn parse_escape<'de, R: Read<'de>>( scratch.extend_from_slice(&utf8_bytes); + // The \ prior to this byte started an escape sequence, + // so we need to parse that now. + parse_escape(read, validate, scratch)?; + return Ok(()); } - tri!(next_or_eof(read)); + read.discard(); let n2 = tri!(read.decode_hex_escape()); diff --git a/tests/test.rs b/tests/test.rs index bf19532b0..636053004 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1715,10 +1715,31 @@ fn test_byte_buf_de() { } #[test] -fn test_byte_buf_de_invalid_escape_sequence() { +fn test_byte_buf_de_lone_surrogate() { let bytes = ByteBuf::from(vec![237, 160, 188]); let v: ByteBuf = from_str(r#""\ud83c""#).unwrap(); assert_eq!(v, bytes); + + let bytes = ByteBuf::from(vec![237, 160, 188, 10]); + let v: ByteBuf = from_str(r#""\ud83c\n""#).unwrap(); + assert_eq!(v, bytes); + + let bytes = ByteBuf::from(vec![237, 160, 188, 32]); + let v: ByteBuf = from_str(r#""\ud83c ""#).unwrap(); + assert_eq!(v, bytes); + + let bytes = ByteBuf::from(vec![237, 176, 129]); + let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); + assert_eq!(v, bytes); + + let res = from_str::(r#""\ud83c\!""#); + assert!(res.is_err()); + + let res = from_str::(r#""\ud83c\u""#); + assert!(res.is_err()); + + let res = from_str::(r#""\ud83c\ud83c""#); + assert!(res.is_err()); } #[test] From 683832a3434a04ee097e04cf4aed1eec5c9a0b67 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:08:38 -0800 Subject: [PATCH 100/508] Touch up doc changes from PR 828 --- src/de.rs | 18 ++++++++---------- src/read.rs | 9 ++++----- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/de.rs b/src/de.rs index d9a5fee8c..a01c4767a 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1560,7 +1560,8 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// /// # Examples /// - /// You can use this to parse JSON strings containing invalid UTF-8 bytes. + /// You can use this to parse JSON strings containing invalid UTF-8 bytes, + /// or unpaired surrogates. /// /// ``` /// use serde_bytes::ByteBuf; @@ -1580,21 +1581,18 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// ``` /// /// Backslash escape sequences like `\n` are still interpreted and required - /// to be valid. `\u` escape sequences are required to represent valid - /// Unicode code points, except in the case of lone surrogates. + /// to be valid. `\u` escape sequences are required to represent a valid + /// Unicode code point or lone surrogate. /// /// ``` /// use serde_bytes::ByteBuf; /// - /// fn look_at_bytes() { + /// fn look_at_bytes() -> Result<(), serde_json::Error> { /// let json_data = b"\"lone surrogate: \\uD801\""; - /// let parsed: Result = serde_json::from_slice(json_data); - /// - /// assert!(parsed.is_ok()); - /// + /// let bytes: ByteBuf = serde_json::from_slice(json_data)?; /// let expected = b"lone surrogate: \xED\xA0\x81"; - /// let bytes: ByteBuf = parsed.unwrap(); - /// assert_eq!(expected, &bytes[..]); + /// assert_eq!(expected, bytes.as_slice()); + /// Ok(()) /// } /// # /// # look_at_bytes(); diff --git a/src/read.rs b/src/read.rs index 034cc6557..3d72880ea 100644 --- a/src/read.rs +++ b/src/read.rs @@ -875,11 +875,10 @@ fn parse_escape<'de, R: Read<'de>>( return Ok(()); } - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - // If `validate` is false and we only find a single - // hex escape that is a surrogate, then we'll accept - // it instead of erroring. + // Non-BMP characters are encoded as a sequence of two hex + // escapes, representing UTF-16 surrogates. If deserializing a + // utf-8 string the surrogates are required to be paired, + // whereas deserializing a byte string accepts lone surrogates. n1 @ 0xD800..=0xDBFF => { if tri!(peek_or_eof(read)) != b'\\' { if validate { From 48dad22b3f2cfc1c4c8dd19976cbe088da932eb4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:10:13 -0800 Subject: [PATCH 101/508] Collapse surrogate encode into extend_from_slice call --- src/read.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/read.rs b/src/read.rs index 3d72880ea..035c865d3 100644 --- a/src/read.rs +++ b/src/read.rs @@ -864,13 +864,11 @@ fn parse_escape<'de, R: Read<'de>>( return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); } - let utf8_bytes = [ + scratch.extend_from_slice(&[ (n >> 12 & 0x0F) as u8 | 0b1110_0000, (n >> 6 & 0x3F) as u8 | 0b1000_0000, (n & 0x3F) as u8 | 0b1000_0000, - ]; - - scratch.extend_from_slice(&utf8_bytes); + ]); return Ok(()); } @@ -886,13 +884,11 @@ fn parse_escape<'de, R: Read<'de>>( return error(read, ErrorCode::UnexpectedEndOfHexEscape); } - let utf8_bytes = [ + scratch.extend_from_slice(&[ (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, (n1 & 0x3F) as u8 | 0b1000_0000, - ]; - - scratch.extend_from_slice(&utf8_bytes); + ]); return Ok(()); } @@ -904,13 +900,11 @@ fn parse_escape<'de, R: Read<'de>>( return error(read, ErrorCode::UnexpectedEndOfHexEscape); } - let utf8_bytes = [ + scratch.extend_from_slice(&[ (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, (n1 & 0x3F) as u8 | 0b1000_0000, - ]; - - scratch.extend_from_slice(&utf8_bytes); + ]); // The \ prior to this byte started an escape sequence, // so we need to parse that now. From 7911e704a0fb762ddda89951f01f02457074ca02 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:10:49 -0800 Subject: [PATCH 102/508] Tail recurse on parse_escape --- src/read.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/read.rs b/src/read.rs index 035c865d3..f2396b81c 100644 --- a/src/read.rs +++ b/src/read.rs @@ -908,9 +908,7 @@ fn parse_escape<'de, R: Read<'de>>( // The \ prior to this byte started an escape sequence, // so we need to parse that now. - parse_escape(read, validate, scratch)?; - - return Ok(()); + return parse_escape(read, validate, scratch); } read.discard(); From cb4a2517b2a8b20449fe0923a9fb9133bacc1b4e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:12:52 -0800 Subject: [PATCH 103/508] Document why the parse_escape recursion is not dangerous --- src/read.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/read.rs b/src/read.rs index f2396b81c..6cb6780f8 100644 --- a/src/read.rs +++ b/src/read.rs @@ -907,7 +907,10 @@ fn parse_escape<'de, R: Read<'de>>( ]); // The \ prior to this byte started an escape sequence, - // so we need to parse that now. + // so we need to parse that now. This recursive call + // does not blow the stack on malicious input because + // the escape is not \u, so it will be handled by one + // of the easy nonrecursive cases. return parse_escape(read, validate, scratch); } read.discard(); From 11d3464f1cd9e48aabb3630773a03da845838c7a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:14:03 -0800 Subject: [PATCH 104/508] Extract common logic of surrogate encode --- src/read.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/read.rs b/src/read.rs index 6cb6780f8..321f16cd4 100644 --- a/src/read.rs +++ b/src/read.rs @@ -858,17 +858,21 @@ fn parse_escape<'de, R: Read<'de>>( b'r' => scratch.push(b'\r'), b't' => scratch.push(b'\t'), b'u' => { + fn encode_surrogate(scratch: &mut Vec, n: u16) { + scratch.extend_from_slice(&[ + (n >> 12 & 0x0F) as u8 | 0b1110_0000, + (n >> 6 & 0x3F) as u8 | 0b1000_0000, + (n & 0x3F) as u8 | 0b1000_0000, + ]); + } + let c = match tri!(read.decode_hex_escape()) { n @ 0xDC00..=0xDFFF => { if validate { return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); } - scratch.extend_from_slice(&[ - (n >> 12 & 0x0F) as u8 | 0b1110_0000, - (n >> 6 & 0x3F) as u8 | 0b1000_0000, - (n & 0x3F) as u8 | 0b1000_0000, - ]); + encode_surrogate(scratch, n); return Ok(()); } @@ -884,11 +888,7 @@ fn parse_escape<'de, R: Read<'de>>( return error(read, ErrorCode::UnexpectedEndOfHexEscape); } - scratch.extend_from_slice(&[ - (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, - (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, - (n1 & 0x3F) as u8 | 0b1000_0000, - ]); + encode_surrogate(scratch, n1); return Ok(()); } @@ -900,11 +900,7 @@ fn parse_escape<'de, R: Read<'de>>( return error(read, ErrorCode::UnexpectedEndOfHexEscape); } - scratch.extend_from_slice(&[ - (n1 >> 12 & 0x0F) as u8 | 0b1110_0000, - (n1 >> 6 & 0x3F) as u8 | 0b1000_0000, - (n1 & 0x3F) as u8 | 0b1000_0000, - ]); + encode_surrogate(scratch, n1); // The \ prior to this byte started an escape sequence, // so we need to parse that now. This recursive call From 311f185d8e5d3c37ed284c692d0b2e493c38fb33 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:15:24 -0800 Subject: [PATCH 105/508] Use binary mask to line up visually with the bits being |'d in --- src/read.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/read.rs b/src/read.rs index 321f16cd4..50111b605 100644 --- a/src/read.rs +++ b/src/read.rs @@ -860,9 +860,9 @@ fn parse_escape<'de, R: Read<'de>>( b'u' => { fn encode_surrogate(scratch: &mut Vec, n: u16) { scratch.extend_from_slice(&[ - (n >> 12 & 0x0F) as u8 | 0b1110_0000, - (n >> 6 & 0x3F) as u8 | 0b1000_0000, - (n & 0x3F) as u8 | 0b1000_0000, + (n >> 12 & 0b0000_1111) as u8 | 0b1110_0000, + (n >> 6 & 0b0011_1111) as u8 | 0b1000_0000, + (n & 0b0011_1111) as u8 | 0b1000_0000, ]); } From 142207623acd222a650511df39ffbcc75ad87a42 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:16:58 -0800 Subject: [PATCH 106/508] Rearrange the early return on lone or encoded surrogate --- src/read.rs | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/src/read.rs b/src/read.rs index 50111b605..4b355fd43 100644 --- a/src/read.rs +++ b/src/read.rs @@ -868,13 +868,12 @@ fn parse_escape<'de, R: Read<'de>>( let c = match tri!(read.decode_hex_escape()) { n @ 0xDC00..=0xDFFF => { - if validate { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - encode_surrogate(scratch, n); - - return Ok(()); + return if validate { + error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) + } else { + encode_surrogate(scratch, n); + Ok(()) + }; } // Non-BMP characters are encoded as a sequence of two hex @@ -883,31 +882,29 @@ fn parse_escape<'de, R: Read<'de>>( // whereas deserializing a byte string accepts lone surrogates. n1 @ 0xD800..=0xDBFF => { if tri!(peek_or_eof(read)) != b'\\' { - if validate { + return if validate { read.discard(); - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - - encode_surrogate(scratch, n1); - - return Ok(()); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + encode_surrogate(scratch, n1); + Ok(()) + }; } read.discard(); if tri!(peek_or_eof(read)) != b'u' { - if validate { + return if validate { read.discard(); - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - - encode_surrogate(scratch, n1); - - // The \ prior to this byte started an escape sequence, - // so we need to parse that now. This recursive call - // does not blow the stack on malicious input because - // the escape is not \u, so it will be handled by one - // of the easy nonrecursive cases. - return parse_escape(read, validate, scratch); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + encode_surrogate(scratch, n1); + // The \ prior to this byte started an escape sequence, + // so we need to parse that now. This recursive call + // does not blow the stack on malicious input because + // the escape is not \u, so it will be handled by one + // of the easy nonrecursive cases. + parse_escape(read, validate, scratch) + }; } read.discard(); From 265fb7ee40d8ef25cb05acc9dc4735621cb1341e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:17:43 -0800 Subject: [PATCH 107/508] Move discard of expected byte immediately after peek --- src/read.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/read.rs b/src/read.rs index 4b355fd43..2621f3f3f 100644 --- a/src/read.rs +++ b/src/read.rs @@ -881,7 +881,9 @@ fn parse_escape<'de, R: Read<'de>>( // utf-8 string the surrogates are required to be paired, // whereas deserializing a byte string accepts lone surrogates. n1 @ 0xD800..=0xDBFF => { - if tri!(peek_or_eof(read)) != b'\\' { + if tri!(peek_or_eof(read)) == b'\\' { + read.discard(); + } else { return if validate { read.discard(); error(read, ErrorCode::UnexpectedEndOfHexEscape) @@ -890,9 +892,10 @@ fn parse_escape<'de, R: Read<'de>>( Ok(()) }; } - read.discard(); - if tri!(peek_or_eof(read)) != b'u' { + if tri!(peek_or_eof(read)) == b'u' { + read.discard(); + } else { return if validate { read.discard(); error(read, ErrorCode::UnexpectedEndOfHexEscape) @@ -906,7 +909,6 @@ fn parse_escape<'de, R: Read<'de>>( parse_escape(read, validate, scratch) }; } - read.discard(); let n2 = tri!(read.decode_hex_escape()); From 76e376c8d1f30dcfdfcdde486b3ae9102c793841 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 24 Nov 2021 21:29:26 -0800 Subject: [PATCH 108/508] Release 1.0.72 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c4eb2ab52..82f8159c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.71" # remember to update html_root_url +version = "1.0.72" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index de55ef589..7332f0ae5 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.71")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.72")] // Ignored clippy lints #![allow( clippy::comparison_chain, From 4a0924cbd17eed894a84ead07dc7d56497411290 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 25 Nov 2021 15:28:16 -0800 Subject: [PATCH 109/508] Skip error codepath on hex escape outside the surrogate range --- src/read.rs | 59 +++++++++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/src/read.rs b/src/read.rs index 2621f3f3f..7bad5708a 100644 --- a/src/read.rs +++ b/src/read.rs @@ -926,12 +926,9 @@ fn parse_escape<'de, R: Read<'de>>( } } - n => match char::from_u32(n as u32) { - Some(c) => c, - None => { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - }, + // Every u16 outside of the surrogate ranges above is guaranteed + // to be a legal char. + n => char::from_u32(n as u32).unwrap(), }; scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes()); @@ -954,38 +951,34 @@ where match ch { b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't' => {} - b'u' => { - let n = match tri!(read.decode_hex_escape()) { - 0xDC00..=0xDFFF => { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(next_or_eof(read)) != b'\\' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - if tri!(next_or_eof(read)) != b'u' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - - let n2 = tri!(read.decode_hex_escape()); - - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } + b'u' => match tri!(read.decode_hex_escape()) { + 0xDC00..=0xDFFF => { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } - (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000 + // Non-BMP characters are encoded as a sequence of + // two hex escapes, representing UTF-16 surrogates. + n1 @ 0xD800..=0xDBFF => { + if tri!(next_or_eof(read)) != b'\\' { + return error(read, ErrorCode::UnexpectedEndOfHexEscape); + } + if tri!(next_or_eof(read)) != b'u' { + return error(read, ErrorCode::UnexpectedEndOfHexEscape); } - n => n as u32, - }; + let n2 = tri!(read.decode_hex_escape()); + if n2 < 0xDC00 || n2 > 0xDFFF { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } - if char::from_u32(n).is_none() { - return error(read, ErrorCode::InvalidUnicodeCodePoint); + let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; + if char::from_u32(n).is_none() { + return error(read, ErrorCode::InvalidUnicodeCodePoint); + } } - } + + _ => {} + }, _ => { return error(read, ErrorCode::InvalidEscape); } From 51e9616deeb0ab1f42a481201aaf28043de76207 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 25 Nov 2021 14:41:09 +0100 Subject: [PATCH 110/508] Allow lone surrogates in raw values --- src/read.rs | 35 ++++++++--------------------------- tests/test.rs | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/src/read.rs b/src/read.rs index 7bad5708a..f6efbb791 100644 --- a/src/read.rs +++ b/src/read.rs @@ -951,34 +951,15 @@ where match ch { b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't' => {} - b'u' => match tri!(read.decode_hex_escape()) { - 0xDC00..=0xDFFF => { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - // Non-BMP characters are encoded as a sequence of - // two hex escapes, representing UTF-16 surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(next_or_eof(read)) != b'\\' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - if tri!(next_or_eof(read)) != b'u' { - return error(read, ErrorCode::UnexpectedEndOfHexEscape); - } - - let n2 = tri!(read.decode_hex_escape()); - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } - - let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; - if char::from_u32(n).is_none() { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - } + b'u' => { + // At this point we don't care if the codepoint is valid. We just + // want to consume it. We don't actually know what is valid or not + // at this point, because that depends on if this string will + // ultimately be parsed into a string or a byte buffer in the "real" + // parse. - _ => {} - }, + tri!(read.decode_hex_escape()); + } _ => { return error(read, ErrorCode::InvalidEscape); } diff --git a/tests/test.rs b/tests/test.rs index 636053004..fa96e812d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1742,6 +1742,20 @@ fn test_byte_buf_de_lone_surrogate() { assert!(res.is_err()); } +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_de_lone_surrogate() { + use serde_json::value::RawValue; + + assert!(from_str::>(r#""\ud83c""#).is_ok()); + assert!(from_str::>(r#""\ud83c\n""#).is_ok()); + assert!(from_str::>(r#""\ud83c ""#).is_ok()); + assert!(from_str::>(r#""\udc01 ""#).is_ok()); + assert!(from_str::>(r#""\udc01\!""#).is_err()); + assert!(from_str::>(r#""\udc01\u""#).is_err()); + assert!(from_str::>(r#""\ud83c\ud83c""#).is_ok()); +} + #[test] fn test_byte_buf_de_multiple() { let s: Vec = from_str(r#"["ab\nc", "cd\ne"]"#).unwrap(); From 7db1ea907de55338d4069924dd1575d4e9b6cf60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 Dec 2021 18:40:17 -0800 Subject: [PATCH 111/508] Ignore needless_late_init Clippy lint error: unneeded late initalization --> tests/../src/lexical/float.rs:173:9 | 173 | let exp: u64; | ^^^^^^^^^^^^^ | = note: `-D clippy::needless-late-init` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init help: declare `exp` here | 174 | let exp: u64 = if (fp.exp == F::DENORMAL_EXPONENT) && (fp.mant & F::HIDDEN_BIT_MASK.as_u64()) == 0 { | ++++++++++++++ help: remove the assignments from the branches | 175 ~ 0 176 | } else { 177 ~ (fp.exp + F::EXPONENT_BIAS) as u64 | help: add a semicolon after the `if` expression | 178 | }; | + --- src/lib.rs | 1 + tests/lexical.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 7332f0ae5..50a6f8237 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -312,6 +312,7 @@ clippy::match_like_matches_macro, clippy::match_single_binding, clippy::needless_doctest_main, + clippy::needless_late_init, clippy::transmute_ptr_to_ptr, clippy::unnecessary_wraps, // clippy bug: https://github.com/rust-lang/rust-clippy/issues/5704 diff --git a/tests/lexical.rs b/tests/lexical.rs index be730420f..928ad6572 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -10,6 +10,7 @@ clippy::float_cmp, clippy::if_not_else, clippy::module_name_repetitions, + clippy::needless_late_init, clippy::shadow_unrelated, clippy::similar_names, clippy::single_match_else, From a28529ddc94de34fb18024ac686d75e29d458032 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 14:50:52 -0800 Subject: [PATCH 112/508] Skip ui test when running in miri --- tests/compiletest.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index f9aea23b5..7974a6249 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,4 +1,5 @@ #[rustversion::attr(not(nightly), ignore)] +#[cfg_attr(miri, ignore)] #[test] fn ui() { let t = trybuild::TestCases::new(); From c3eddcdf4006a53efd2d2d5aaa5eceea4f7f6f8c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 14:53:18 -0800 Subject: [PATCH 113/508] Collect imports to top of test suite --- tests/test.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 636053004..2925835c7 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -20,9 +20,11 @@ trace_macros!(true); mod macros; use serde::de::{self, IgnoredAny, IntoDeserializer}; -use serde::ser::{self, Serializer}; +use serde::ser::{self, SerializeMap, SerializeSeq, Serializer}; use serde::{Deserialize, Serialize}; use serde_bytes::{ByteBuf, Bytes}; +#[cfg(feature = "raw_value")] +use serde_json::value::RawValue; use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, to_vec, to_writer, Deserializer, Number, Value, @@ -34,8 +36,11 @@ use std::hash::{Hash, Hasher}; use std::io; use std::iter; use std::marker::PhantomData; +use std::mem; +use std::net; use std::str::FromStr; use std::string::ToString; +use std::thread; use std::{f32, f64}; use std::{i16, i32, i64, i8}; use std::{u16, u32, u64, u8}; @@ -1456,7 +1461,6 @@ fn test_serialize_seq_with_no_len() { where S: ser::Serializer, { - use serde::ser::SerializeSeq; let mut seq = serializer.serialize_seq(None)?; for elem in &self.0 { seq.serialize_element(elem)?; @@ -1543,7 +1547,6 @@ fn test_serialize_map_with_no_len() { where S: ser::Serializer, { - use serde::ser::SerializeMap; let mut map = serializer.serialize_map(None)?; for (k, v) in &self.0 { map.serialize_entry(k, v)?; @@ -1622,10 +1625,6 @@ fn test_serialize_map_with_no_len() { #[test] fn test_deserialize_from_stream() { - use serde::Deserialize; - use std::net; - use std::thread; - #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Message { message: String, @@ -1789,8 +1788,6 @@ fn test_json_pointer() { #[test] fn test_json_pointer_mut() { - use std::mem; - // Test case taken from https://tools.ietf.org/html/rfc6901#page-5 let mut data: Value = from_str( r#"{ @@ -2172,8 +2169,6 @@ fn test_integer128() { #[cfg(feature = "raw_value")] #[test] fn test_borrowed_raw_value() { - use serde_json::value::RawValue; - #[derive(Serialize, Deserialize)] struct Wrapper<'a> { a: i8, @@ -2206,8 +2201,6 @@ fn test_borrowed_raw_value() { #[cfg(feature = "raw_value")] #[test] fn test_boxed_raw_value() { - use serde_json::value::RawValue; - #[derive(Serialize, Deserialize)] struct Wrapper { a: i8, @@ -2254,8 +2247,6 @@ fn test_boxed_raw_value() { #[cfg(feature = "raw_value")] #[test] fn test_raw_invalid_utf8() { - use serde_json::value::RawValue; - let j = &[b'"', b'\xCE', b'\xF8', b'"']; let value_err = serde_json::from_slice::(j).unwrap_err(); let raw_value_err = serde_json::from_slice::>(j).unwrap_err(); From f0774c482ae5ca619161312e573de0ffa69c9a60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 15:00:37 -0800 Subject: [PATCH 114/508] Raise required rustc from 1.31 to 1.36 --- .github/workflows/ci.yml | 6 +++--- Cargo.toml | 2 +- README.md | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b29f1f46d..f20fc0283 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.40.0, 1.38.0, 1.31.0] + rust: [beta, stable, 1.45.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable @@ -42,9 +42,9 @@ jobs: toolchain: ${{matrix.rust}} - run: cargo check - run: cargo check --features preserve_order - if: matrix.rust != '1.31.0' + if: 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.31.0' + if: matrix.rust != '1.36.0' - run: cargo check --features float_roundtrip - run: cargo check --features arbitrary_precision - run: cargo check --features raw_value diff --git a/Cargo.toml b/Cargo.toml index 82f8159c9..0e1bdcac5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ categories = ["encoding"] readme = "README.md" include = ["build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2018" -rust-version = "1.31" +rust-version = "1.36" [dependencies] serde = { version = "1.0.100", default-features = false } diff --git a/README.md b/README.md index 96587a0f6..27a365372 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.31+]][rustc] +# Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.36+]][rustc] [Build Status]: https://img.shields.io/github/workflow/status/serde-rs/json/CI/master [travis]: https://github.com/serde-rs/json/actions?query=branch%3Amaster [Latest Version]: https://img.shields.io/crates/v/serde_json.svg [crates.io]: https://crates.io/crates/serde\_json -[Rustc Version 1.31+]: https://img.shields.io/badge/rustc-1.31+-lightgray.svg -[rustc]: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-2018.html +[Rustc Version 1.36+]: https://img.shields.io/badge/rustc-1.36+-lightgray.svg +[rustc]: https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html **Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.** From aaaf125f0b2504b30abb0446adf18dae8e709a8b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 14:56:35 -0800 Subject: [PATCH 115/508] Disable TcpStream test on miri error: unsupported operation: can't call foreign function: getaddrinfo --> nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/net.rs:191:21 | 191 | cvt_gai(c::getaddrinfo(c_host.as_ptr(), ptr::null(), &hints, &mut res)) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't call foreign function: getaddrinfo | = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support --- tests/test.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 2925835c7..7dbc21638 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -27,7 +27,7 @@ use serde_bytes::{ByteBuf, Bytes}; use serde_json::value::RawValue; use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, - to_vec, to_writer, Deserializer, Number, Value, + to_vec, Deserializer, Number, Value, }; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; @@ -37,10 +37,8 @@ use std::io; use std::iter; use std::marker::PhantomData; use std::mem; -use std::net; use std::str::FromStr; use std::string::ToString; -use std::thread; use std::{f32, f64}; use std::{i16, i32, i64, i8}; use std::{u16, u32, u64, u8}; @@ -1623,14 +1621,19 @@ fn test_serialize_map_with_no_len() { assert_eq!(s, expected); } +#[cfg(not(miri))] #[test] fn test_deserialize_from_stream() { + use serde_json::to_writer; + use std::net::{TcpListener, TcpStream}; + use std::thread; + #[derive(Debug, PartialEq, Serialize, Deserialize)] struct Message { message: String, } - let l = net::TcpListener::bind("localhost:20000").unwrap(); + let l = TcpListener::bind("localhost:20000").unwrap(); thread::spawn(|| { let l = l; @@ -1647,7 +1650,7 @@ fn test_deserialize_from_stream() { } }); - let mut stream = net::TcpStream::connect("localhost:20000").unwrap(); + let mut stream = TcpStream::connect("localhost:20000").unwrap(); let request = Message { message: "hi there".to_string(), }; From 52eec5e2ee24cd58a3be1b8d19e1e23d2ca795ca Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 15:05:15 -0800 Subject: [PATCH 116/508] Add a miri test job in CI --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f20fc0283..2a7832678 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,6 +67,16 @@ jobs: - uses: dtolnay/rust-toolchain@1.36.0 - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc + miri: + name: Miri + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: dtolnay/rust-toolchain@nightly + with: + components: miri + - run: cargo miri test + clippy: name: Clippy runs-on: ubuntu-latest From b66b0eb3224ce10a8e7cf15f3dd4b3035ecfc10f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 11 Dec 2021 15:05:19 -0800 Subject: [PATCH 117/508] Track raw pointers in miri CI run --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a7832678..588ef0d20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,6 +76,8 @@ jobs: with: components: miri - run: cargo miri test + env: + MIRIFLAGS: "-Zmiri-tag-raw-pointers" clippy: name: Clippy From 48455de6743221e1091bcff342d539b585b4431f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 12 Dec 2021 10:53:01 -0800 Subject: [PATCH 118/508] Update to itoa 1.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 0e1bdcac5..350b68fa1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ rust-version = "1.36" [dependencies] serde = { version = "1.0.100", default-features = false } indexmap = { version = "1.5", optional = true } -itoa = { version = "0.4.3", default-features = false } +itoa = "1.0" ryu = "1.0" [dev-dependencies] From 296f718e18681c52909da99f24198133e58528b2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 13 Dec 2021 14:19:58 -0800 Subject: [PATCH 119/508] Release 1.0.73 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 350b68fa1..9f7f7dade 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.72" # remember to update html_root_url +version = "1.0.73" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 50a6f8237..66129c31b 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.72")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.73")] // Ignored clippy lints #![allow( clippy::comparison_chain, From 95f67a09399d546d9ecadeb747a845a77ff309b2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 17 Dec 2021 18:22:30 -0800 Subject: [PATCH 120/508] Ignore return_self_not_must_use clippy lint error: missing `#[must_use]` attribute on a method returning `Self` --> src/map.rs:595:5 | 595 | / pub fn and_modify(self, f: F) -> Self 596 | | where 597 | | F: FnOnce(&mut Value), 598 | | { ... | 605 | | } 606 | | } | |_____^ | = note: `-D clippy::return-self-not-must-use` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use error: missing `#[must_use]` attribute on a method returning `Self` --> src/value/mod.rs:836:5 | 836 | / pub fn take(&mut self) -> Value { 837 | | mem::replace(self, Value::Null) 838 | | } | |_____^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 66129c31b..323a5d65e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -313,6 +313,7 @@ clippy::match_single_binding, clippy::needless_doctest_main, clippy::needless_late_init, + 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 From 51df12e0cdbcc193a41c682d8b3a0a6f44128ed8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 30 Dec 2021 19:43:21 -0800 Subject: [PATCH 121/508] Unconditionally import from alloc Previously serde_json supported versions of rustc older than 1.36 which didn't have a stable alloc crate. These days ever toolchain version we support has alloc. --- src/lib.rs | 53 +++++++++++++---------------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 323a5d65e..ac8677cc0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -362,56 +362,29 @@ //////////////////////////////////////////////////////////////////////////////// -#[cfg(not(feature = "std"))] extern crate alloc; /// A facade around all the types we need from the `std`, `core`, and `alloc` /// crates. This avoids elaborate import wrangling having to happen in every /// module. mod lib { - mod core { - #[cfg(not(feature = "std"))] - pub use core::*; - #[cfg(feature = "std")] - pub use std::*; - } - - pub use self::core::cell::{Cell, RefCell}; - pub use self::core::clone::{self, Clone}; - pub use self::core::convert::{self, From, Into}; - pub use self::core::default::{self, Default}; - pub use self::core::fmt::{self, Debug, Display}; - pub use self::core::hash::{self, Hash, Hasher}; - pub use self::core::iter::FusedIterator; - pub use self::core::marker::{self, PhantomData}; - pub use self::core::ops::{Bound, RangeBounds}; - pub use self::core::result::{self, Result}; - pub use self::core::{borrow, char, cmp, iter, mem, num, ops, slice, str}; + pub use core::cell::{Cell, RefCell}; + pub use core::clone::{self, Clone}; + pub use core::convert::{self, From, Into}; + pub use core::default::{self, Default}; + pub use core::fmt::{self, Debug, Display}; + pub use core::hash::{self, Hash, Hasher}; + pub use core::iter::FusedIterator; + pub use core::marker::{self, PhantomData}; + pub use core::ops::{Bound, RangeBounds}; + pub use core::result::{self, Result}; + pub use core::{borrow, char, cmp, iter, mem, num, ops, slice, str}; - #[cfg(not(feature = "std"))] pub use alloc::borrow::{Cow, ToOwned}; - #[cfg(feature = "std")] - pub use std::borrow::{Cow, ToOwned}; - - #[cfg(not(feature = "std"))] - pub use alloc::string::{String, ToString}; - #[cfg(feature = "std")] - pub use std::string::{String, ToString}; - - #[cfg(not(feature = "std"))] - pub use alloc::vec::{self, Vec}; - #[cfg(feature = "std")] - pub use std::vec::{self, Vec}; - - #[cfg(not(feature = "std"))] pub use alloc::boxed::Box; - #[cfg(feature = "std")] - pub use std::boxed::Box; - - #[cfg(not(feature = "std"))] pub use alloc::collections::{btree_map, BTreeMap}; - #[cfg(feature = "std")] - pub use std::collections::{btree_map, BTreeMap}; + pub use alloc::string::{String, ToString}; + pub use alloc::vec::{self, Vec}; #[cfg(feature = "std")] pub use std::error; From 31198f589cff820750cf249a6b899f89dab514c0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 30 Dec 2021 19:49:59 -0800 Subject: [PATCH 122/508] Switch to $(,)? in tri macro The $(...)? operation is supported since Rust 1.32. --- src/lib.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac8677cc0..c3a1f3d8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -410,15 +410,12 @@ pub use crate::value::{from_value, to_value, Map, Number, Value}; // We only use our own error type; no need for From conversions provided by the // standard library's try! macro. This reduces lines of LLVM IR by 4%. macro_rules! tri { - ($e:expr) => { + ($e:expr $(,)?) => { match $e { crate::lib::Result::Ok(val) => val, crate::lib::Result::Err(err) => return crate::lib::Result::Err(err), } }; - ($e:expr,) => { - tri!($e) - }; } #[macro_use] From 5d2cbcdd4b146e98b5aa2200de7a8ae6231bf0ba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 30 Dec 2021 19:55:10 -0800 Subject: [PATCH 123/508] Eliminate lib module --- src/de.rs | 10 ++++++++-- src/error.rs | 9 +++++++-- src/io/core.rs | 4 +++- src/lexical/bhcomp.rs | 2 +- src/lexical/bignum.rs | 2 +- src/lexical/math.rs | 3 ++- src/lexical/num.rs | 2 +- src/lexical/rounding.rs | 2 +- src/lexical/shift.rs | 2 +- src/lib.rs | 34 ++-------------------------------- src/map.rs | 17 +++++++++++++---- src/number.rs | 6 ++++-- src/raw.rs | 3 ++- src/read.rs | 7 +++++-- src/ser.rs | 6 ++++-- src/value/de.rs | 8 ++++++-- src/value/from.rs | 6 ++++-- src/value/index.rs | 9 ++++++--- src/value/mod.rs | 8 ++++++-- src/value/partial_eq.rs | 2 +- src/value/ser.rs | 6 +++++- tests/lexical.rs | 2 ++ 22 files changed, 85 insertions(+), 65 deletions(-) diff --git a/src/de.rs b/src/de.rs index a01c4767a..7eba7f48a 100644 --- a/src/de.rs +++ b/src/de.rs @@ -3,10 +3,16 @@ use crate::error::{Error, ErrorCode, Result}; #[cfg(feature = "float_roundtrip")] use crate::lexical; -use crate::lib::str::FromStr; -use crate::lib::*; use crate::number::Number; use crate::read::{self, Fused, Reference}; +use alloc::string::String; +use alloc::vec::Vec; +#[cfg(feature = "float_roundtrip")] +use core::iter; +use core::iter::FusedIterator; +use core::marker::PhantomData; +use core::result; +use core::str::FromStr; use serde::de::{self, Expected, Unexpected}; use serde::{forward_to_deserialize_any, serde_if_integer128}; diff --git a/src/error.rs b/src/error.rs index 4219d324f..6390c4368 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,9 +1,14 @@ //! When serializing or deserializing JSON goes wrong. use crate::io; -use crate::lib::str::FromStr; -use crate::lib::*; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use core::fmt::{self, Debug, Display}; +use core::result; +use core::str::FromStr; use serde::{de, ser}; +#[cfg(feature = "std")] +use std::error; /// This type represents all possible errors that can occur when serializing or /// deserializing JSON data. diff --git a/src/io/core.rs b/src/io/core.rs index 97354eb30..465ab8b24 100644 --- a/src/io/core.rs +++ b/src/io/core.rs @@ -1,7 +1,9 @@ //! Reimplements core logic and types from `std::io` in an `alloc`-friendly //! fashion. -use crate::lib::*; +use alloc::vec::Vec; +use core::fmt::{self, Display}; +use core::result; pub enum ErrorKind { Other, diff --git a/src/lexical/bhcomp.rs b/src/lexical/bhcomp.rs index 6d76c5944..1f2a7bbde 100644 --- a/src/lexical/bhcomp.rs +++ b/src/lexical/bhcomp.rs @@ -12,7 +12,7 @@ use super::float::*; use super::math::*; use super::num::*; use super::rounding::*; -use crate::lib::{cmp, mem}; +use core::{cmp, mem}; // MANTISSA diff --git a/src/lexical/bignum.rs b/src/lexical/bignum.rs index dee4e688b..f9551f534 100644 --- a/src/lexical/bignum.rs +++ b/src/lexical/bignum.rs @@ -3,7 +3,7 @@ //! Big integer type definition. use super::math::*; -use crate::lib::Vec; +use alloc::vec::Vec; /// Storage for a big integer type. #[derive(Clone, PartialEq, Eq)] diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 45fb00d15..0925d0c9a 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -9,7 +9,8 @@ use super::large_powers; use super::num::*; use super::small_powers::*; -use crate::lib::{cmp, iter, mem, Vec}; +use alloc::vec::Vec; +use core::{cmp, iter, mem}; // ALIASES // ------- diff --git a/src/lexical/num.rs b/src/lexical/num.rs index 27c78edf9..e47e00341 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -2,7 +2,7 @@ //! Utilities for Rust numbers. -use crate::lib::ops; +use core::ops; /// Precalculated values of radix**i for i in range [0, arr.len()-1]. /// Each value can be **exactly** represented as that type. diff --git a/src/lexical/rounding.rs b/src/lexical/rounding.rs index d2704c92c..6ec1292aa 100644 --- a/src/lexical/rounding.rs +++ b/src/lexical/rounding.rs @@ -5,7 +5,7 @@ use super::float::ExtendedFloat; use super::num::*; use super::shift::*; -use crate::lib::mem; +use core::mem; // MASKS diff --git a/src/lexical/shift.rs b/src/lexical/shift.rs index b0bd469f4..a0bae01e0 100644 --- a/src/lexical/shift.rs +++ b/src/lexical/shift.rs @@ -3,7 +3,7 @@ //! Bit-shift helpers. use super::float::ExtendedFloat; -use crate::lib::mem; +use core::mem; // Shift extended-precision float right `shift` bytes. #[inline] diff --git a/src/lib.rs b/src/lib.rs index c3a1f3d8c..9692ab6b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,38 +360,8 @@ #![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(docsrs, feature(doc_cfg))] -//////////////////////////////////////////////////////////////////////////////// - extern crate alloc; -/// A facade around all the types we need from the `std`, `core`, and `alloc` -/// crates. This avoids elaborate import wrangling having to happen in every -/// module. -mod lib { - pub use core::cell::{Cell, RefCell}; - pub use core::clone::{self, Clone}; - pub use core::convert::{self, From, Into}; - pub use core::default::{self, Default}; - pub use core::fmt::{self, Debug, Display}; - pub use core::hash::{self, Hash, Hasher}; - pub use core::iter::FusedIterator; - pub use core::marker::{self, PhantomData}; - pub use core::ops::{Bound, RangeBounds}; - pub use core::result::{self, Result}; - pub use core::{borrow, char, cmp, iter, mem, num, ops, slice, str}; - - pub use alloc::borrow::{Cow, ToOwned}; - pub use alloc::boxed::Box; - pub use alloc::collections::{btree_map, BTreeMap}; - pub use alloc::string::{String, ToString}; - pub use alloc::vec::{self, Vec}; - - #[cfg(feature = "std")] - pub use std::error; -} - -//////////////////////////////////////////////////////////////////////////////// - #[cfg(feature = "std")] #[doc(inline)] pub use crate::de::from_reader; @@ -412,8 +382,8 @@ pub use crate::value::{from_value, to_value, Map, Number, Value}; macro_rules! tri { ($e:expr $(,)?) => { match $e { - crate::lib::Result::Ok(val) => val, - crate::lib::Result::Err(err) => return crate::lib::Result::Err(err), + core::result::Result::Ok(val) => val, + core::result::Result::Err(err) => return core::result::Result::Err(err), } }; } diff --git a/src/map.rs b/src/map.rs index 635aeb9b0..146eb6af8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,12 +6,19 @@ //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html -use crate::lib::borrow::Borrow; -use crate::lib::iter::FromIterator; -use crate::lib::*; use crate::value::Value; +use alloc::string::String; +use core::borrow::Borrow; +use core::fmt::{self, Debug}; +use core::hash::Hash; +use core::iter::{FromIterator, FusedIterator}; +#[cfg(feature = "preserve_order")] +use core::mem; +use core::ops; use serde::de; +#[cfg(not(feature = "preserve_order"))] +use alloc::collections::{btree_map, BTreeMap}; #[cfg(feature = "preserve_order")] use indexmap::{self, IndexMap}; @@ -165,6 +172,8 @@ impl Map { 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> { @@ -202,7 +211,7 @@ impl Map { S: Into, { #[cfg(not(feature = "preserve_order"))] - use crate::lib::btree_map::Entry as EntryImpl; + use alloc::collections::btree_map::Entry as EntryImpl; #[cfg(feature = "preserve_order")] use indexmap::map::Entry as EntryImpl; diff --git a/src/number.rs b/src/number.rs index dfac7535e..b965271fd 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,6 +1,8 @@ use crate::de::ParserNumber; use crate::error::Error; -use crate::lib::*; +use core::fmt::{self, Debug, Display}; +#[cfg(not(feature = "arbitrary_precision"))] +use core::hash::{Hash, Hasher}; use serde::de::{self, Unexpected, Visitor}; use serde::{ forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, @@ -286,7 +288,7 @@ impl Number { } } -impl fmt::Display for Number { +impl Display for Number { #[cfg(not(feature = "arbitrary_precision"))] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.n { diff --git a/src/raw.rs b/src/raw.rs index a0e16305c..b853314c8 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,5 +1,6 @@ use crate::error::Error; -use crate::lib::*; +use core::fmt::{self, Debug, Display}; +use core::mem; use serde::de::value::BorrowedStrDeserializer; use serde::de::{ self, Deserialize, DeserializeSeed, Deserializer, IntoDeserializer, MapAccess, Unexpected, diff --git a/src/read.rs b/src/read.rs index 7bad5708a..c6186c235 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,6 +1,9 @@ use crate::error::{Error, ErrorCode, Result}; -use crate::lib::ops::Deref; -use crate::lib::*; +use alloc::vec::Vec; +use core::char; +use core::cmp; +use core::ops::Deref; +use core::str; #[cfg(feature = "std")] use crate::io; diff --git a/src/ser.rs b/src/ser.rs index 6637981f5..b497c56c8 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2,8 +2,10 @@ use crate::error::{Error, ErrorCode, Result}; use crate::io; -use crate::lib::num::FpCategory; -use crate::lib::*; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::fmt::{self, Display}; +use core::num::FpCategory; use serde::ser::{self, Impossible, Serialize}; use serde::serde_if_integer128; diff --git a/src/value/de.rs b/src/value/de.rs index 24ca82696..347dbee96 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -1,9 +1,13 @@ use crate::error::Error; -use crate::lib::str::FromStr; -use crate::lib::*; use crate::map::Map; use crate::number::Number; use crate::value::Value; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; +use alloc::vec::{self, Vec}; +use core::fmt; +use core::slice; +use core::str::FromStr; use serde::de::{ self, Deserialize, DeserializeSeed, EnumAccess, Expected, IntoDeserializer, MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, diff --git a/src/value/from.rs b/src/value/from.rs index 59e09fd8f..7b37ef688 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,8 +1,10 @@ use super::Value; -use crate::lib::iter::FromIterator; -use crate::lib::*; use crate::map::Map; use crate::number::Number; +use alloc::borrow::Cow; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::iter::FromIterator; #[cfg(feature = "arbitrary_precision")] use serde::serde_if_integer128; diff --git a/src/value/index.rs b/src/value/index.rs index d759a1df0..0d90a5d9a 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -1,6 +1,9 @@ use super::Value; -use crate::lib::*; use crate::map::Map; +use alloc::borrow::ToOwned; +use alloc::string::String; +use core::fmt::{self, Display}; +use core::ops; /// A type that can be used to index into a `serde_json::Value`. /// @@ -133,14 +136,14 @@ mod private { pub trait Sealed {} impl Sealed for usize {} impl Sealed for str {} - impl Sealed for super::String {} + impl Sealed for alloc::string::String {} impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} } /// Used in panic messages. struct Type<'a>(&'a Value); -impl<'a> fmt::Display for Type<'a> { +impl<'a> Display for Type<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self.0 { Value::Null => formatter.write_str("null"), diff --git a/src/value/mod.rs b/src/value/mod.rs index a28da6636..3f00c9585 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -92,7 +92,11 @@ use crate::error::Error; use crate::io; -use crate::lib::*; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt::{self, Debug, Display}; +use core::mem; +use core::str; use serde::de::DeserializeOwned; use serde::ser::Serialize; @@ -191,7 +195,7 @@ impl Debug for Value { } } -impl fmt::Display for Value { +impl Display for Value { /// Display a JSON value as a string. /// /// ``` diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 354ea5a5f..b4ef84c4f 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -1,5 +1,5 @@ use super::Value; -use crate::lib::*; +use alloc::string::String; fn eq_i64(value: &Value, other: i64) -> bool { value.as_i64().map_or(false, |i| i == other) diff --git a/src/value/ser.rs b/src/value/ser.rs index 03cb12bb1..5f59c6373 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1,8 +1,12 @@ use crate::error::{Error, ErrorCode, Result}; -use crate::lib::*; use crate::map::Map; use crate::number::Number; use crate::value::{to_value, Value}; +use alloc::borrow::ToOwned; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use core::fmt::Display; +use core::result; use serde::ser::{Impossible, Serialize}; #[cfg(feature = "arbitrary_precision")] diff --git a/tests/lexical.rs b/tests/lexical.rs index 928ad6572..6e0f07b8c 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -20,6 +20,8 @@ clippy::wildcard_imports )] +extern crate alloc; + #[path = "../src/lexical/mod.rs"] mod lexical; From 18a88dad66e25d3cda4aa16d64a716e82ac3989d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 30 Dec 2021 21:43:05 -0800 Subject: [PATCH 124/508] Avoid evaluating $c more than once in overflow macro --- src/de.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index 7eba7f48a..b4c5ef7dc 100644 --- a/src/de.rs +++ b/src/de.rs @@ -93,7 +93,9 @@ impl<'a> Deserializer> { macro_rules! overflow { ($a:ident * 10 + $b:ident, $c:expr) => { - $a >= $c / 10 && ($a > $c / 10 || $b > $c % 10) + match $c { + c => $a >= c / 10 && ($a > c / 10 || $b > c % 10), + } }; } From 012f567a389c89d74f02ae14628968febef1d3da Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 1 Jan 2022 19:03:16 +0800 Subject: [PATCH 125/508] Allow creating `RawValue`s from references to unsized values --- src/raw.rs | 2 +- tests/test.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/raw.rs b/src/raw.rs index b853314c8..f171535d7 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -282,7 +282,7 @@ impl From> for Box { #[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub fn to_raw_value(value: &T) -> Result, Error> where - T: Serialize, + T: Serialize + ?Sized, { let json_string = crate::to_string(value)?; Ok(RawValue::from_owned(json_string.into_boxed_str())) diff --git a/tests/test.rs b/tests/test.rs index 7dbc21638..4d195fb24 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2264,6 +2264,15 @@ fn test_raw_invalid_utf8() { ); } +#[cfg(feature = "raw_value")] +#[test] +fn test_serialize_unsized_value_to_raw_value() { + assert_eq!( + serde_json::value::to_raw_value("foobar").unwrap().get(), + r#""foobar""# + ); +} + #[test] fn test_borrow_in_map_key() { #[derive(Deserialize, Debug)] From ef7794f87f43d22fc74d1e38aa2cec1d6bcec12e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jan 2022 11:51:40 -0800 Subject: [PATCH 126/508] Detect warnings in CI --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 588ef0d20..be910fd38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +env: + RUSTFLAGS: -Dwarnings + jobs: test: name: Rust nightly ${{matrix.os == 'windows' && '(windows)' || ''}} From 58d40de6ed22bf0c80bbe2443dd00f265281aa2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jan 2022 12:13:38 -0800 Subject: [PATCH 127/508] Release 1.0.74 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9f7f7dade..96034225e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.73" # remember to update html_root_url +version = "1.0.74" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 9692ab6b3..47671b4bf 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.73")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.74")] // Ignored clippy lints #![allow( clippy::comparison_chain, From c79d9ad2e1da0ea3d8e21841404df78ba6f82435 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 3 Jan 2022 12:00:17 -0800 Subject: [PATCH 128/508] Run miri also with some features enabled --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index be910fd38..efef8ca4d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -81,6 +81,9 @@ jobs: - run: cargo miri test env: MIRIFLAGS: "-Zmiri-tag-raw-pointers" + - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value + env: + MIRIFLAGS: "-Zmiri-tag-raw-pointers" clippy: name: Clippy From 3f459308f5055e9a4b1b611a77dad07132011e8d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 3 Jan 2022 12:01:56 -0800 Subject: [PATCH 129/508] Set miriflags once for whole miri job --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efef8ca4d..1441c10d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,17 +73,15 @@ jobs: miri: name: Miri runs-on: ubuntu-latest + env: + MIRIFLAGS: "-Zmiri-tag-raw-pointers" steps: - uses: actions/checkout@v2 - uses: dtolnay/rust-toolchain@nightly with: components: miri - run: cargo miri test - env: - MIRIFLAGS: "-Zmiri-tag-raw-pointers" - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value - env: - MIRIFLAGS: "-Zmiri-tag-raw-pointers" clippy: name: Clippy From aebe84cb09663d0c3371273fb20d838c2e752bcd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 7 Jan 2022 18:37:02 -0800 Subject: [PATCH 130/508] Raise toolchain version for preserve_order to rust 1.46 Our indexmap dependency needs at least this version. error[E0658]: use of unstable library feature 'vec_drain_as_slice': recently added --> github.com-1ecc6299db9ec823/indexmap-1.8.0/src/map.rs:1182:30 | 1182 | let iter = self.iter.as_slice().iter().map(Bucket::refs); | ^^^^^^^^ error[E0658]: use of unstable library feature 'vec_drain_as_slice': recently added --> github.com-1ecc6299db9ec823/indexmap-1.8.0/src/set.rs:842:30 | 842 | let iter = self.iter.as_slice().iter().map(Bucket::key_ref); | ^^^^^^^^ --- .github/workflows/ci.yml | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1441c10d3..61999373f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.45.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, stable, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable @@ -44,14 +44,15 @@ jobs: with: toolchain: ${{matrix.rust}} - run: cargo check - - run: cargo check --features preserve_order - if: 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.36.0' - run: cargo check --features float_roundtrip - run: cargo check --features arbitrary_precision - run: cargo check --features raw_value - run: cargo check --features unbounded_depth + - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc + - run: cargo check --features preserve_order + if: 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.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' - name: Build without std run: | rustup target add aarch64-unknown-none @@ -62,14 +63,6 @@ jobs: --features alloc if: matrix.rust == 'stable' && matrix.os == 'ubuntu' - nostd: - name: Rust 1.36.0 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: dtolnay/rust-toolchain@1.36.0 - - run: cargo check --manifest-path tests/crate/Cargo.toml --no-default-features --features alloc - miri: name: Miri runs-on: ubuntu-latest From 66919777d0c31addd190c7a48ec78145a270294d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 14 Jan 2022 18:59:27 -0800 Subject: [PATCH 131/508] Disable buggy iter_not_returning_iterator lint https://github.com/rust-lang/rust-clippy/issues/8285 error: this method is named `iter` but its return type does not implement `Iterator` --> src/map.rs:238:5 | 238 | pub fn iter(&self) -> Iter { | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::iter-not-returning-iterator` implied by `-D clippy::pedantic` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator error: this method is named `iter_mut` but its return type does not implement `Iterator` --> src/map.rs:246:5 | 246 | pub fn iter_mut(&mut self) -> IterMut { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 47671b4bf..c36876da3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -321,6 +321,8 @@ )] // 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 0ca5a69d734c4f5a0185e93de491b45e25ca39b6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jan 2022 16:39:19 -0800 Subject: [PATCH 132/508] Add regression test for issue 845 --- tests/regression/issue845.rs | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 tests/regression/issue845.rs diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs new file mode 100644 index 000000000..dcca55694 --- /dev/null +++ b/tests/regression/issue845.rs @@ -0,0 +1,72 @@ +use serde::{Deserialize, Deserializer}; +use std::convert::TryFrom; +use std::fmt::{self, Display}; +use std::marker::PhantomData; +use std::str::FromStr; + +pub struct NumberVisitor { + marker: PhantomData, +} + +impl<'de, T> serde::de::Visitor<'de> for NumberVisitor +where + T: TryFrom + TryFrom + FromStr, + >::Error: Display, + >::Error: Display, + ::Err: Display, +{ + type Value = T; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("an integer or string") + } + + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, + { + T::try_from(v).map_err(serde::de::Error::custom) + } + + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, + { + T::try_from(v).map_err(serde::de::Error::custom) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + v.parse().map_err(serde::de::Error::custom) + } +} + +fn deserialize_integer_or_string<'de, D, T>(deserializer: D) -> Result +where + D: Deserializer<'de>, + T: TryFrom + TryFrom + FromStr, + >::Error: Display, + >::Error: Display, + ::Err: Display, +{ + deserializer.deserialize_any(NumberVisitor { + marker: PhantomData, + }) +} + +#[derive(Deserialize, Debug)] +pub struct Struct { + #[serde(deserialize_with = "deserialize_integer_or_string")] + pub i: i64, +} + +#[test] +fn test() { + let j = r#" {"i":100} "#; + println!("{:?}", serde_json::from_str::(j).unwrap()); + + let j = r#" {"i":"100"} "#; + println!("{:?}", serde_json::from_str::(j).unwrap()); +} From d54138145514758ea9475baf77cd5bf4788e9eaf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jan 2022 16:27:44 -0800 Subject: [PATCH 133/508] Deserialize small numbers as integers in arbitrary_precision --- src/de.rs | 9 +++++++++ src/lib.rs | 1 + tests/test.rs | 12 ++---------- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/de.rs b/src/de.rs index b4c5ef7dc..bf103f04b 100644 --- a/src/de.rs +++ b/src/de.rs @@ -864,6 +864,15 @@ impl<'de, R: Read<'de>> Deserializer { buf.push('-'); } self.scan_integer(&mut buf)?; + if positive { + if let Ok(unsigned) = buf.parse() { + return Ok(ParserNumber::U64(unsigned)); + } + } else { + if let Ok(signed) = buf.parse() { + return Ok(ParserNumber::I64(signed)); + } + } Ok(ParserNumber::String(buf)) } diff --git a/src/lib.rs b/src/lib.rs index c36876da3..0a815747c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -303,6 +303,7 @@ #![doc(html_root_url = "https://docs.rs/serde_json/1.0.74")] // Ignored clippy lints #![allow( + clippy::collapsible_else_if, clippy::comparison_chain, clippy::deprecated_cfg_attr, clippy::doc_markdown, diff --git a/tests/test.rs b/tests/test.rs index 054e96088..bfcb5290f 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -715,11 +715,7 @@ fn test_parse_char() { ), ( "10", - if cfg!(feature = "arbitrary_precision") { - "invalid type: number, expected a character at line 1 column 2" - } else { - "invalid type: integer `10`, expected a character at line 1 column 2" - }, + "invalid type: integer `10`, expected a character at line 1 column 2", ), ]); @@ -1203,11 +1199,7 @@ fn test_parse_struct() { test_parse_err::(&[ ( "5", - if cfg!(feature = "arbitrary_precision") { - "invalid type: number, expected struct Outer at line 1 column 1" - } else { - "invalid type: integer `5`, expected struct Outer at line 1 column 1" - }, + "invalid type: integer `5`, expected struct Outer at line 1 column 1", ), ( "\"hello\"", From a22b686f499e12146cf9a8932d2aebce7c9f2fc3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 15 Jan 2022 16:53:20 -0800 Subject: [PATCH 134/508] Release 1.0.75 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 96034225e..111dc5bb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.74" # remember to update html_root_url +version = "1.0.75" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 0a815747c..f48f05c69 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.74")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.75")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 8ecd48308ac3533fa22ff08639220b15f076b593 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 05:00:31 -0800 Subject: [PATCH 135/508] Fix imports on features +alloc +raw_value -std Closes #850. --- .github/workflows/ci.yml | 1 + src/raw.rs | 3 +++ src/value/de.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61999373f..b7e82ab8f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,6 +49,7 @@ jobs: - run: cargo check --features raw_value - run: cargo check --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,raw_value - run: cargo check --features preserve_order if: 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 diff --git a/src/raw.rs b/src/raw.rs index 8f11a06d3..b4a4ef8df 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,4 +1,7 @@ use crate::error::Error; +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::string::String; use core::fmt::{self, Debug, Display}; use core::mem; use serde::de::value::BorrowedStrDeserializer; diff --git a/src/value/de.rs b/src/value/de.rs index 347dbee96..75e49df55 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -4,6 +4,8 @@ use crate::number::Number; use crate::value::Value; use alloc::borrow::{Cow, ToOwned}; use alloc::string::String; +#[cfg(feature = "raw_value")] +use alloc::string::ToString; use alloc::vec::{self, Vec}; use core::fmt; use core::slice; From 19e9b749ce0ea21617d98d025f1351263a3901ed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 05:07:46 -0800 Subject: [PATCH 136/508] Release 1.0.76 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 111dc5bb2..7d66c8476 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.75" # remember to update html_root_url +version = "1.0.76" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index f48f05c69..97d7d3148 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.75")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.76")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 71257c5667f3005694652477c0d47c81db57edea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 11:47:14 -0800 Subject: [PATCH 137/508] Add discord invite links --- README.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 27a365372..6d8af6945 100644 --- a/README.md +++ b/README.md @@ -330,17 +330,20 @@ Benchmarks live in the [serde-rs/json-benchmark] repo. Serde is one of the most widely used Rust libraries so any place that Rustaceans congregate will be able to help you out. For chat, consider trying the -[#general] or [#beginners] channels of the unofficial community Discord, the -[#rust-usage] channel of the official Rust Project Discord, or the -[#general][zulip] stream in Zulip. For asynchronous, consider the [\[rust\] tag -on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned -weekly easy questions post, or the Rust [Discourse forum][discourse]. It's -acceptable to file a support issue in this repo but they tend not to get as many -eyes as any of the above and may get closed without a response after some time. - -[#general]: https://discord.com/channels/273534239310479360/274215136414400513 -[#beginners]: https://discord.com/channels/273534239310479360/273541522815713281 +[#rust-questions] or [#rust-beginners] channels of the unofficial community +Discord (invite: ), the [#rust-usage] or +[#beginners] channels of the official Rust Project Discord (invite: +), or the [#general][zulip] stream in Zulip. For +asynchronous, consider the [\[rust\] tag on StackOverflow][stackoverflow], the +[/r/rust] subreddit which has a pinned weekly easy questions post, or the Rust +[Discourse forum][discourse]. It's acceptable to file a support issue in this +repo but they tend not to get as many eyes as any of the above and may get +closed without a response after some time. + +[#rust-questions]: https://discord.com/channels/273534239310479360/274215136414400513 +[#rust-beginners]: https://discord.com/channels/273534239310479360/273541522815713281 [#rust-usage]: https://discord.com/channels/442252698964721669/443150878111694848 +[#beginners]: https://discord.com/channels/442252698964721669/448238009733742612 [zulip]: https://rust-lang.zulipchat.com/#narrow/stream/122651-general [stackoverflow]: https://stackoverflow.com/questions/tagged/rust [/r/rust]: https://www.reddit.com/r/rust From 4c1564931859d1ba51dccdebe0e16959697297d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 12:07:26 -0800 Subject: [PATCH 138/508] Include integration tests in published package Closes #578. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7d66c8476..0ca559218 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,6 @@ documentation = "https://docs.serde.rs/serde_json/" keywords = ["json", "serde", "serialization"] categories = ["encoding"] readme = "README.md" -include = ["build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] edition = "2018" rust-version = "1.36" From 5fe9bdd3562bf29d02d1ab798bbcff069173306b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 12:44:52 -0800 Subject: [PATCH 139/508] Improve error on compiling with neither std nor alloc Before: error: expected item, found `"serde_json requires that either `std` (default) or `alloc` feature is enabled"` --> src/features_check/error.rs:1:1 | 1 | "serde_json requires that either `std` (default) or `alloc` feature is enabled" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected item error[E0407]: method `visit_string` is not a member of trait `Visitor` --> src/raw.rs:455:5 | 455 | fn visit_string(self, s: String) -> Result | ^ ------------ help: there is an associated function with a similar name: `visit_str` | _____| | | 456 | | where 457 | | E: de::Error, 458 | | { 459 | | Ok(RawValue::from_owned(s.into_boxed_str())) 460 | | } | |_____^ not a member of trait `Visitor` error[E0046]: not all trait items implemented, missing: `collect_str` --> src/ser.rs:1376:1 | 1376 | impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, W, F> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `collect_str` in implementation | = help: implement the missing item: `fn collect_str(self, _: &T) -> core::result::Result<::Ok, ::Error> where T: Display { todo!() }` error[E0046]: not all trait items implemented, missing: `collect_str` --> src/value/ser.rs:864:1 | 864 | impl serde::ser::Serializer for RawValueEmitter { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `collect_str` in implementation | = help: implement the missing item: `fn collect_str(self, _: &T) -> core::result::Result<::Ok, ::Error> where T: Display { todo!() }` error[E0599]: no method named `visit_string` found for struct `BoxedFromString` in the current scope --> src/raw.rs:452:14 | 428 | pub struct BoxedFromString; | --------------------------- method `visit_string` not found for this ... 452 | self.visit_string(s.to_owned()) | ^^^^^^^^^^^^ method not found in `BoxedFromString` After: error: expected item, found `"serde_json requires that either `std` (default) or `alloc` feature is enabled"` --> src/features_check/error.rs:1:1 | 1 | "serde_json requires that either `std` (default) or `alloc` feature is enabled" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected item --- src/raw.rs | 3 ++- src/ser.rs | 7 +++++++ src/value/ser.rs | 7 +++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/raw.rs b/src/raw.rs index b4a4ef8df..c8377ac82 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -449,9 +449,10 @@ impl<'de> Visitor<'de> for BoxedFromString { where E: de::Error, { - self.visit_string(s.to_owned()) + Ok(RawValue::from_owned(s.to_owned().into_boxed_str())) } + #[cfg(any(feature = "std", feature = "alloc"))] fn visit_string(self, s: String) -> Result where E: de::Error, diff --git a/src/ser.rs b/src/ser.rs index b497c56c8..db77cd883 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -1548,6 +1548,13 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, ) -> Result { Err(ser::Error::custom("expected RawValue")) } + + fn collect_str(self, value: &T) -> Result + where + T: ?Sized + Display, + { + self.serialize_str(&value.to_string()) + } } /// Represents a character escape code in a type-safe manner. diff --git a/src/value/ser.rs b/src/value/ser.rs index 5f59c6373..179380a51 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -1020,4 +1020,11 @@ impl serde::ser::Serializer for RawValueEmitter { ) -> Result { Err(invalid_raw_value()) } + + fn collect_str(self, value: &T) -> Result + where + T: ?Sized + Display, + { + self.serialize_str(&value.to_string()) + } } From d8512af496a65906a93ff9191cd963e0dbc64de5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 12:52:01 -0800 Subject: [PATCH 140/508] Release 1.0.77 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ca559218..8c63f9b51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.76" # remember to update html_root_url +version = "1.0.77" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 97d7d3148..0a0a1e129 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.76")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.77")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 6a3fb68979b7e9da0f690f6f07e907045c1b30d9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 15:17:01 -0800 Subject: [PATCH 141/508] Add test of deserializing a &RawValue in map key position Currently fails with: ---- test_raw_value_in_map_key stdout ---- thread 'test_borrowed_raw_value' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: newtype struct, expected any valid JSON value", line: 1, column: 2)', tests/test.rs:2230:52 --- Cargo.toml | 1 + tests/test.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 8c63f9b51..795974fce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ ryu = "1.0" [dev-dependencies] automod = "1.0" +ref-cast = "1.0" rustversion = "1.0" serde_bytes = "0.11" serde_derive = "1.0" diff --git a/tests/test.rs b/tests/test.rs index bfcb5290f..084a7b7aa 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2193,6 +2193,46 @@ fn test_borrowed_raw_value() { assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); } +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_value_in_map_key() { + use ref_cast::RefCast; + + #[derive(RefCast)] + #[repr(transparent)] + struct RawMapKey(RawValue); + + impl<'de> Deserialize<'de> for &'de RawMapKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let raw_value = <&RawValue>::deserialize(deserializer)?; + Ok(RawMapKey::ref_cast(raw_value)) + } + } + + impl PartialEq for RawMapKey { + fn eq(&self, other: &Self) -> bool { + self.0.get() == other.0.get() + } + } + + impl Eq for RawMapKey {} + + impl Hash for RawMapKey { + fn hash(&self, hasher: &mut H) { + self.0.get().hash(hasher); + } + } + + let map_from_str: std::collections::HashMap<&RawMapKey, &RawValue> = + serde_json::from_str(r#" {"\\k":"\\v"} "#).unwrap(); + let (map_k, map_v) = map_from_str.into_iter().next().unwrap(); + assert_eq!("\"\\\\k\"", map_k.0.get()); + assert_eq!("\"\\\\v\"", map_v.get()); +} + #[cfg(feature = "raw_value")] #[test] fn test_boxed_raw_value() { From e5cdfcc7ee482238a654afbf3c0116a9f852ce79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 15:08:36 -0800 Subject: [PATCH 142/508] Support deserializing map key as &RawValue --- src/de.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index bf103f04b..ffd0d48c2 100644 --- a/src/de.rs +++ b/src/de.rs @@ -2184,10 +2184,18 @@ where } #[inline] - fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result + fn deserialize_newtype_struct(self, name: &'static str, visitor: V) -> Result where V: de::Visitor<'de>, { + #[cfg(feature = "raw_value")] + { + if name == crate::raw::TOKEN { + return self.de.deserialize_raw_value(visitor); + } + } + + let _ = name; visitor.visit_newtype_struct(self) } From 2d81cbd11302bd246db248dfb335110d1827e893 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 15:21:59 -0800 Subject: [PATCH 143/508] Move raw_value test imports to block of imports --- tests/test.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 084a7b7aa..304007a37 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -19,6 +19,8 @@ trace_macros!(true); #[macro_use] mod macros; +#[cfg(feature = "raw_value")] +use ref_cast::RefCast; use serde::de::{self, IgnoredAny, IntoDeserializer}; use serde::ser::{self, SerializeMap, SerializeSeq, Serializer}; use serde::{Deserialize, Serialize}; @@ -31,6 +33,8 @@ use serde_json::{ }; use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; +#[cfg(feature = "raw_value")] +use std::collections::HashMap; use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; use std::io; @@ -2196,8 +2200,6 @@ fn test_borrowed_raw_value() { #[cfg(feature = "raw_value")] #[test] fn test_raw_value_in_map_key() { - use ref_cast::RefCast; - #[derive(RefCast)] #[repr(transparent)] struct RawMapKey(RawValue); @@ -2226,7 +2228,7 @@ fn test_raw_value_in_map_key() { } } - let map_from_str: std::collections::HashMap<&RawMapKey, &RawValue> = + let map_from_str: HashMap<&RawMapKey, &RawValue> = serde_json::from_str(r#" {"\\k":"\\v"} "#).unwrap(); let (map_k, map_v) = map_from_str.into_iter().next().unwrap(); assert_eq!("\"\\\\k\"", map_k.0.get()); From 98cafacefe69f0c1095eccda1795b699ac711787 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 22 Jan 2022 15:57:11 -0800 Subject: [PATCH 144/508] Release 1.0.78 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 795974fce..00098582c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.77" # remember to update html_root_url +version = "1.0.78" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 0a0a1e129..03fa1d891 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.77")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.78")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From aa78d6ca4e26bca42156aa7185d35c637c38b644 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 28 Jan 2022 19:00:03 -0800 Subject: [PATCH 145/508] Resolve needless_borrow clippy lint error: this expression borrows a value the compiler would automatically borrow --> tests/../src/lexical/math.rs:597:25 | 597 | for (xi, yi) in (&mut x[xstart..]).iter_mut().zip(y.iter()) { | ^^^^^^^^^^^^^^^^^^ help: change this to: `x[xstart..]` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow --- src/lexical/math.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexical/math.rs b/src/lexical/math.rs index 0925d0c9a..37cc1d24a 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -594,7 +594,7 @@ mod large { // Iteratively add elements from y to x. let mut carry = false; - for (xi, yi) in (&mut x[xstart..]).iter_mut().zip(y.iter()) { + for (xi, yi) in x[xstart..].iter_mut().zip(y.iter()) { // Only one op of the two can overflow, since we added at max // Limb::max_value() + Limb::max_value(). Add the previous carry, // and store the current carry for the next. From 977975ee650829a1f3c232cd5f641a7011bdce1d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 28 Jan 2022 18:58:58 -0800 Subject: [PATCH 146/508] Ignore buggy ptr_arg clippy lint https://github.com/rust-lang/rust-clippy/issues/8366 error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do --> src/read.rs:65:45 | 65 | fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec) -> Result>; | ^^^^^^^^^^^^^^^ help: change this to: `&'s mut [u8]` | = note: `-D clippy::ptr-arg` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do --> src/read.rs:76:18 | 76 | scratch: &'s mut Vec, | ^^^^^^^^^^^^^^^ help: change this to: `&'s mut [u8]` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 03fa1d891..4943a8d7c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -314,6 +314,8 @@ 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, From 7025523603fe604d11b92ccd4ca314e343d3ae50 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 11 Feb 2022 20:51:15 -0800 Subject: [PATCH 147/508] Release 1.0.79 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 00098582c..1513e9b8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.78" # remember to update html_root_url +version = "1.0.79" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 4943a8d7c..63846e7b5 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.78")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.79")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 16b68b06d601f63a5f0368b3711acbe0f0f1223e Mon Sep 17 00:00:00 2001 From: Devon Sawatsky Date: Mon, 28 Feb 2022 14:19:14 -0800 Subject: [PATCH 148/508] Tweak grammar to improve readability --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6d8af6945..f137cc7b3 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ with JSON data in Rust. of your data to conform to a particular structure and want to get real work done without JSON's loosey-goosey nature tripping you up. -Serde JSON provides efficient, flexible, safe ways of converting data +Serde JSON provides efficient, flexible, and safe ways of converting data between each of these representations. ## Operating on untyped JSON values @@ -131,9 +131,9 @@ section. 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 -implement correctly, for example imagine trying to detect the presence of +implement correctly; for example, imagine trying to detect the presence of unrecognized fields in the input data. The compiler is powerless to help you -when you make a mistake, for example imagine typoing `v["name"]` as `v["nmae"]` +when you make a mistake; for example, imagine typoing `v["name"]` as `v["nmae"]` in one of the dozens of places it is used in your code. ## Parsing JSON as strongly typed data structures @@ -265,8 +265,8 @@ let john = json!({ }); ``` -This is amazingly convenient but we have the problem we had before with -`Value` which is that the IDE and Rust compiler cannot help us if we get it +This is amazingly convenient, but we have the problem we had before with +`Value`: the IDE and Rust compiler cannot help us if we get it wrong. Serde JSON provides a better way of serializing strongly-typed data structures into JSON text. @@ -321,14 +321,14 @@ way. This includes built-in Rust standard library types like `Vec` and It is fast. You should expect in the ballpark of 500 to 1000 megabytes per second deserialization and 600 to 900 megabytes per second serialization, depending on the characteristics of your data. This is competitive with the -fastest C and C++ JSON libraries or even 30% faster for many use cases. +fastest C and C++ JSON libraries (or even 30% faster for many use cases). Benchmarks live in the [serde-rs/json-benchmark] repo. [serde-rs/json-benchmark]: https://github.com/serde-rs/json-benchmark ## Getting help -Serde is one of the most widely used Rust libraries so any place that Rustaceans +Serde is one of the most widely used Rust libraries, so any place that Rustaceans congregate will be able to help you out. For chat, consider trying the [#rust-questions] or [#rust-beginners] channels of the unofficial community Discord (invite: ), the [#rust-usage] or @@ -337,7 +337,7 @@ Discord (invite: ), the [#rust-usage] or asynchronous, consider the [\[rust\] tag on StackOverflow][stackoverflow], the [/r/rust] subreddit which has a pinned weekly easy questions post, or the Rust [Discourse forum][discourse]. It's acceptable to file a support issue in this -repo but they tend not to get as many eyes as any of the above and may get +repo, but they tend not to get as many eyes as any of the above and may get closed without a response after some time. [#rust-questions]: https://discord.com/channels/273534239310479360/274215136414400513 From c5475a32db364b6102d6e3b024912ba0eb5b3116 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 28 Feb 2022 14:36:06 -0800 Subject: [PATCH 149/508] Apply readme changes from PR 864 to crate-level rustdoc --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 63846e7b5..611c9144a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -227,10 +227,10 @@ //! }); //! ``` //! -//! This is amazingly convenient but we have the problem we had before with -//! `Value` which is that the IDE and Rust compiler cannot help us if we get it -//! wrong. Serde JSON provides a better way of serializing strongly-typed data -//! structures into JSON text. +//! This is amazingly convenient, but we have the problem we had before with +//! `Value`: the IDE and Rust compiler cannot help us if we get it wrong. Serde +//! JSON provides a better way of serializing strongly-typed data structures +//! into JSON text. //! //! # Creating JSON by serializing data structures //! From 2733e635b32fa9c10d1d8ef8c224b6aa405727d3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 28 Feb 2022 14:36:39 -0800 Subject: [PATCH 150/508] Rewrap readme to 80 columns --- README.md | 88 +++++++++++++++++++++++++++---------------------------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/README.md b/README.md index ecfdbeb21..df58a47e4 100644 --- a/README.md +++ b/README.md @@ -42,22 +42,21 @@ transmit data objects consisting of key-value pairs. } ``` -There are three common ways that you might find yourself needing to work -with JSON data in Rust. - - - **As text data.** An unprocessed string of JSON data that you receive on - an HTTP endpoint, read from a file, or prepare to send to a remote - server. - - **As an untyped or loosely typed representation.** Maybe you want to - check that some JSON data is valid before passing it on, but without - knowing the structure of what it contains. Or you want to do very basic - manipulations like insert a key in a particular spot. - - **As a strongly typed Rust data structure.** When you expect all or most - of your data to conform to a particular structure and want to get real - work done without JSON's loosey-goosey nature tripping you up. - -Serde JSON provides efficient, flexible, safe ways of converting data -between each of these representations. +There are three common ways that you might find yourself needing to work with +JSON data in Rust. + + - **As text data.** An unprocessed string of JSON data that you receive on an + HTTP endpoint, read from a file, or prepare to send to a remote server. + - **As an untyped or loosely typed representation.** Maybe you want to check + that some JSON data is valid before passing it on, but without knowing the + structure of what it contains. Or you want to do very basic manipulations + like insert a key in a particular spot. + - **As a strongly typed Rust data structure.** When you expect all or most of + your data to conform to a particular structure and want to get real work done + without JSON's loosey-goosey nature tripping you up. + +Serde JSON provides efficient, flexible, safe ways of converting data between +each of these representations. ## Operating on untyped JSON values @@ -78,8 +77,8 @@ enum Value { A string of JSON data can be parsed into a `serde_json::Value` by the [`serde_json::from_str`][from_str] function. There is also [`from_slice`][from_slice] for parsing from a byte slice &[u8] and -[`from_reader`][from_reader] for parsing from any `io::Read` like a File or -a TCP stream. +[`from_reader`][from_reader] for parsing from any `io::Read` like a File or a +TCP stream.
@@ -185,20 +184,20 @@ fn typed_example() -> Result<()> { This is the same `serde_json::from_str` function as before, but this time we assign the return value to a variable of type `Person` so Serde will automatically interpret the input data as a `Person` and produce informative -error messages if the layout does not conform to what a `Person` is expected -to look like. +error messages if the layout does not conform to what a `Person` is expected to +look like. -Any type that implements Serde's `Deserialize` trait can be deserialized -this way. This includes built-in Rust standard library types like `Vec` -and `HashMap`, as well as any structs or enums annotated with +Any type that implements Serde's `Deserialize` trait can be deserialized this +way. This includes built-in Rust standard library types like `Vec` and +`HashMap`, as well as any structs or enums annotated with `#[derive(Deserialize)]`. -Once we have `p` of type `Person`, our IDE and the Rust compiler can help us -use it correctly like they do for any other Rust code. The IDE can -autocomplete field names to prevent typos, which was impossible in the -`serde_json::Value` representation. And the Rust compiler can check that -when we write `p.phones[0]`, then `p.phones` is guaranteed to be a -`Vec` so indexing into it makes sense and produces a `String`. +Once we have `p` of type `Person`, our IDE and the Rust compiler can help us use +it correctly like they do for any other Rust code. The IDE can autocomplete +field names to prevent typos, which was impossible in the `serde_json::Value` +representation. And the Rust compiler can check that when we write +`p.phones[0]`, then `p.phones` is guaranteed to be a `Vec` so indexing +into it makes sense and produces a `String`. The necessary setup for using Serde's derive macros is explained on the *[Using derive]* page of the Serde site. @@ -237,13 +236,13 @@ fn main() { } ``` -The `Value::to_string()` function converts a `serde_json::Value` into a -`String` of JSON text. +The `Value::to_string()` function converts a `serde_json::Value` into a `String` +of JSON text. -One neat thing about the `json!` macro is that variables and expressions can -be interpolated directly into the JSON value as you are building it. Serde -will check at compile time that the value you are interpolating is able to -be represented as JSON. +One neat thing about the `json!` macro is that variables and expressions can be +interpolated directly into the JSON value as you are building it. Serde will +check at compile time that the value you are interpolating is able to be +represented as JSON.
@@ -266,9 +265,9 @@ let john = json!({ ``` This is amazingly convenient, but we have the problem we had before with -`Value`: the IDE and Rust compiler cannot help us if we get it -wrong. Serde JSON provides a better way of serializing strongly-typed data -structures into JSON text. +`Value`: the IDE and Rust compiler cannot help us if we get it wrong. Serde JSON +provides a better way of serializing strongly-typed data structures into JSON +text. ## Creating JSON by serializing data structures @@ -311,10 +310,9 @@ fn print_an_address() -> Result<()> { } ``` -Any type that implements Serde's `Serialize` trait can be serialized this -way. This includes built-in Rust standard library types like `Vec` and -`HashMap`, as well as any structs or enums annotated with -`#[derive(Serialize)]`. +Any type that implements Serde's `Serialize` trait can be serialized this way. +This includes built-in Rust standard library types like `Vec` and `HashMap`, as well as any structs or enums annotated with `#[derive(Serialize)]`. ## Performance @@ -328,9 +326,9 @@ Benchmarks live in the [serde-rs/json-benchmark] repo. ## Getting help -Serde is one of the most widely used Rust libraries, so any place that Rustaceans -congregate will be able to help you out. For chat, consider trying the -[#rust-questions] or [#rust-beginners] channels of the unofficial community +Serde is one of the most widely used Rust libraries, so any place that +Rustaceans congregate will be able to help you out. For chat, consider trying +the [#rust-questions] or [#rust-beginners] channels of the unofficial community Discord (invite: ), the [#rust-usage] or [#beginners] channels of the official Rust Project Discord (invite: ), or the [#general][zulip] stream in Zulip. For From 829175e6069fb16672875f125f6afdd7c6da1dec Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 30 Mar 2022 11:33:22 -0700 Subject: [PATCH 151/508] Fix dev dependencies on serde's derive feature --- Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.toml b/Cargo.toml index 1513e9b8f..e0e4e2c75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ ryu = "1.0" automod = "1.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" From 6995bbf78444af61bcd98749c538222a18d4612f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 Apr 2022 19:06:54 -0700 Subject: [PATCH 152/508] Update workflows to actions/checkout@v3 --- .github/workflows/ci.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7e82ab8f..cd35ba4c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: matrix: os: [ubuntu, windows] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - run: cargo test - run: cargo test --features preserve_order --tests -- --skip ui --exact @@ -39,7 +39,7 @@ jobs: - rust: stable os: windows steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} @@ -70,7 +70,7 @@ jobs: env: MIRIFLAGS: "-Zmiri-tag-raw-pointers" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly with: components: miri @@ -82,7 +82,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name != 'pull_request' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@clippy - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic @@ -91,7 +91,7 @@ jobs: name: Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - run: cargo doc --features raw_value,unbounded_depth env: @@ -101,7 +101,7 @@ jobs: name: Fuzz runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - run: cargo install cargo-fuzz --debug - run: cargo fuzz build -O From aff685b8c9d20aa3773d2d4d6753e4b399768af6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Apr 2022 19:37:41 -0700 Subject: [PATCH 153/508] Drop unneeded quoting from env variable in workflows yaml --- .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 cd35ba4c1..0fd4bae57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: name: Miri runs-on: ubuntu-latest env: - MIRIFLAGS: "-Zmiri-tag-raw-pointers" + MIRIFLAGS: -Zmiri-tag-raw-pointers steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly From 52a9c050f5dcc0dc3de4825b131b8ff05219cc82 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Apr 2022 19:38:27 -0700 Subject: [PATCH 154/508] Pull miri from miri branch of dtolnay/rust-toolchain --- .github/workflows/ci.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fd4bae57..b7ecec844 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,9 +71,7 @@ jobs: MIRIFLAGS: -Zmiri-tag-raw-pointers steps: - uses: actions/checkout@v3 - - uses: dtolnay/rust-toolchain@nightly - with: - components: miri + - uses: dtolnay/rust-toolchain@miri - run: cargo miri test - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value From 585e4c5dc771d55459f2ff860bbe998660db0853 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 30 Apr 2022 12:16:22 -0700 Subject: [PATCH 155/508] Release 1.0.80 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0e4e2c75..7222bd75f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.79" # remember to update html_root_url +version = "1.0.80" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 611c9144a..3fe0f598e 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.79")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.80")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 048a64caec40e5a962f6919354fb7b6a8f0d34fd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 30 Apr 2022 20:16:31 -0700 Subject: [PATCH 156/508] Resolve type_repetition_in_bounds clippy lint error: this type has already been used as a bound predicate --> src/value/ser.rs:278:9 | 278 | T: Display, | ^^^^^^^^^^ | = note: `-D clippy::type-repetition-in-bounds` implied by `-D clippy::pedantic` = help: consider combining the bounds: `T: Sized + Display` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds error: this type has already been used as a bound predicate --> src/value/ser.rs:611:9 | 611 | T: Display, | ^^^^^^^^^^ | = help: consider combining the bounds: `T: Sized + Display` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds --- src/value/ser.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index 179380a51..098703767 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -273,9 +273,9 @@ impl serde::Serializer for Serializer { }) } - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display, + T: ?Sized + Display, { Ok(Value::String(value.to_string())) } @@ -606,9 +606,9 @@ impl serde::Serializer for MapKeySerializer { Err(key_must_be_a_string()) } - fn collect_str(self, value: &T) -> Result + fn collect_str(self, value: &T) -> Result where - T: Display, + T: ?Sized + Display, { Ok(value.to_string()) } From c0f93432cc67d2ac00b081b2f06f4f5e7374955b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 30 Apr 2022 20:19:45 -0700 Subject: [PATCH 157/508] Ignore trait_duplication_in_bounds clippy false positives https://github.com/rust-lang/rust-clippy/issues/8757 error: this trait bound is already specified in the where clause --> tests/regression/issue845.rs:13:8 | 13 | T: TryFrom + TryFrom + FromStr, | ^^^^^^^^^^^^ | = note: `-D clippy::trait-duplication-in-bounds` implied by `-D clippy::pedantic` = help: consider removing this trait bound = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds error: this trait bound is already specified in the where clause --> tests/regression/issue845.rs:14:33 | 14 | >::Error: Display, | ^^^^^^^ | = help: consider removing this trait bound = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds error: this trait bound is already specified in the where clause --> tests/regression/issue845.rs:49:8 | 49 | T: TryFrom + TryFrom + FromStr, | ^^^^^^^^^^^^ | = help: consider removing this trait bound = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds error: this trait bound is already specified in the where clause --> tests/regression/issue845.rs:50:33 | 50 | >::Error: Display, | ^^^^^^^ | = help: consider removing this trait bound = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds --- tests/regression/issue845.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs index dcca55694..56037ae66 100644 --- a/tests/regression/issue845.rs +++ b/tests/regression/issue845.rs @@ -1,3 +1,5 @@ +#![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}; From 27939bb1f99728c8b878778db8ac51b3d98f9c70 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Tue, 3 May 2022 11:57:05 -0700 Subject: [PATCH 158/508] Explicitly enable indexmap/std --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7222bd75f..795f679fb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ rust-version = "1.36" [dependencies] serde = { version = "1.0.100", default-features = false } -indexmap = { version = "1.5", optional = true } +indexmap = { version = "1.5.2", features = ["std"], optional = true } itoa = "1.0" ryu = "1.0" From 6c3dfe948a1d088198cfa82f777858502bdb39c2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 3 May 2022 12:18:24 -0700 Subject: [PATCH 159/508] Make it clearer that preserve_order implies a std dependency As indicated in the summary of #885. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 795f679fb..cae892fdb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ alloc = ["serde/alloc"] # Make serde_json::Map use a representation which maintains insertion order. # This allows data to be read into a Value and written back to a JSON string # while preserving the order of map keys in the input. -preserve_order = ["indexmap"] +preserve_order = ["indexmap", "std"] # Use sufficient precision when parsing fixed precision floats from JSON to # ensure that they maintain accuracy when round-tripped through JSON. This comes From a0ea9371cd9c163450dbdd978a64d642b0c03873 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 3 May 2022 12:19:34 -0700 Subject: [PATCH 160/508] Release 1.0.81 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cae892fdb..832881648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.80" # remember to update html_root_url +version = "1.0.81" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 3fe0f598e..37d323da5 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.80")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.81")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From aed7f7c8189eb225fabe909d059054bb3ef14540 Mon Sep 17 00:00:00 2001 From: Dezhi Wu Date: Thu, 5 May 2022 22:07:13 +0800 Subject: [PATCH 161/508] Fix a typo --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 146eb6af8..bf8f4dc9f 100644 --- a/src/map.rs +++ b/src/map.rs @@ -193,7 +193,7 @@ impl Map { } } - /// Moves all elements from other into Self, leaving other empty. + /// Moves all elements from other into self, leaving other empty. #[inline] pub fn append(&mut self, other: &mut Self) { #[cfg(feature = "preserve_order")] From 2683b1aedc80e3b761033c13bd3359b56923253c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 6 May 2022 04:01:35 -0700 Subject: [PATCH 162/508] Run miri in stricter miri-strict-provenance mode --- .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 b7ecec844..7062361cb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: name: Miri runs-on: ubuntu-latest env: - MIRIFLAGS: -Zmiri-tag-raw-pointers + MIRIFLAGS: -Zmiri-strict-provenance steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@miri From efa4c9f9bb3c1f5f6220315497a7aee28eb3c044 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 12 May 2022 21:34:52 -0700 Subject: [PATCH 163/508] Ignore unused_macro_rules warning in test macros warning: 1st rule of macro `json_str` is never used --> tests/macros/mod.rs:2:5 | 2 | ([]) => { | ^^^^ | = note: `#[warn(unused_macro_rules)]` on by default warning: 3rd rule of macro `json_str` is never used --> tests/macros/mod.rs:11:5 | 11 | ({}) => { | ^^^^ --- tests/macros/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/macros/mod.rs b/tests/macros/mod.rs index 8ac4619f7..aaf820f07 100644 --- a/tests/macros/mod.rs +++ b/tests/macros/mod.rs @@ -1,3 +1,5 @@ +#![allow(unused_macro_rules)] + macro_rules! json_str { ([]) => { "[]" From b899429dfb63d598f35f2f4c3cd96e0891472cbc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 15 May 2022 13:02:13 -0700 Subject: [PATCH 164/508] Update fuzz manifest to match cargo fuzz's new template --- fuzz/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 78925766c..c475cd9f1 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,9 +1,8 @@ [package] name = "serde_json-fuzz" version = "0.0.0" -authors = ["David Korczynski "] publish = false -edition = "2018" +edition = "2021" [package.metadata] cargo-fuzz = true @@ -15,6 +14,7 @@ serde_json = { path = ".." } [[bin]] name = "from_slice" path = "fuzz_targets/from_slice.rs" +test = false +doc = false -# Prevent this from interfering with workspaces [workspace] From f50e29656a52466fe61256bf7bd9acf9b650a50a Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Tue, 12 Apr 2022 02:08:35 +0200 Subject: [PATCH 165/508] Deserialize invalid UTF-8 into byte bufs as WTF-8 Previously #828 added support for deserializing lone leading and trailing surrogates into WTF-8 encoded bytes when deserializing a string as bytes. This commit extends this to cover the case of a leading surrogate followed by code units that are not trailing surrogates. This allows for deserialization of "\ud83c\ud83c" (two leading surrogates), or "\ud83c\u0061" (a leading surrogate followed by "a"). The docs also now make it clear that we are serializing the invalid code points as WTF-8. This reference to WTF-8 signals to the user that they can use a WTF-8 parser on the bytes to construct a valid UTF-8 string. --- src/de.rs | 5 ++++- src/read.rs | 41 +++++++++++++++++++++++++++----------- tests/test.rs | 54 +++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/de.rs b/src/de.rs index ffd0d48c2..2b6daf46f 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1570,7 +1570,10 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// /// The behavior of serde_json is specified to fail on non-UTF-8 strings /// when deserializing into Rust UTF-8 string types such as String, and - /// succeed with non-UTF-8 bytes when deserializing using this method. + /// succeed with the bytes representing the [WTF-8] encoding of code points + /// when deserializing using this method. + /// + /// [WTF-8]: https://simonsapin.github.io/wtf-8 /// /// Escape sequences are processed as usual, and for `\uXXXX` escapes it is /// still checked if the hex number represents a valid Unicode code point. diff --git a/src/read.rs b/src/read.rs index 1319d89c9..f3774e563 100644 --- a/src/read.rs +++ b/src/read.rs @@ -861,20 +861,33 @@ fn parse_escape<'de, R: Read<'de>>( b'r' => scratch.push(b'\r'), b't' => scratch.push(b'\t'), b'u' => { - fn encode_surrogate(scratch: &mut Vec, n: u16) { - scratch.extend_from_slice(&[ - (n >> 12 & 0b0000_1111) as u8 | 0b1110_0000, - (n >> 6 & 0b0011_1111) as u8 | 0b1000_0000, - (n & 0b0011_1111) as u8 | 0b1000_0000, - ]); + fn encode_wtf8(scratch: &mut Vec, cp: u16) { + match cp { + 0x0000..=0x007F => { + scratch.extend_from_slice(&[cp as u8]); + } + 0x0080..=0x07FF => { + scratch + .extend_from_slice(&[0xC0 | (cp >> 6) as u8, 0x80 | (cp & 0x3F) as u8]); + } + 0x0800..=0xFFFF => { + scratch.extend_from_slice(&[ + 0xE0 | (cp >> 12) as u8, + 0x80 | ((cp >> 6) & 0x3F) as u8, + 0x80 | (cp & 0x3F) as u8, + ]); + } + } } let c = match tri!(read.decode_hex_escape()) { n @ 0xDC00..=0xDFFF => { return if validate { + // TODO: the error message is wrong, this is a lone + // _trailing_ surrogate error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) } else { - encode_surrogate(scratch, n); + encode_wtf8(scratch, n); Ok(()) }; } @@ -889,9 +902,9 @@ fn parse_escape<'de, R: Read<'de>>( } else { return if validate { read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) + error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) } else { - encode_surrogate(scratch, n1); + encode_wtf8(scratch, n1); Ok(()) }; } @@ -903,7 +916,7 @@ fn parse_escape<'de, R: Read<'de>>( read.discard(); error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { - encode_surrogate(scratch, n1); + encode_wtf8(scratch, n1); // The \ prior to this byte started an escape sequence, // so we need to parse that now. This recursive call // does not blow the stack on malicious input because @@ -916,7 +929,13 @@ fn parse_escape<'de, R: Read<'de>>( let n2 = tri!(read.decode_hex_escape()); if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + return if validate { + error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) + } else { + encode_wtf8(scratch, n1); + encode_wtf8(scratch, n2); + Ok(()) + }; } let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; diff --git a/tests/test.rs b/tests/test.rs index b11635e75..7c5d5a78e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1713,7 +1713,8 @@ fn test_byte_buf_de() { } #[test] -fn test_byte_buf_de_lone_surrogate() { +fn test_byte_buf_de_invalid_surrogates() { + // lone leading surrogate let bytes = ByteBuf::from(vec![237, 160, 188]); let v: ByteBuf = from_str(r#""\ud83c""#).unwrap(); assert_eq!(v, bytes); @@ -1726,23 +1727,49 @@ fn test_byte_buf_de_lone_surrogate() { let v: ByteBuf = from_str(r#""\ud83c ""#).unwrap(); assert_eq!(v, bytes); - let bytes = ByteBuf::from(vec![237, 176, 129]); - let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); - assert_eq!(v, bytes); - let res = from_str::(r#""\ud83c\!""#); assert!(res.is_err()); let res = from_str::(r#""\ud83c\u""#); assert!(res.is_err()); - let res = from_str::(r#""\ud83c\ud83c""#); - assert!(res.is_err()); + // lone trailing surrogate + let bytes = ByteBuf::from(vec![237, 176, 129]); + let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by other leading surrogate + let bytes = ByteBuf::from(vec![237, 160, 188, 237, 160, 188]); + let v: ByteBuf = from_str(r#""\ud83c\ud83c""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by "a" (U+0061) in \u encoding + let bytes = ByteBuf::from(vec![237, 160, 188, 97]); + let v: ByteBuf = from_str(r#""\ud83c\u0061""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+0080 + let bytes = ByteBuf::from(vec![237, 160, 188, 194, 128]); + let v: ByteBuf = from_str(r#""\ud83c\u0080""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+FFFF + let bytes = ByteBuf::from(vec![237, 160, 188, 239, 191, 191]); + let v: ByteBuf = from_str(r#""\ud83c\uffff""#).unwrap(); + assert_eq!(v, bytes); +} + +#[test] +fn test_byte_buf_de_surrogate_pair() { + // leading surrogate followed by trailing surrogate + let bytes = ByteBuf::from(vec![240, 159, 128, 128]); + let v: ByteBuf = from_str(r#""\ud83c\udc00""#).unwrap(); + assert_eq!(v, bytes); } #[cfg(feature = "raw_value")] #[test] -fn test_raw_de_lone_surrogate() { +fn test_raw_de_invalid_surrogates() { use serde_json::value::RawValue; assert!(from_str::>(r#""\ud83c""#).is_ok()); @@ -1752,6 +1779,17 @@ fn test_raw_de_lone_surrogate() { assert!(from_str::>(r#""\udc01\!""#).is_err()); assert!(from_str::>(r#""\udc01\u""#).is_err()); assert!(from_str::>(r#""\ud83c\ud83c""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0061""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0080""#).is_ok()); + assert!(from_str::>(r#""\ud83c\uffff""#).is_ok()); +} + +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_de_surrogate_pair() { + use serde_json::value::RawValue; + + assert!(from_str::>(r#""\ud83c\udc00""#).is_ok()); } #[test] From f901012df66811354cb1d490ad59480d8fdf77b5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 21 May 2022 20:46:02 -0700 Subject: [PATCH 166/508] Ignore derive_partial_eq_without_eq clippy lint error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:65:24 | 65 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:74:24 | 74 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:565:25 | 565 | #[derive(Serialize, PartialEq, Debug)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:1278:28 | 1278 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:1395:21 | 1395 | #[derive(Debug, PartialEq, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:1415:21 | 1415 | #[derive(Debug, PartialEq, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:1425:21 | 1425 | #[derive(Debug, PartialEq, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq error: you are deriving `PartialEq` and can implement `Eq` --> tests/test.rs:1627:21 | 1627 | #[derive(Debug, PartialEq, Serialize, Deserialize)] | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index b11635e75..13d30c490 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,6 +1,7 @@ #![cfg(not(feature = "preserve_order"))] #![allow( clippy::cast_precision_loss, + clippy::derive_partial_eq_without_eq, clippy::excessive_precision, clippy::float_cmp, clippy::items_after_statements, From 845b928a63374e230e217f92ecdc3b6273456960 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 6 Jun 2022 15:57:48 -0700 Subject: [PATCH 167/508] Add actions job to notice outdated dependencies --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7062361cb..1b2749643 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,3 +103,12 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - run: cargo install cargo-fuzz --debug - run: cargo fuzz build -O + + outdated: + name: Outdated + runs-on: ubuntu-latest + if: github.event_name != 'pull_request' + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/install@cargo-outdated + - run: cargo outdated --workspace --exit-code 1 From 6b91c96f8d508fae5e3c8f69b57e5b363c769384 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 6 Jun 2022 17:04:11 -0700 Subject: [PATCH 168/508] Check for outdated deps in fuzz target --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b2749643..06bbb85f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -112,3 +112,4 @@ jobs: - uses: actions/checkout@v3 - uses: dtolnay/install@cargo-outdated - run: cargo outdated --workspace --exit-code 1 + - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 From de5c34b704c7a6cb764c5ef5742457c7f00155e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 Jun 2022 00:02:25 -0700 Subject: [PATCH 169/508] Resolve get_first clippy lint error: accessing first element with `slice.as_bytes().get(0)` --> src/error.rs:441:11 | 441 | match slice.as_bytes().get(0) { | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.as_bytes().first()` | = note: `-D clippy::get-first` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#get_first --- src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 6390c4368..796266782 100644 --- a/src/error.rs +++ b/src/error.rs @@ -438,7 +438,7 @@ fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> { } fn starts_with_digit(slice: &str) -> bool { - match slice.as_bytes().get(0) { + match slice.as_bytes().first() { None => false, Some(&byte) => byte >= b'0' && byte <= b'9', } From 45f1c4a7441b81fab9824ee46b844d64233750e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 Jun 2022 00:03:54 -0700 Subject: [PATCH 170/508] Resolve borrow_deref_ref clippy lint error: deref on an immutable reference --> src/value/mod.rs:428:45 | 428 | Value::Array(ref array) => Some(&*array), | ^^^^^^^ | = note: `-D clippy::borrow-deref-ref` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref help: if you would like to reborrow, try removing `&*` | 428 | Value::Array(ref array) => Some(array), | ~~~~~ help: if you would like to deref, try using `&**` | 428 | Value::Array(ref array) => Some(&**array), | ~~~~~~~~ --- 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 3f00c9585..a0bc31ee7 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -425,7 +425,7 @@ impl Value { /// ``` pub fn as_array(&self) -> Option<&Vec> { match *self { - Value::Array(ref array) => Some(&*array), + Value::Array(ref array) => Some(array), _ => None, } } From 05e03ee181c56bd67c5315f1f68e8392da537128 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 Jun 2022 00:15:08 -0700 Subject: [PATCH 171/508] Eliminate all use of ref keyword --- src/error.rs | 10 ++++---- src/map.rs | 14 +++++------ src/read.rs | 6 ++--- src/ser.rs | 24 +++++++------------ src/value/de.rs | 60 +++++++++++++++++++++++----------------------- src/value/index.rs | 26 ++++++++++---------- src/value/mod.rs | 56 +++++++++++++++++++++---------------------- src/value/ser.rs | 31 ++++++++++-------------- 8 files changed, 108 insertions(+), 119 deletions(-) diff --git a/src/error.rs b/src/error.rs index 796266782..1875ef08b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -284,9 +284,9 @@ impl Error { impl Display for ErrorCode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ErrorCode::Message(ref msg) => f.write_str(msg), - ErrorCode::Io(ref err) => Display::fmt(err, f), + match self { + ErrorCode::Message(msg) => f.write_str(msg), + ErrorCode::Io(err) => Display::fmt(err, f), ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"), ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"), ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"), @@ -318,8 +318,8 @@ impl Display for ErrorCode { impl serde::de::StdError for Error { #[cfg(feature = "std")] fn source(&self) -> Option<&(dyn error::Error + 'static)> { - match self.err.code { - ErrorCode::Io(ref err) => Some(err), + match &self.err.code { + ErrorCode::Io(err) => Some(err), _ => None, } } diff --git a/src/map.rs b/src/map.rs index bf8f4dc9f..87cf54566 100644 --- a/src/map.rs +++ b/src/map.rs @@ -323,10 +323,10 @@ impl Eq for Map {} /// # /// # let val = &Value::String("".to_owned()); /// # let _ = -/// match *val { -/// Value::String(ref s) => Some(s.as_str()), -/// Value::Array(ref arr) => arr[0].as_str(), -/// Value::Object(ref map) => map["type"].as_str(), +/// match val { +/// Value::String(s) => Some(s.as_str()), +/// Value::Array(arr) => arr[0].as_str(), +/// Value::Object(map) => map["type"].as_str(), /// _ => None, /// } /// # ; @@ -530,9 +530,9 @@ impl<'a> Entry<'a> { /// assert_eq!(map.entry("serde").key(), &"serde"); /// ``` pub fn key(&self) -> &String { - match *self { - Entry::Vacant(ref e) => e.key(), - Entry::Occupied(ref e) => e.key(), + match self { + Entry::Vacant(e) => e.key(), + Entry::Occupied(e) => e.key(), } } diff --git a/src/read.rs b/src/read.rs index 1319d89c9..fc3a3ca74 100644 --- a/src/read.rs +++ b/src/read.rs @@ -252,7 +252,7 @@ where Some(ch) => { #[cfg(feature = "raw_value")] { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } @@ -263,7 +263,7 @@ where Some(Ok(ch)) => { #[cfg(feature = "raw_value")] { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } @@ -298,7 +298,7 @@ where #[cfg(feature = "raw_value")] fn discard(&mut self) { if let Some(ch) = self.ch.take() { - if let Some(ref mut buf) = self.raw_buffer { + if let Some(buf) = &mut self.raw_buffer { buf.push(ch); } } diff --git a/src/ser.rs b/src/ser.rs index db77cd883..64cb00e1a 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -533,11 +533,8 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { - ref mut ser, - ref mut state, - } => { + match self { + Compound::Map { ser, state } => { tri!(ser .formatter .begin_array_value(&mut ser.writer, *state == State::First) @@ -671,11 +668,8 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { - ref mut ser, - ref mut state, - } => { + match self { + Compound::Map { ser, state } => { tri!(ser .formatter .begin_object_key(&mut ser.writer, *state == State::First) @@ -702,8 +696,8 @@ where where T: ?Sized + Serialize, { - match *self { - Compound::Map { ref mut ser, .. } => { + match self { + Compound::Map { ser, .. } => { tri!(ser .formatter .begin_object_value(&mut ser.writer) @@ -753,10 +747,10 @@ where where T: ?Sized + Serialize, { - match *self { + match self { Compound::Map { .. } => ser::SerializeMap::serialize_entry(self, key, value), #[cfg(feature = "arbitrary_precision")] - Compound::Number { ref mut ser, .. } => { + Compound::Number { ser, .. } => { if key == crate::number::TOKEN { tri!(value.serialize(NumberStrEmitter(ser))); Ok(()) @@ -765,7 +759,7 @@ where } } #[cfg(feature = "raw_value")] - Compound::RawValue { ref mut ser, .. } => { + Compound::RawValue { ser, .. } => { if key == crate::raw::TOKEN { tri!(value.serialize(RawValueStrEmitter(ser))); Ok(()) diff --git a/src/value/de.rs b/src/value/de.rs index 75e49df55..cc1d38565 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -648,8 +648,8 @@ macro_rules! deserialize_value_ref_number { where V: Visitor<'de>, { - match *self { - Value::Number(ref n) => n.deserialize_any(visitor), + match self { + Value::Number(n) => n.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -659,8 +659,8 @@ macro_rules! deserialize_value_ref_number { where V: Visitor<'de>, { - match *self { - Value::Number(ref n) => n.$method(visitor), + match self { + Value::Number(n) => n.$method(visitor), _ => self.deserialize_any(visitor), } } @@ -710,13 +710,13 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { + match self { Value::Null => visitor.visit_unit(), - Value::Bool(v) => visitor.visit_bool(v), - Value::Number(ref n) => n.deserialize_any(visitor), - Value::String(ref v) => visitor.visit_borrowed_str(v), - Value::Array(ref v) => visit_array_ref(v, visitor), - Value::Object(ref v) => visit_object_ref(v, visitor), + Value::Bool(v) => visitor.visit_bool(*v), + Value::Number(n) => n.deserialize_any(visitor), + Value::String(v) => visitor.visit_borrowed_str(v), + Value::Array(v) => visit_array_ref(v, visitor), + Value::Object(v) => visit_object_ref(v, visitor), } } @@ -755,8 +755,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - let (variant, value) = match *self { - Value::Object(ref value) => { + let (variant, value) = match self { + Value::Object(value) => { let mut iter = value.into_iter(); let (variant, value) = match iter.next() { Some(v) => v, @@ -776,8 +776,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { } (variant, Some(value)) } - Value::String(ref variant) => (variant, None), - ref other => { + Value::String(variant) => (variant, None), + other => { return Err(serde::de::Error::invalid_type( other.unexpected(), &"string or map", @@ -831,8 +831,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::String(ref v) => visitor.visit_borrowed_str(v), + match self { + Value::String(v) => visitor.visit_borrowed_str(v), _ => Err(self.invalid_type(&visitor)), } } @@ -848,9 +848,9 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::String(ref v) => visitor.visit_borrowed_str(v), - Value::Array(ref v) => visit_array_ref(v, visitor), + match self { + Value::String(v) => visitor.visit_borrowed_str(v), + Value::Array(v) => visit_array_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -883,8 +883,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Array(ref v) => visit_array_ref(v, visitor), + match self { + Value::Array(v) => visit_array_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -912,8 +912,8 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Object(ref v) => visit_object_ref(v, visitor), + match self { + Value::Object(v) => visit_object_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -927,9 +927,9 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - match *self { - Value::Array(ref v) => visit_array_ref(v, visitor), - Value::Object(ref v) => visit_object_ref(v, visitor), + match self { + Value::Array(v) => visit_array_ref(v, visitor), + Value::Object(v) => visit_object_ref(v, visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -1274,11 +1274,11 @@ impl Value { #[cold] fn unexpected(&self) -> Unexpected { - match *self { + match self { Value::Null => Unexpected::Unit, - Value::Bool(b) => Unexpected::Bool(b), - Value::Number(ref n) => n.unexpected(), - Value::String(ref s) => Unexpected::Str(s), + Value::Bool(b) => Unexpected::Bool(*b), + Value::Number(n) => n.unexpected(), + Value::String(s) => Unexpected::Str(s), Value::Array(_) => Unexpected::Seq, Value::Object(_) => Unexpected::Map, } diff --git a/src/value/index.rs b/src/value/index.rs index 0d90a5d9a..c74042b75 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -53,20 +53,20 @@ pub trait Index: private::Sealed { impl Index for usize { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { - match *v { - Value::Array(ref vec) => vec.get(*self), + match v { + Value::Array(vec) => vec.get(*self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { - match *v { - Value::Array(ref mut vec) => vec.get_mut(*self), + match v { + Value::Array(vec) => vec.get_mut(*self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { - match *v { - Value::Array(ref mut vec) => { + match v { + Value::Array(vec) => { let len = vec.len(); vec.get_mut(*self).unwrap_or_else(|| { panic!( @@ -82,23 +82,23 @@ impl Index for usize { impl Index for str { fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> { - match *v { - Value::Object(ref map) => map.get(self), + match v { + Value::Object(map) => map.get(self), _ => None, } } fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> { - match *v { - Value::Object(ref mut map) => map.get_mut(self), + match v { + Value::Object(map) => map.get_mut(self), _ => None, } } fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value { - if let Value::Null = *v { + if let Value::Null = v { *v = Value::Object(Map::new()); } - match *v { - Value::Object(ref mut map) => map.entry(self.to_owned()).or_insert(Value::Null), + match v { + Value::Object(map) => map.entry(self.to_owned()).or_insert(Value::Null), _ => panic!("cannot access key {:?} in JSON {}", self, Type(v)), } } diff --git a/src/value/mod.rs b/src/value/mod.rs index a0bc31ee7..4793e93ec 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -176,17 +176,17 @@ pub enum Value { impl Debug for Value { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match *self { + match self { Value::Null => formatter.debug_tuple("Null").finish(), - Value::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(), - Value::Number(ref v) => Debug::fmt(v, formatter), - Value::String(ref v) => formatter.debug_tuple("String").field(v).finish(), - Value::Array(ref v) => { + Value::Bool(v) => formatter.debug_tuple("Bool").field(v).finish(), + Value::Number(v) => Debug::fmt(v, formatter), + Value::String(v) => formatter.debug_tuple("String").field(v).finish(), + Value::Array(v) => { formatter.write_str("Array(")?; Debug::fmt(v, formatter)?; formatter.write_str(")") } - Value::Object(ref v) => { + Value::Object(v) => { formatter.write_str("Object(")?; Debug::fmt(v, formatter)?; formatter.write_str(")") @@ -365,8 +365,8 @@ impl Value { /// assert_eq!(v["b"].as_object(), None); /// ``` pub fn as_object(&self) -> Option<&Map> { - match *self { - Value::Object(ref map) => Some(map), + match self { + Value::Object(map) => Some(map), _ => None, } } @@ -383,8 +383,8 @@ impl Value { /// assert_eq!(v, json!({ "a": {} })); /// ``` pub fn as_object_mut(&mut self) -> Option<&mut Map> { - match *self { - Value::Object(ref mut map) => Some(map), + match self { + Value::Object(map) => Some(map), _ => None, } } @@ -424,8 +424,8 @@ impl Value { /// assert_eq!(v["b"].as_array(), None); /// ``` pub fn as_array(&self) -> Option<&Vec> { - match *self { - Value::Array(ref array) => Some(array), + match self { + Value::Array(array) => Some(array), _ => None, } } @@ -442,8 +442,8 @@ impl Value { /// assert_eq!(v, json!({ "a": [] })); /// ``` pub fn as_array_mut(&mut self) -> Option<&mut Vec> { - match *self { - Value::Array(ref mut list) => Some(list), + match self { + Value::Array(list) => Some(list), _ => None, } } @@ -491,8 +491,8 @@ impl Value { /// println!("The value is: {}", v["a"].as_str().unwrap()); /// ``` pub fn as_str(&self) -> Option<&str> { - match *self { - Value::String(ref s) => Some(s), + match self { + Value::String(s) => Some(s), _ => None, } } @@ -537,8 +537,8 @@ impl Value { /// assert!(!v["c"].is_i64()); /// ``` pub fn is_i64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_i64(), + match self { + Value::Number(n) => n.is_i64(), _ => false, } } @@ -562,8 +562,8 @@ impl Value { /// assert!(!v["c"].is_u64()); /// ``` pub fn is_u64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_u64(), + match self { + Value::Number(n) => n.is_u64(), _ => false, } } @@ -588,8 +588,8 @@ impl Value { /// assert!(!v["c"].is_f64()); /// ``` pub fn is_f64(&self) -> bool { - match *self { - Value::Number(ref n) => n.is_f64(), + match self { + Value::Number(n) => n.is_f64(), _ => false, } } @@ -608,8 +608,8 @@ impl Value { /// assert_eq!(v["c"].as_i64(), None); /// ``` pub fn as_i64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_i64(), + match self { + Value::Number(n) => n.as_i64(), _ => None, } } @@ -627,8 +627,8 @@ impl Value { /// assert_eq!(v["c"].as_u64(), None); /// ``` pub fn as_u64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_u64(), + match self { + Value::Number(n) => n.as_u64(), _ => None, } } @@ -646,8 +646,8 @@ impl Value { /// assert_eq!(v["c"].as_f64(), Some(-64.0)); /// ``` pub fn as_f64(&self) -> Option { - match *self { - Value::Number(ref n) => n.as_f64(), + match self { + Value::Number(n) => n.as_f64(), _ => None, } } diff --git a/src/value/ser.rs b/src/value/ser.rs index 098703767..c142dacbf 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -18,14 +18,14 @@ impl Serialize for Value { where S: ::serde::Serializer, { - match *self { + match self { Value::Null => serializer.serialize_unit(), - Value::Bool(b) => serializer.serialize_bool(b), - Value::Number(ref n) => n.serialize(serializer), - Value::String(ref s) => serializer.serialize_str(s), - Value::Array(ref v) => v.serialize(serializer), + Value::Bool(b) => serializer.serialize_bool(*b), + Value::Number(n) => n.serialize(serializer), + Value::String(s) => serializer.serialize_str(s), + Value::Array(v) => v.serialize(serializer), #[cfg(any(feature = "std", feature = "alloc"))] - Value::Object(ref m) => { + Value::Object(m) => { use serde::ser::SerializeMap; let mut map = tri!(serializer.serialize_map(Some(m.len()))); for (k, v) in m { @@ -384,10 +384,8 @@ impl serde::ser::SerializeMap for SerializeMap { where T: ?Sized + Serialize, { - match *self { - SerializeMap::Map { - ref mut next_key, .. - } => { + match self { + SerializeMap::Map { next_key, .. } => { *next_key = Some(tri!(key.serialize(MapKeySerializer))); Ok(()) } @@ -402,11 +400,8 @@ impl serde::ser::SerializeMap for SerializeMap { where T: ?Sized + Serialize, { - match *self { - SerializeMap::Map { - ref mut map, - ref mut next_key, - } => { + match self { + SerializeMap::Map { map, next_key } => { let key = next_key.take(); // Panic because this indicates a bug in the program rather than an // expected failure. @@ -622,10 +617,10 @@ impl serde::ser::SerializeStruct for SerializeMap { where T: ?Sized + Serialize, { - match *self { + match self { SerializeMap::Map { .. } => serde::ser::SerializeMap::serialize_entry(self, key, value), #[cfg(feature = "arbitrary_precision")] - SerializeMap::Number { ref mut out_value } => { + SerializeMap::Number { out_value } => { if key == crate::number::TOKEN { *out_value = Some(value.serialize(NumberValueEmitter)?); Ok(()) @@ -634,7 +629,7 @@ impl serde::ser::SerializeStruct for SerializeMap { } } #[cfg(feature = "raw_value")] - SerializeMap::RawValue { ref mut out_value } => { + SerializeMap::RawValue { out_value } => { if key == crate::raw::TOKEN { *out_value = Some(value.serialize(RawValueEmitter)?); Ok(()) From 3d173405c2e63d0ce2dc575288b164c2ae265f3d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 16 Jun 2022 20:50:08 -0700 Subject: [PATCH 172/508] Bump oldest rustc for preserve_order feature to 1.56.1 Required by 2021 edition in hashbrown. error: failed to download `hashbrown v0.12.1` Caused by: unable to get packages from source Caused by: failed to parse manifest at github.com-1ecc6299db9ec823/hashbrown-0.12.1/Cargo.toml Caused by: failed to parse the `edition` key Caused by: this version of Cargo is older than the `2021` edition, and only supports `2015` and `2018` editions. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 06bbb85f4..d35709ded 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, stable, 1.56.1, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable @@ -51,9 +51,9 @@ jobs: - 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,raw_value - run: cargo check --features preserve_order - if: matrix.rust != '1.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' + 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.45.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' + 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: | rustup target add aarch64-unknown-none From 1bf61e9c087c33cc13063b538678120b2ca1d8e3 Mon Sep 17 00:00:00 2001 From: Kevin Velasco Date: Fri, 24 Jun 2022 10:58:58 +0800 Subject: [PATCH 173/508] make Value be From> --- src/value/from.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/value/from.rs b/src/value/from.rs index 7b37ef688..bd137b4cf 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -268,3 +268,15 @@ impl From<()> for Value { Value::Null } } + +impl From> for Value +where + T: Into, +{ + fn from(opt: Option) -> Self { + match opt { + None => Self::Null, + Some(value) => Into::into(value), + } + } +} From b87778bf54dd4d7dc63aacd4c1d7f48e33317fa2 Mon Sep 17 00:00:00 2001 From: Kevin Velasco Date: Fri, 24 Jun 2022 11:20:05 +0800 Subject: [PATCH 174/508] don't use experimental (in 1.36) Self:: access for enums --- src/value/from.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/from.rs b/src/value/from.rs index bd137b4cf..858a6e48a 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -275,7 +275,7 @@ where { fn from(opt: Option) -> Self { match opt { - None => Self::Null, + None => Value::Null, Some(value) => Into::into(value), } } From f286714a4bdc476705f8fb95e1d2d5593d7c11fd Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 29 Jun 2022 11:46:28 -0700 Subject: [PATCH 175/508] Release 1.0.82 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 832881648..28da336a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.81" # remember to update html_root_url +version = "1.0.82" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] license = "MIT OR Apache-2.0" description = "A JSON serialization file format" diff --git a/src/lib.rs b/src/lib.rs index 37d323da5..736ab8472 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.81")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.82")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 11cb87a59d289dfda7d70df457a6682a57a4f1c8 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 29 Jun 2022 15:07:03 -0700 Subject: [PATCH 176/508] Speed up fuzz build in CI using precompiled cargo-fuzz --- .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 d35709ded..cc9689f44 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,7 +101,7 @@ jobs: steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@nightly - - run: cargo install cargo-fuzz --debug + - uses: dtolnay/install@cargo-fuzz - run: cargo fuzz build -O outdated: From 5b441a2881da9fd7d08186328849856dda001ad4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 1 Jul 2022 19:37:03 -0700 Subject: [PATCH 177/508] Ignore explicit_auto_deref clippy lint error: deref which would be done by auto-deref --> src/value/partial_eq.rs:32:22 | 32 | eq_str(self, *other) | ^^^^^^ help: try this: `other` | = note: `-D clippy::explicit-auto-deref` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref error: deref which would be done by auto-deref --> src/value/partial_eq.rs:44:23 | 44 | eq_str(other, *self) | ^^^^^ help: try this: `self` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 736ab8472..7fc351213 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -308,6 +308,7 @@ clippy::deprecated_cfg_attr, clippy::doc_markdown, clippy::excessive_precision, + clippy::explicit_auto_deref, clippy::float_cmp, clippy::manual_range_contains, clippy::match_like_matches_macro, From d1cbbb634a775821257b1dca123047a53ff81429 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 19 Jul 2022 21:20:38 -0700 Subject: [PATCH 178/508] Update ui test suite to nightly-2022-07-20 --- tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 9b83c1777..3cebc4fd3 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -4,4 +4,4 @@ error: unexpected end of macro invocation 4 | json!({ "a" }); | ^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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_value.stderr b/tests/ui/missing_value.stderr index d538d9613..a1edbc37b 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -4,4 +4,4 @@ error: unexpected end of macro invocation 4 | json!({ "a" : }); | ^^^^^^^^^^^^^^^^ missing tokens in macro arguments | - = note: this error originates in the macro `json_internal` (in Nightly builds, run with -Z macro-backtrace for more info) + = 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) From 84c157b41df3385f06fc671db0eaad9ddf1f909c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Jul 2022 15:07:22 -0700 Subject: [PATCH 179/508] Directly install aarch64-unknown-none target support --- .github/workflows/ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc9689f44..00194c20d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,9 +33,12 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, stable, 1.56.1, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: + - rust: stable + os: ubuntu + target: aarch64-unknown-none - rust: stable os: windows steps: @@ -43,6 +46,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{matrix.rust}} + targets: ${{matrix.target}} - run: cargo check - run: cargo check --features float_roundtrip - run: cargo check --features arbitrary_precision @@ -55,14 +59,8 @@ jobs: - 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: | - rustup target add aarch64-unknown-none - cargo check \ - --manifest-path tests/crate/Cargo.toml \ - --target aarch64-unknown-none \ - --no-default-features \ - --features alloc - if: matrix.rust == 'stable' && matrix.os == 'ubuntu' + run: cargo check --manifest-path tests/crate/Cargo.toml --target ${{matrix.target}} --no-default-features --features alloc + if: matrix.target miri: name: Miri From 1a433816f6cb6391e3816f3d2cabf2af2428fc23 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 20 Jul 2022 15:18:35 -0700 Subject: [PATCH 180/508] Convert i/u128 conversion to itoa instead of std::fmt --- src/number.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/number.rs b/src/number.rs index b965271fd..ca4292361 100644 --- a/src/number.rs +++ b/src/number.rs @@ -752,13 +752,15 @@ impl_from_signed!(i8, i16, i32, i64, isize); serde_if_integer128! { impl From for Number { fn from(i: i128) -> Self { - Number { n: i.to_string() } + let n = itoa::Buffer::new().format(i).to_owned(); + Number { n } } } impl From for Number { fn from(u: u128) -> Self { - Number { n: u.to_string() } + let n = itoa::Buffer::new().format(u).to_owned(); + Number { n } } } } From aac479a70a5aa64aa13d7665429844dbcb76eee9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 26 Jul 2022 21:24:07 -0700 Subject: [PATCH 181/508] =?UTF-8?q?Avoid=20cargo=201.43=E2=80=931.45=20in?= =?UTF-8?q?=20GitHub=20Actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These versions are incompatible with some recent GitHub change error: failed to get `indexmap` as a dependency of package `serde_json v1.0.82 (/home/runner/work/json/json)` Caused by: failed to load source for dependency `indexmap` Caused by: Unable to update registry `https://github.com/rust-lang/crates.io-index` Caused by: failed to fetch `https://github.com/rust-lang/crates.io-index` Caused by: error reading from the zlib stream; class=Zlib (5) --- .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 00194c20d..66597e3ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.45.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable From 01ef46e36312bc792ca94c0c80f54986f3e1708b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Jul 2022 22:11:16 -0700 Subject: [PATCH 182/508] Ignore assertions_on_result_states clippy lint error: called `assert!` with `Result::is_err` --> tests/regression/issue795.rs:53:5 | 53 | assert!(serde_json::from_str::(s).is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `serde_json::from_str::(s).unwrap_err()` | = note: `-D clippy::assertions-on-result-states` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/regression/issue795.rs:56:5 | 56 | assert!(serde_json::from_value::(j).is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `serde_json::from_value::(j).unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/stream.rs:179:9 | 179 | assert!(stream.next().unwrap().is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `stream.next().unwrap().unwrap_err()` | = note: `-D clippy::assertions-on-result-states` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:940:5 | 940 | assert!(v.is_err()); | ^^^^^^^^^^^^^^^^^^^ help: replace with: `v.unwrap_err()` | = note: `-D clippy::assertions-on-result-states` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:1735:5 | 1735 | assert!(res.is_err()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:1738:5 | 1738 | assert!(res.is_err()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:1741:5 | 1741 | assert!(res.is_err()); | ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:1930:5 | 1930 | assert!(serde_json::to_value(&map).is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `serde_json::to_value(&map).unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states error: called `assert!` with `Result::is_err` --> tests/test.rs:2005:5 | 2005 | assert!(from_str::(r#" "V"0 "#).is_err()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `from_str::(r#" "V"0 "#).unwrap_err()` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states --- tests/regression/issue795.rs | 2 ++ tests/stream.rs | 1 + tests/test.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/tests/regression/issue795.rs b/tests/regression/issue795.rs index 06b68724e..bb82852c5 100644 --- a/tests/regression/issue795.rs +++ b/tests/regression/issue795.rs @@ -1,3 +1,5 @@ +#![allow(clippy::assertions_on_result_states)] + use serde::de::{ Deserialize, Deserializer, EnumAccess, IgnoredAny, MapAccess, VariantAccess, Visitor, }; diff --git a/tests/stream.rs b/tests/stream.rs index ca54e9a15..ec6b9e3d0 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -1,4 +1,5 @@ #![cfg(not(feature = "preserve_order"))] +#![allow(clippy::assertions_on_result_states)] use serde_json::{json, Deserializer, Value}; diff --git a/tests/test.rs b/tests/test.rs index 13d30c490..0aeaff88d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,6 @@ #![cfg(not(feature = "preserve_order"))] #![allow( + clippy::assertions_on_result_states, clippy::cast_precision_loss, clippy::derive_partial_eq_without_eq, clippy::excessive_precision, From 22da79711a02d8d189069c28a8a136f88b52aa70 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 31 Jul 2022 19:19:07 -0700 Subject: [PATCH 183/508] Sort package entries in Cargo.toml --- Cargo.toml | 8 ++++---- fuzz/Cargo.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 28da336a2..7636ac9df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,14 +2,14 @@ name = "serde_json" version = "1.0.82" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] -license = "MIT OR Apache-2.0" +categories = ["encoding"] description = "A JSON serialization file format" -repository = "https://github.com/serde-rs/json" documentation = "https://docs.serde.rs/serde_json/" +edition = "2018" keywords = ["json", "serde", "serialization"] -categories = ["encoding"] +license = "MIT OR Apache-2.0" readme = "README.md" -edition = "2018" +repository = "https://github.com/serde-rs/json" rust-version = "1.36" [dependencies] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index c475cd9f1..94d74ea88 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "serde_json-fuzz" version = "0.0.0" -publish = false edition = "2021" +publish = false [package.metadata] cargo-fuzz = true From 25129334eb1829e849db356d91244f857615750e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 31 Jul 2022 19:25:47 -0700 Subject: [PATCH 184/508] Add authors to Cargo.toml --- fuzz/Cargo.toml | 1 + tests/crate/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 94d74ea88..8e925bf94 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "serde_json-fuzz" version = "0.0.0" +authors = ["David Tolnay "] edition = "2021" publish = false diff --git a/tests/crate/Cargo.toml b/tests/crate/Cargo.toml index 59da7c726..6a3ac7034 100644 --- a/tests/crate/Cargo.toml +++ b/tests/crate/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "serde_json_test" version = "0.0.0" +authors = ["David Tolnay "] edition = "2018" publish = false From db96d72c3f41d556f4fdb6bfcaf69570ed2370cb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Aug 2022 00:06:49 -0700 Subject: [PATCH 185/508] Add categories to crates.io metadata --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7636ac9df..97583abf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "serde_json" version = "1.0.82" # remember to update html_root_url authors = ["Erick Tryzelaar ", "David Tolnay "] -categories = ["encoding"] +categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" documentation = "https://docs.serde.rs/serde_json/" edition = "2018" From 2b0403f5cde64353be212f0465639e0693426480 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Aug 2022 07:08:00 -0700 Subject: [PATCH 186/508] Release 1.0.83 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 97583abf5..0a125d85f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.82" # remember to update html_root_url +version = "1.0.83" # 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 7fc351213..f96c0c18f 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.82")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.83")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 39fa675ecf75dc80a5f0ea4b6d160980390930e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 8 Aug 2022 23:01:12 -0700 Subject: [PATCH 187/508] =?UTF-8?q?Avoid=20cargo=201.45=E2=80=931.50=20in?= =?UTF-8?q?=20GitHub=20Actions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These versions are incompatible with packages published by a recent Cargo --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66597e3ec..fad6877c8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,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, 1.53.0, 1.51.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable @@ -55,9 +55,9 @@ jobs: - 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,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' + if: matrix.rust != '1.53.0' && matrix.rust != '1.51.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' + if: matrix.rust != '1.53.0' && matrix.rust != '1.51.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 a047355bcdd58bf9d2f38cb30fd1f2c99f945fea Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 12:57:19 -0700 Subject: [PATCH 188/508] Move expected value for indented debug test into test --- Cargo.toml | 1 + tests/debug.rs | 26 ++++++++++++++------------ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0a125d85f..227aa86fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ ryu = "1.0" [dev-dependencies] automod = "1.0" +indoc = "1.0" ref-cast = "1.0" rustversion = "1.0" serde = { version = "1.0.100", features = ["derive"] } diff --git a/tests/debug.rs b/tests/debug.rs index d2d8448be..07e614ee2 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -1,3 +1,4 @@ +use indoc::indoc; use serde_json::{json, Number, Value}; #[test] @@ -50,19 +51,20 @@ fn error() { assert_eq!(format!("{:?}", err), expected); } -const INDENTED_EXPECTED: &str = r#"Object({ - "array": Array([ - Number( - 0, - ), - Number( - 1, - ), - ]), -})"#; - #[test] fn indented() { let j = json!({ "array": [0, 1] }); - assert_eq!(format!("{:#?}", j), INDENTED_EXPECTED); + let expected = indoc! {r#" + Object({ + "array": Array([ + Number( + 0, + ), + Number( + 1, + ), + ]), + })"# + }; + assert_eq!(format!("{:#?}", j), expected); } From 0671603282fadef01afa08e5cfd33c3a0dca0fdb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 12:59:04 -0700 Subject: [PATCH 189/508] Cover all the Value variants in indented Debug test --- tests/debug.rs | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/debug.rs b/tests/debug.rs index 07e614ee2..ba4093897 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -53,17 +53,34 @@ fn error() { #[test] fn indented() { - let j = json!({ "array": [0, 1] }); + let j = json!({ + "Null": null, + "Bool": true, + "Number": 1, + "String": "...", + "Array": [true], + "EmptyArray": [], + "EmptyObject": {} + }); let expected = indoc! {r#" Object({ - "array": Array([ - Number( - 0, - ), - Number( - 1, + "Array": Array([ + Bool( + true, ), ]), + "Bool": Bool( + true, + ), + "EmptyArray": Array([]), + "EmptyObject": Object({}), + "Null": Null, + "Number": Number( + 1, + ), + "String": String( + "...", + ), })"# }; assert_eq!(format!("{:#?}", j), expected); From de62e3e4068acc13b3e00841dd1462264c789752 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:00:58 -0700 Subject: [PATCH 190/508] Make Debug test compatible with preserve_order --- tests/debug.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/debug.rs b/tests/debug.rs index ba4093897..e24cbabe9 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -54,13 +54,13 @@ fn error() { #[test] fn indented() { let j = json!({ - "Null": null, + "Array": [true], "Bool": true, + "EmptyArray": [], + "EmptyObject": {}, + "Null": null, "Number": 1, "String": "...", - "Array": [true], - "EmptyArray": [], - "EmptyObject": {} }); let expected = indoc! {r#" Object({ From dd6a86db078e0a850bb9b4ec9c548cbf185b8f2d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:11:25 -0700 Subject: [PATCH 191/508] Reduce unneeded parens and newlines in Debug for Value --- src/number.rs | 18 +++--------------- src/value/mod.rs | 22 ++++++++++------------ tests/debug.rs | 32 ++++++++++++-------------------- 3 files changed, 25 insertions(+), 47 deletions(-) diff --git a/src/number.rs b/src/number.rs index ca4292361..4ee052690 100644 --- a/src/number.rs +++ b/src/number.rs @@ -307,27 +307,15 @@ impl Display for Number { impl Debug for Number { #[cfg(not(feature = "arbitrary_precision"))] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let mut debug = formatter.debug_tuple("Number"); match self.n { - N::PosInt(i) => { - debug.field(&i); - } - N::NegInt(i) => { - debug.field(&i); - } - N::Float(f) => { - debug.field(&f); - } + N::PosInt(_) | N::NegInt(_) => write!(formatter, "Number({})", self), + N::Float(f) => write!(formatter, "Number({:?})", f), } - debug.finish() } #[cfg(feature = "arbitrary_precision")] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter - .debug_tuple("Number") - .field(&format_args!("{}", self.n)) - .finish() + write!(formatter, "Number({})", self) } } diff --git a/src/value/mod.rs b/src/value/mod.rs index 4793e93ec..c467df6cc 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -177,19 +177,17 @@ pub enum Value { impl Debug for Value { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self { - Value::Null => formatter.debug_tuple("Null").finish(), - Value::Bool(v) => formatter.debug_tuple("Bool").field(v).finish(), - Value::Number(v) => Debug::fmt(v, formatter), - Value::String(v) => formatter.debug_tuple("String").field(v).finish(), - Value::Array(v) => { - formatter.write_str("Array(")?; - Debug::fmt(v, formatter)?; - formatter.write_str(")") + Value::Null => formatter.write_str("Null"), + Value::Bool(boolean) => write!(formatter, "Bool({})", boolean), + Value::Number(number) => Debug::fmt(number, formatter), + Value::String(string) => write!(formatter, "String({:?})", string), + Value::Array(vec) => { + formatter.write_str("Array ")?; + Debug::fmt(vec, formatter) } - Value::Object(v) => { - formatter.write_str("Object(")?; - Debug::fmt(v, formatter)?; - formatter.write_str(")") + Value::Object(map) => { + formatter.write_str("Object ")?; + Debug::fmt(map, formatter) } } } diff --git a/tests/debug.rs b/tests/debug.rs index e24cbabe9..f99a0d921 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -36,12 +36,12 @@ fn value_string() { #[test] fn value_array() { - assert_eq!(format!("{:?}", json!([])), "Array([])"); + assert_eq!(format!("{:?}", json!([])), "Array []"); } #[test] fn value_object() { - assert_eq!(format!("{:?}", json!({})), "Object({})"); + assert_eq!(format!("{:?}", json!({})), "Object {}"); } #[test] @@ -63,25 +63,17 @@ fn indented() { "String": "...", }); let expected = indoc! {r#" - Object({ - "Array": Array([ - Bool( - true, - ), - ]), - "Bool": Bool( - true, - ), - "EmptyArray": Array([]), - "EmptyObject": Object({}), + Object { + "Array": Array [ + Bool(true), + ], + "Bool": Bool(true), + "EmptyArray": Array [], + "EmptyObject": Object {}, "Null": Null, - "Number": Number( - 1, - ), - "String": String( - "...", - ), - })"# + "Number": Number(1), + "String": String("..."), + }"# }; assert_eq!(format!("{:#?}", j), expected); } From 9e9b2b72fbb994530bdf2b87bb5ebb580df86433 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:24:24 -0700 Subject: [PATCH 192/508] =?UTF-8?q?Revert=20"Avoid=20cargo=201.45=E2=80=93?= =?UTF-8?q?1.50=20in=20GitHub=20Actions"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 39fa675ecf75dc80a5f0ea4b6d160980390930e5. --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fad6877c8..66597e3ec 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.56.1, 1.53.0, 1.51.0, 1.40.0, 1.38.0, 1.36.0] + rust: [beta, 1.56.1, 1.53.0, 1.46.0, 1.40.0, 1.38.0, 1.36.0] os: [ubuntu] include: - rust: stable @@ -55,9 +55,9 @@ jobs: - 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,raw_value - run: cargo check --features preserve_order - if: matrix.rust != '1.53.0' && matrix.rust != '1.51.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' + 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.51.0' && matrix.rust != '1.40.0' && matrix.rust != '1.38.0' && matrix.rust != '1.36.0' + 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 6b8b07365e6c5d56b2a73ada2bd938227a68d166 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:24:42 -0700 Subject: [PATCH 193/508] Release 1.0.84 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 227aa86fd..dfc971400 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.83" # remember to update html_root_url +version = "1.0.84" # 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 f96c0c18f..1fe548c12 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.83")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.84")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From de251c8198995b8bd8b770bc2bb6c9be5a7e6b53 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:37:56 -0700 Subject: [PATCH 194/508] Add test of Display for Number containing float --- tests/debug.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/debug.rs b/tests/debug.rs index f99a0d921..432a1378b 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -27,6 +27,7 @@ fn value_number() { assert_eq!(format!("{:?}", json!(1)), "Number(1)"); assert_eq!(format!("{:?}", json!(-1)), "Number(-1)"); assert_eq!(format!("{:?}", json!(1.0)), "Number(1.0)"); + assert_eq!(Number::from_f64(1.0).unwrap().to_string(), "1"); } #[test] From 8ba854166d12e1813aaf09501aa0dd87f3250aed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:38:31 -0700 Subject: [PATCH 195/508] Preserve '.0' when Displaying Number --- src/number.rs | 12 ++---------- tests/debug.rs | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/number.rs b/src/number.rs index 4ee052690..9c1acd1a8 100644 --- a/src/number.rs +++ b/src/number.rs @@ -294,7 +294,8 @@ impl Display for Number { match self.n { N::PosInt(u) => Display::fmt(&u, formatter), N::NegInt(i) => Display::fmt(&i, formatter), - N::Float(f) => Display::fmt(&f, formatter), + // Preserve `.0` on integral values, which Display hides + N::Float(f) => Debug::fmt(&f, formatter), } } @@ -305,15 +306,6 @@ impl Display for Number { } impl Debug for Number { - #[cfg(not(feature = "arbitrary_precision"))] - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match self.n { - N::PosInt(_) | N::NegInt(_) => write!(formatter, "Number({})", self), - N::Float(f) => write!(formatter, "Number({:?})", f), - } - } - - #[cfg(feature = "arbitrary_precision")] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "Number({})", self) } diff --git a/tests/debug.rs b/tests/debug.rs index 432a1378b..0535bfdd1 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -27,7 +27,7 @@ fn value_number() { assert_eq!(format!("{:?}", json!(1)), "Number(1)"); assert_eq!(format!("{:?}", json!(-1)), "Number(-1)"); assert_eq!(format!("{:?}", json!(1.0)), "Number(1.0)"); - assert_eq!(Number::from_f64(1.0).unwrap().to_string(), "1"); + assert_eq!(Number::from_f64(1.0).unwrap().to_string(), "1.0"); // not just "1" } #[test] From cb2515ba445f724c2059dc68e57e9795d4866be7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 13:54:24 -0700 Subject: [PATCH 196/508] Make Display for Number produce the same representation as serializing --- src/number.rs | 7 +++---- tests/debug.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/number.rs b/src/number.rs index 9c1acd1a8..df8819259 100644 --- a/src/number.rs +++ b/src/number.rs @@ -292,10 +292,9 @@ impl Display for Number { #[cfg(not(feature = "arbitrary_precision"))] fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match self.n { - N::PosInt(u) => Display::fmt(&u, formatter), - N::NegInt(i) => Display::fmt(&i, formatter), - // Preserve `.0` on integral values, which Display hides - N::Float(f) => Debug::fmt(&f, formatter), + N::PosInt(u) => formatter.write_str(itoa::Buffer::new().format(u)), + N::NegInt(i) => formatter.write_str(itoa::Buffer::new().format(i)), + N::Float(f) => formatter.write_str(ryu::Buffer::new().format_finite(f)), } } diff --git a/tests/debug.rs b/tests/debug.rs index 0535bfdd1..8ddcf5a38 100644 --- a/tests/debug.rs +++ b/tests/debug.rs @@ -28,6 +28,7 @@ fn value_number() { assert_eq!(format!("{:?}", json!(-1)), "Number(-1)"); assert_eq!(format!("{:?}", json!(1.0)), "Number(1.0)"); assert_eq!(Number::from_f64(1.0).unwrap().to_string(), "1.0"); // not just "1" + assert_eq!(Number::from_f64(12e40).unwrap().to_string(), "1.2e41"); } #[test] From 44d9c53e2507636c0c2afee0c9c132095dddb7df Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 21 Aug 2022 14:07:43 -0700 Subject: [PATCH 197/508] Release 1.0.85 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index dfc971400..e57825774 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.84" # remember to update html_root_url +version = "1.0.85" # 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 1fe548c12..cf4b83d19 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.84")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.85")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 5f801ea312a916b30ae9c1cc65e8e498b91199ac Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 31 Aug 2022 00:32:31 -0700 Subject: [PATCH 198/508] Ignore using destructuring assignment to wildcard pattern --- fuzz/fuzz_targets/from_slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fuzz/fuzz_targets/from_slice.rs b/fuzz/fuzz_targets/from_slice.rs index e071faff8..28bb3ee01 100644 --- a/fuzz/fuzz_targets/from_slice.rs +++ b/fuzz/fuzz_targets/from_slice.rs @@ -4,5 +4,5 @@ use libfuzzer_sys::fuzz_target; use serde_json::{from_slice, Value}; fuzz_target!(|data: &[u8]| { - let _ = from_slice::(data); + _ = from_slice::(data); }); From 7af05a9ba7460788f44de82de61e9cf5992467c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Sep 2022 22:31:34 -0700 Subject: [PATCH 199/508] Resolve needless_borrow clippy lints error: the borrowed expression implements the required traits --> src/value/ser.rs:198:60 | 198 | values.insert(String::from(variant), tri!(to_value(&value))); | ^^^^^^ help: change this to: `value` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> src/value/ser.rs:317:37 | 317 | self.vec.push(tri!(to_value(&value))); | ^^^^^^ help: change this to: `value` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> src/value/ser.rs:366:37 | 366 | self.vec.push(tri!(to_value(&value))); | ^^^^^^ help: change this to: `value` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> src/value/ser.rs:409:47 | 409 | map.insert(key, tri!(to_value(&value))); | ^^^^^^ help: change this to: `value` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> src/value/ser.rs:666:58 | 666 | self.map.insert(String::from(key), tri!(to_value(&value))); | ^^^^^^ help: change this to: `value` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> tests/test.rs:98:26 | 98 | let v = to_value(&value).unwrap(); | ^^^^^^ help: change this to: `value` | = note: `-D clippy::needless-borrow` implied by `-D clippy::all` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow error: the borrowed expression implements the required traits --> tests/test.rs:114:26 | 114 | let v = to_value(&value).unwrap(); | ^^^^^^ help: change this to: `value` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow --- src/value/ser.rs | 10 +++++----- tests/test.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index c142dacbf..37e495f65 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -195,7 +195,7 @@ impl serde::Serializer for Serializer { T: ?Sized + Serialize, { let mut values = Map::new(); - values.insert(String::from(variant), tri!(to_value(&value))); + values.insert(String::from(variant), tri!(to_value(value))); Ok(Value::Object(values)) } @@ -314,7 +314,7 @@ impl serde::ser::SerializeSeq for SerializeVec { where T: ?Sized + Serialize, { - self.vec.push(tri!(to_value(&value))); + self.vec.push(tri!(to_value(value))); Ok(()) } @@ -363,7 +363,7 @@ impl serde::ser::SerializeTupleVariant for SerializeTupleVariant { where T: ?Sized + Serialize, { - self.vec.push(tri!(to_value(&value))); + self.vec.push(tri!(to_value(value))); Ok(()) } @@ -406,7 +406,7 @@ impl serde::ser::SerializeMap for SerializeMap { // Panic because this indicates a bug in the program rather than an // expected failure. let key = key.expect("serialize_value called before serialize_key"); - map.insert(key, tri!(to_value(&value))); + map.insert(key, tri!(to_value(value))); Ok(()) } #[cfg(feature = "arbitrary_precision")] @@ -663,7 +663,7 @@ impl serde::ser::SerializeStructVariant for SerializeStructVariant { where T: ?Sized + Serialize, { - self.map.insert(String::from(key), tri!(to_value(&value))); + self.map.insert(String::from(key), tri!(to_value(value))); Ok(()) } diff --git a/tests/test.rs b/tests/test.rs index 0aeaff88d..396d55bc4 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -95,7 +95,7 @@ where let s = to_string(value).unwrap(); assert_eq!(s, out); - let v = to_value(&value).unwrap(); + let v = to_value(value).unwrap(); let s = to_string(&v).unwrap(); assert_eq!(s, out); } @@ -111,7 +111,7 @@ where let s = to_string_pretty(value).unwrap(); assert_eq!(s, out); - let v = to_value(&value).unwrap(); + let v = to_value(value).unwrap(); let s = to_string_pretty(&v).unwrap(); assert_eq!(s, out); } From 9af851709f0455affa230788246b364bb98c449a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Sep 2022 22:32:16 -0700 Subject: [PATCH 200/508] Resolve manual_string_new pedantic clippy lint in test error: empty String is being created manually --> tests/test.rs:1110:18 | 1110 | ("\"\"", "".to_string()), | ^^^^^^^^^^^^^^ help: consider using: `String::new()` | = note: `-D clippy::manual-string-new` implied by `-D clippy::pedantic` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 396d55bc4..aa5b5caa0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1107,7 +1107,7 @@ fn test_parse_string() { ]); test_parse_ok(vec![ - ("\"\"", "".to_string()), + ("\"\"", String::new()), ("\"foo\"", "foo".to_string()), (" \"foo\" ", "foo".to_string()), ("\"\\\"\"", "\"".to_string()), From d64ffdc92a80db1866cfb18575120642cbb4b763 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 2 Sep 2022 15:09:33 -0700 Subject: [PATCH 201/508] GitHub Workflows security hardening --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 66597e3ec..03d049dbc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: pull_request: schedule: [cron: "40 1 * * *"] +permissions: + contents: read + env: RUSTFLAGS: -Dwarnings From ec72b2764fe37faefa199cbddad2c080069cb23e Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Mon, 5 Sep 2022 11:03:43 +0000 Subject: [PATCH 202/508] arbitrary_precision without std --- src/number.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/number.rs b/src/number.rs index df8819259..220d7c2e5 100644 --- a/src/number.rs +++ b/src/number.rs @@ -14,6 +14,12 @@ use crate::error::ErrorCode; #[cfg(feature = "arbitrary_precision")] use serde::de::{IntoDeserializer, MapAccess}; +#[cfg(feature = "arbitrary_precision")] +use alloc::{ + borrow::ToOwned, + string::{String, ToString}, +}; + #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; From 94019a31c6036dc4ebb9afc44a214f950caf0d1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 14 Sep 2022 09:16:02 -0700 Subject: [PATCH 203/508] Remove default package.readme metadata from Cargo.toml Since cargo 1.46.0, README.md is recognized by default. --- Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e57825774..63b7dc603 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,6 @@ documentation = "https://docs.serde.rs/serde_json/" edition = "2018" keywords = ["json", "serde", "serialization"] license = "MIT OR Apache-2.0" -readme = "README.md" repository = "https://github.com/serde-rs/json" rust-version = "1.36" From 8ab65c58ea2133accb361c04e19ba36b76fea202 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 9 Oct 2022 12:41:32 -0700 Subject: [PATCH 204/508] Add a CI build for arbitrary precision without std --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 03d049dbc..55baf110d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,7 @@ jobs: - run: cargo check --features raw_value - run: cargo check --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 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' From 99d6164d558c56b0271398026ca78d7e405ecf17 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 9 Oct 2022 12:44:31 -0700 Subject: [PATCH 205/508] Reorganize number.rs imports --- src/number.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/number.rs b/src/number.rs index 220d7c2e5..3c8f6f1bf 100644 --- a/src/number.rs +++ b/src/number.rs @@ -1,25 +1,22 @@ use crate::de::ParserNumber; use crate::error::Error; +#[cfg(feature = "arbitrary_precision")] +use crate::error::ErrorCode; +#[cfg(feature = "arbitrary_precision")] +use alloc::borrow::ToOwned; +#[cfg(feature = "arbitrary_precision")] +use alloc::string::{String, ToString}; use core::fmt::{self, Debug, Display}; #[cfg(not(feature = "arbitrary_precision"))] use core::hash::{Hash, Hasher}; use serde::de::{self, Unexpected, Visitor}; +#[cfg(feature = "arbitrary_precision")] +use serde::de::{IntoDeserializer, MapAccess}; use serde::{ forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, Serializer, }; -#[cfg(feature = "arbitrary_precision")] -use crate::error::ErrorCode; -#[cfg(feature = "arbitrary_precision")] -use serde::de::{IntoDeserializer, MapAccess}; - -#[cfg(feature = "arbitrary_precision")] -use alloc::{ - borrow::ToOwned, - string::{String, ToString}, -}; - #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; From 4217e8e78896ed2657a62eeb0ccf29432450a032 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 9 Oct 2022 12:50:06 -0700 Subject: [PATCH 206/508] Release 1.0.86 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63b7dc603..f92c786b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.85" # remember to update html_root_url +version = "1.0.86" # 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 cf4b83d19..3a1983c3f 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.85")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.86")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 84d6d95e01621281f3e1228a9b35c66086b5fa9e Mon Sep 17 00:00:00 2001 From: Nathan West Date: Wed, 19 Oct 2022 16:26:34 -0400 Subject: [PATCH 207/508] Add direct support for i128 and u128 via itoa --- src/ser.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 64cb00e1a..d3d06c726 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -121,7 +121,7 @@ where serde_if_integer128! { fn serialize_i128(self, value: i128) -> Result<()> { self.formatter - .write_number_str(&mut self.writer, &value.to_string()) + .write_i128(&mut self.writer, value) .map_err(Error::io) } } @@ -165,7 +165,7 @@ where serde_if_integer128! { fn serialize_u128(self, value: u128) -> Result<()> { self.formatter - .write_number_str(&mut self.writer, &value.to_string()) + .write_u128(&mut self.writer, value) .map_err(Error::io) } } @@ -974,7 +974,7 @@ where tri!(self .ser .formatter - .write_number_str(&mut self.ser.writer, &value.to_string()) + .write_i128(&mut self.ser.writer, value) .map_err(Error::io)); tri!(self .ser @@ -1071,7 +1071,7 @@ where tri!(self .ser .formatter - .write_number_str(&mut self.ser.writer, &value.to_string()) + .write_u128(&mut self.ser.writer, value) .map_err(Error::io)); tri!(self .ser @@ -1661,6 +1661,17 @@ pub trait Formatter { writer.write_all(s.as_bytes()) } + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_i128(&mut self, writer: &mut W, value: i128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) + } + /// Writes an integer value like `123` to the specified writer. #[inline] fn write_u8(&mut self, writer: &mut W, value: u8) -> io::Result<()> @@ -1705,6 +1716,17 @@ pub trait Formatter { writer.write_all(s.as_bytes()) } + /// Writes an integer value like `123` to the specified writer. + #[inline] + fn write_u128(&mut self, writer: &mut W, value: u128) -> io::Result<()> + where + W: ?Sized + io::Write, + { + let mut buffer = itoa::Buffer::new(); + let s = buffer.format(value); + writer.write_all(s.as_bytes()) + } + /// Writes a floating point value like `-31.26e+12` to the specified writer. #[inline] fn write_f32(&mut self, writer: &mut W, value: f32) -> io::Result<()> From ab0f884fb63a20e7797501101351d8d9c6b25a1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Oct 2022 14:31:50 -0700 Subject: [PATCH 208/508] Delete 1.36+ caveat from descriptions of alloc feature --- Cargo.toml | 1 - README.md | 4 ++-- src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f92c786b5..5380001c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ std = ["serde/std"] # Provide integration for heap-allocated collections without depending on the # rest of the Rust standard library. # NOTE: Disabling both `std` *and* `alloc` features is not supported yet. -# Available on Rust 1.36+. alloc = ["serde/alloc"] # Make serde_json::Map use a representation which maintains insertion order. diff --git a/README.md b/README.md index df58a47e4..2efdfd14d 100644 --- a/README.md +++ b/README.md @@ -350,8 +350,8 @@ closed without a response after some time. ## No-std support As long as there is a memory allocator, it is possible to use serde_json without -the rest of the Rust standard library. This is supported on Rust 1.36+. Disable -the default "std" feature and enable the "alloc" feature: +the rest of the Rust standard library. Disable the default "std" feature and +enable the "alloc" feature: ```toml [dependencies] diff --git a/src/lib.rs b/src/lib.rs index 3a1983c3f..f90889702 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,8 +279,8 @@ //! # No-std support //! //! As long as there is a memory allocator, it is possible to use serde_json -//! without the rest of the Rust standard library. This is supported on Rust -//! 1.36+. Disable the default "std" feature and enable the "alloc" feature: +//! without the rest of the Rust standard library. Disable the default "std" +//! feature and enable the "alloc" feature: //! //! ```toml //! [dependencies] From ec0456ad7b2bf1c6c54cf966364f70d5a67f659f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Oct 2022 14:46:23 -0700 Subject: [PATCH 209/508] Use existing macros to implement 128-bit From impls --- src/number.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/number.rs b/src/number.rs index 3c8f6f1bf..e57b6d518 100644 --- a/src/number.rs +++ b/src/number.rs @@ -732,19 +732,8 @@ impl_from_signed!(i8, i16, i32, i64, isize); #[cfg(feature = "arbitrary_precision")] serde_if_integer128! { - impl From for Number { - fn from(i: i128) -> Self { - let n = itoa::Buffer::new().format(i).to_owned(); - Number { n } - } - } - - impl From for Number { - fn from(u: u128) -> Self { - let n = itoa::Buffer::new().format(u).to_owned(); - Number { n } - } - } + impl_from_unsigned!(u128); + impl_from_signed!(i128); } impl Number { From 37e76e0f8552260079ee4aad8fd569a48eeaf02f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Oct 2022 14:44:02 -0700 Subject: [PATCH 210/508] Assume serde_if_integer128 is true --- src/de.rs | 145 +++++++++++++++++++++------------------------- src/number.rs | 26 +++------ src/ser.rs | 117 ++++++++++++++++--------------------- src/value/de.rs | 23 +++----- src/value/from.rs | 9 +-- src/value/ser.rs | 15 ++--- 6 files changed, 138 insertions(+), 197 deletions(-) diff --git a/src/de.rs b/src/de.rs index ffd0d48c2..378b71062 100644 --- a/src/de.rs +++ b/src/de.rs @@ -14,7 +14,7 @@ use core::marker::PhantomData; use core::result; use core::str::FromStr; use serde::de::{self, Expected, Unexpected}; -use serde::{forward_to_deserialize_any, serde_if_integer128}; +use serde::forward_to_deserialize_any; #[cfg(feature = "arbitrary_precision")] use crate::number::NumberDeserializer; @@ -335,31 +335,25 @@ impl<'de, R: Read<'de>> Deserializer { } } - serde_if_integer128! { - fn scan_integer128(&mut self, buf: &mut String) -> Result<()> { - match tri!(self.next_char_or_null()) { - b'0' => { - buf.push('0'); - // There can be only one leading '0'. - match tri!(self.peek_or_null()) { - b'0'..=b'9' => { - Err(self.peek_error(ErrorCode::InvalidNumber)) - } - _ => Ok(()), - } + fn scan_integer128(&mut self, buf: &mut String) -> Result<()> { + match tri!(self.next_char_or_null()) { + b'0' => { + buf.push('0'); + // There can be only one leading '0'. + match tri!(self.peek_or_null()) { + b'0'..=b'9' => Err(self.peek_error(ErrorCode::InvalidNumber)), + _ => Ok(()), } - c @ b'1'..=b'9' => { + } + c @ b'1'..=b'9' => { + buf.push(c as char); + while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { + self.eat_char(); buf.push(c as char); - while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { - self.eat_char(); - buf.push(c as char); - } - Ok(()) - } - _ => { - Err(self.error(ErrorCode::InvalidNumber)) } + Ok(()) } + _ => Err(self.error(ErrorCode::InvalidNumber)), } } @@ -1437,67 +1431,65 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { val } - serde_if_integer128! { - fn deserialize_i128(self, visitor: V) -> Result - where - V: de::Visitor<'de>, - { - let mut buf = String::new(); - - match tri!(self.parse_whitespace()) { - Some(b'-') => { - self.eat_char(); - buf.push('-'); - } - Some(_) => {} - None => { - return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); - } - }; + fn deserialize_i128(self, visitor: V) -> Result + where + V: de::Visitor<'de>, + { + let mut buf = String::new(); - tri!(self.scan_integer128(&mut buf)); + match tri!(self.parse_whitespace()) { + Some(b'-') => { + self.eat_char(); + buf.push('-'); + } + Some(_) => {} + None => { + return Err(self.peek_error(ErrorCode::EofWhileParsingValue)); + } + }; - let value = match buf.parse() { - Ok(int) => visitor.visit_i128(int), - Err(_) => { - return Err(self.error(ErrorCode::NumberOutOfRange)); - } - }; + tri!(self.scan_integer128(&mut buf)); - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), + 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)); - } + 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)); - } - }; + let mut buf = String::new(); + tri!(self.scan_integer128(&mut buf)); - match value { - Ok(value) => Ok(value), - Err(err) => Err(self.fix_position(err)), + 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)), } } @@ -2164,15 +2156,12 @@ where 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); - - serde_if_integer128! { - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u128 => visit_u128); - } + deserialize_integer_key!(deserialize_u128 => visit_u128); #[inline] fn deserialize_option(self, visitor: V) -> Result diff --git a/src/number.rs b/src/number.rs index e57b6d518..21a76411c 100644 --- a/src/number.rs +++ b/src/number.rs @@ -12,10 +12,7 @@ use core::hash::{Hash, Hasher}; use serde::de::{self, Unexpected, Visitor}; #[cfg(feature = "arbitrary_precision")] use serde::de::{IntoDeserializer, MapAccess}; -use serde::{ - forward_to_deserialize_any, serde_if_integer128, Deserialize, Deserializer, Serialize, - Serializer, -}; +use serde::{forward_to_deserialize_any, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "arbitrary_precision")] pub(crate) const TOKEN: &str = "$serde_json::private::Number"; @@ -540,18 +537,15 @@ impl<'de> Deserializer<'de> for Number { deserialize_number!(deserialize_i16 => visit_i16); deserialize_number!(deserialize_i32 => visit_i32); deserialize_number!(deserialize_i64 => visit_i64); + deserialize_number!(deserialize_i128 => visit_i128); deserialize_number!(deserialize_u8 => visit_u8); deserialize_number!(deserialize_u16 => visit_u16); deserialize_number!(deserialize_u32 => visit_u32); deserialize_number!(deserialize_u64 => visit_u64); + deserialize_number!(deserialize_u128 => visit_u128); deserialize_number!(deserialize_f32 => visit_f32); deserialize_number!(deserialize_f64 => visit_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128 => visit_i128); - deserialize_number!(deserialize_u128 => visit_u128); - } - forward_to_deserialize_any! { bool char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier @@ -568,18 +562,15 @@ impl<'de, 'a> Deserializer<'de> for &'a Number { deserialize_number!(deserialize_i16 => visit_i16); deserialize_number!(deserialize_i32 => visit_i32); deserialize_number!(deserialize_i64 => visit_i64); + deserialize_number!(deserialize_i128 => visit_i128); deserialize_number!(deserialize_u8 => visit_u8); deserialize_number!(deserialize_u16 => visit_u16); deserialize_number!(deserialize_u32 => visit_u32); deserialize_number!(deserialize_u64 => visit_u64); + deserialize_number!(deserialize_u128 => visit_u128); deserialize_number!(deserialize_f32 => visit_f32); deserialize_number!(deserialize_f64 => visit_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128 => visit_i128); - deserialize_number!(deserialize_u128 => visit_u128); - } - forward_to_deserialize_any! { bool char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct map struct enum identifier @@ -731,10 +722,9 @@ impl_from_unsigned!(u8, u16, u32, u64, usize); impl_from_signed!(i8, i16, i32, i64, isize); #[cfg(feature = "arbitrary_precision")] -serde_if_integer128! { - impl_from_unsigned!(u128); - impl_from_signed!(i128); -} +impl_from_unsigned!(u128); +#[cfg(feature = "arbitrary_precision")] +impl_from_signed!(i128); impl Number { #[cfg(not(feature = "arbitrary_precision"))] diff --git a/src/ser.rs b/src/ser.rs index 17fe4daa3..b965f0ed1 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -7,7 +7,6 @@ use alloc::vec::Vec; use core::fmt::{self, Display}; use core::num::FpCategory; use serde::ser::{self, Impossible, Serialize}; -use serde::serde_if_integer128; /// A structure for serializing Rust values into JSON. #[cfg_attr(docsrs, doc(cfg(feature = "std")))] @@ -118,12 +117,10 @@ where Ok(()) } - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result<()> { - self.formatter - .write_i128(&mut self.writer, value) - .map_err(Error::io) - } + fn serialize_i128(self, value: i128) -> Result<()> { + self.formatter + .write_i128(&mut self.writer, value) + .map_err(Error::io) } #[inline] @@ -162,12 +159,10 @@ where Ok(()) } - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result<()> { - self.formatter - .write_u128(&mut self.writer, value) - .map_err(Error::io) - } + fn serialize_u128(self, value: u128) -> Result<()> { + self.formatter + .write_u128(&mut self.writer, value) + .map_err(Error::io) } #[inline] @@ -964,25 +959,23 @@ where Ok(()) } - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result<()> { - tri!(self - .ser - .formatter - .begin_string(&mut self.ser.writer) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .write_i128(&mut self.ser.writer, value) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) - } + fn serialize_i128(self, value: i128) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_i128(&mut self.ser.writer, value) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io)); + Ok(()) } fn serialize_u8(self, value: u8) -> Result<()> { @@ -1061,25 +1054,23 @@ where Ok(()) } - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result<()> { - tri!(self - .ser - .formatter - .begin_string(&mut self.ser.writer) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .write_u128(&mut self.ser.writer, value) - .map_err(Error::io)); - tri!(self - .ser - .formatter - .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) - } + fn serialize_u128(self, value: u128) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .write_u128(&mut self.ser.writer, value) + .map_err(Error::io)); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(Error::io)); + Ok(()) } fn serialize_f32(self, _value: f32) -> Result<()> { @@ -1218,10 +1209,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, Err(invalid_number()) } - serde_if_integer128! { - fn serialize_i128(self, _v: i128) -> Result<()> { - Err(invalid_number()) - } + fn serialize_i128(self, _v: i128) -> Result<()> { + Err(invalid_number()) } fn serialize_u8(self, _v: u8) -> Result<()> { @@ -1240,10 +1229,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, Err(invalid_number()) } - serde_if_integer128! { - fn serialize_u128(self, _v: u128) -> Result<()> { - Err(invalid_number()) - } + fn serialize_u128(self, _v: u128) -> Result<()> { + Err(invalid_number()) } fn serialize_f32(self, _v: f32) -> Result<()> { @@ -1399,10 +1386,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, Err(ser::Error::custom("expected RawValue")) } - serde_if_integer128! { - fn serialize_i128(self, _v: i128) -> Result<()> { - Err(ser::Error::custom("expected RawValue")) - } + fn serialize_i128(self, _v: i128) -> Result<()> { + Err(ser::Error::custom("expected RawValue")) } fn serialize_u8(self, _v: u8) -> Result<()> { @@ -1421,10 +1406,8 @@ impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, Err(ser::Error::custom("expected RawValue")) } - serde_if_integer128! { - fn serialize_u128(self, _v: u128) -> Result<()> { - Err(ser::Error::custom("expected RawValue")) - } + fn serialize_u128(self, _v: u128) -> Result<()> { + Err(ser::Error::custom("expected RawValue")) } fn serialize_f32(self, _v: f32) -> Result<()> { diff --git a/src/value/de.rs b/src/value/de.rs index cc1d38565..09d69c980 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -14,7 +14,7 @@ use serde::de::{ self, Deserialize, DeserializeSeed, EnumAccess, Expected, IntoDeserializer, MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, }; -use serde::{forward_to_deserialize_any, serde_if_integer128}; +use serde::forward_to_deserialize_any; #[cfg(feature = "arbitrary_precision")] use crate::number::NumberFromString; @@ -228,18 +228,15 @@ impl<'de> serde::Deserializer<'de> for Value { deserialize_number!(deserialize_i16); deserialize_number!(deserialize_i32); deserialize_number!(deserialize_i64); + deserialize_number!(deserialize_i128); deserialize_number!(deserialize_u8); deserialize_number!(deserialize_u16); deserialize_number!(deserialize_u32); deserialize_number!(deserialize_u64); + deserialize_number!(deserialize_u128); deserialize_number!(deserialize_f32); deserialize_number!(deserialize_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128); - deserialize_number!(deserialize_u128); - } - #[inline] fn deserialize_option(self, visitor: V) -> Result where @@ -724,18 +721,15 @@ impl<'de> serde::Deserializer<'de> for &'de Value { deserialize_value_ref_number!(deserialize_i16); deserialize_value_ref_number!(deserialize_i32); deserialize_value_ref_number!(deserialize_i64); + deserialize_number!(deserialize_i128); deserialize_value_ref_number!(deserialize_u8); deserialize_value_ref_number!(deserialize_u16); deserialize_value_ref_number!(deserialize_u32); deserialize_value_ref_number!(deserialize_u64); + deserialize_number!(deserialize_u128); deserialize_value_ref_number!(deserialize_f32); deserialize_value_ref_number!(deserialize_f64); - serde_if_integer128! { - deserialize_number!(deserialize_i128); - deserialize_number!(deserialize_u128); - } - fn deserialize_option(self, visitor: V) -> Result where V: Visitor<'de>, @@ -1156,15 +1150,12 @@ impl<'de> serde::Deserializer<'de> for MapKeyDeserializer<'de> { 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); - - serde_if_integer128! { - deserialize_integer_key!(deserialize_i128 => visit_i128); - deserialize_integer_key!(deserialize_u128 => visit_u128); - } + deserialize_integer_key!(deserialize_u128 => visit_u128); #[inline] fn deserialize_option(self, visitor: V) -> Result diff --git a/src/value/from.rs b/src/value/from.rs index 858a6e48a..c5a6a3960 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -6,9 +6,6 @@ use alloc::string::{String, ToString}; use alloc::vec::Vec; use core::iter::FromIterator; -#[cfg(feature = "arbitrary_precision")] -use serde::serde_if_integer128; - macro_rules! from_integer { ($($ty:ident)*) => { $( @@ -27,10 +24,8 @@ from_integer! { } #[cfg(feature = "arbitrary_precision")] -serde_if_integer128! { - from_integer! { - i128 u128 - } +from_integer! { + i128 u128 } impl From for Value { diff --git a/src/value/ser.rs b/src/value/ser.rs index 37e495f65..892a63d5f 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -9,9 +9,6 @@ use core::fmt::Display; use core::result; use serde::ser::{Impossible, Serialize}; -#[cfg(feature = "arbitrary_precision")] -use serde::serde_if_integer128; - impl Serialize for Value { #[inline] fn serialize(&self, serializer: S) -> result::Result @@ -96,10 +93,8 @@ impl serde::Serializer for Serializer { } #[cfg(feature = "arbitrary_precision")] - serde_if_integer128! { - fn serialize_i128(self, value: i128) -> Result { - Ok(Value::Number(value.into())) - } + fn serialize_i128(self, value: i128) -> Result { + Ok(Value::Number(value.into())) } #[inline] @@ -123,10 +118,8 @@ impl serde::Serializer for Serializer { } #[cfg(feature = "arbitrary_precision")] - serde_if_integer128! { - fn serialize_u128(self, value: u128) -> Result { - Ok(Value::Number(value.into())) - } + fn serialize_u128(self, value: u128) -> Result { + Ok(Value::Number(value.into())) } #[inline] From 31070349afe65bd2418450184a88a1463579f0af Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Oct 2022 14:53:58 -0700 Subject: [PATCH 211/508] Eliminate needless 'tri!' from Serializer impl --- src/ser.rs | 259 ++++++++++++++++++----------------------------------- 1 file changed, 88 insertions(+), 171 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index b965f0ed1..80c2deb0c 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -74,47 +74,37 @@ where #[inline] fn serialize_bool(self, value: bool) -> Result<()> { - tri!(self - .formatter + self.formatter .write_bool(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i8(self, value: i8) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i8(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i16(self, value: i16) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i16(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i32(self, value: i32) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i32(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_i64(self, value: i64) -> Result<()> { - tri!(self - .formatter + self.formatter .write_i64(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i128(self, value: i128) -> Result<()> { @@ -125,38 +115,30 @@ where #[inline] fn serialize_u8(self, value: u8) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u8(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u16(self, value: u16) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u16(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u32(self, value: u32) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u32(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] fn serialize_u64(self, value: u64) -> Result<()> { - tri!(self - .formatter + self.formatter .write_u64(&mut self.writer, value) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u128(self, value: u128) -> Result<()> { @@ -168,39 +150,29 @@ where #[inline] fn serialize_f32(self, value: f32) -> Result<()> { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => { - tri!(self - .formatter - .write_null(&mut self.writer) - .map_err(Error::io)); - } - _ => { - tri!(self - .formatter - .write_f32(&mut self.writer, value) - .map_err(Error::io)); - } + FpCategory::Nan | FpCategory::Infinite => self + .formatter + .write_null(&mut self.writer) + .map_err(Error::io), + _ => self + .formatter + .write_f32(&mut self.writer, value) + .map_err(Error::io), } - Ok(()) } #[inline] fn serialize_f64(self, value: f64) -> Result<()> { match value.classify() { - FpCategory::Nan | FpCategory::Infinite => { - tri!(self - .formatter - .write_null(&mut self.writer) - .map_err(Error::io)); - } - _ => { - tri!(self - .formatter - .write_f64(&mut self.writer, value) - .map_err(Error::io)); - } + FpCategory::Nan | FpCategory::Infinite => self + .formatter + .write_null(&mut self.writer) + .map_err(Error::io), + _ => self + .formatter + .write_f64(&mut self.writer, value) + .map_err(Error::io), } - Ok(()) } #[inline] @@ -212,8 +184,7 @@ where #[inline] fn serialize_str(self, value: &str) -> Result<()> { - tri!(format_escaped_str(&mut self.writer, &mut self.formatter, value).map_err(Error::io)); - Ok(()) + format_escaped_str(&mut self.writer, &mut self.formatter, value).map_err(Error::io) } #[inline] @@ -228,11 +199,9 @@ where #[inline] fn serialize_unit(self) -> Result<()> { - tri!(self - .formatter + self.formatter .write_null(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] @@ -292,11 +261,9 @@ where .formatter .end_object_value(&mut self.writer) .map_err(Error::io)); - tri!(self - .formatter + self.formatter .end_object(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[inline] @@ -485,11 +452,9 @@ where } } } - tri!(self - .formatter + self.formatter .end_string(&mut self.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } } @@ -536,11 +501,9 @@ where .map_err(Error::io)); *state = State::Rest; tri!(value.serialize(&mut **ser)); - tri!(ser - .formatter + ser.formatter .end_array_value(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -552,13 +515,10 @@ where #[inline] fn end(self) -> Result<()> { match self { - Compound::Map { ser, state } => { - match state { - State::Empty => {} - _ => tri!(ser.formatter.end_array(&mut ser.writer).map_err(Error::io)), - } - Ok(()) - } + Compound::Map { ser, state } => match state { + State::Empty => Ok(()), + _ => ser.formatter.end_array(&mut ser.writer).map_err(Error::io), + }, #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), #[cfg(feature = "raw_value")] @@ -639,8 +599,7 @@ where .formatter .end_object_value(&mut ser.writer) .map_err(Error::io)); - tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)); - Ok(()) + ser.formatter.end_object(&mut ser.writer).map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -673,11 +632,9 @@ where tri!(key.serialize(MapKeySerializer { ser: *ser })); - tri!(ser - .formatter + ser.formatter .end_object_key(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -698,11 +655,9 @@ where .begin_object_value(&mut ser.writer) .map_err(Error::io)); tri!(value.serialize(&mut **ser)); - tri!(ser - .formatter + ser.formatter .end_object_value(&mut ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -714,13 +669,10 @@ where #[inline] fn end(self) -> Result<()> { match self { - Compound::Map { ser, state } => { - match state { - State::Empty => {} - _ => tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)), - } - Ok(()) - } + Compound::Map { ser, state } => match state { + State::Empty => Ok(()), + _ => ser.formatter.end_object(&mut ser.writer).map_err(Error::io), + }, #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), #[cfg(feature = "raw_value")] @@ -747,8 +699,7 @@ where #[cfg(feature = "arbitrary_precision")] Compound::Number { ser, .. } => { if key == crate::number::TOKEN { - tri!(value.serialize(NumberStrEmitter(ser))); - Ok(()) + value.serialize(NumberStrEmitter(ser)) } else { Err(invalid_number()) } @@ -756,8 +707,7 @@ where #[cfg(feature = "raw_value")] Compound::RawValue { ser, .. } => { if key == crate::raw::TOKEN { - tri!(value.serialize(RawValueStrEmitter(ser))); - Ok(()) + value.serialize(RawValueStrEmitter(ser)) } else { Err(invalid_raw_value()) } @@ -811,8 +761,7 @@ where .formatter .end_object_value(&mut ser.writer) .map_err(Error::io)); - tri!(ser.formatter.end_object(&mut ser.writer).map_err(Error::io)); - Ok(()) + ser.formatter.end_object(&mut ser.writer).map_err(Error::io) } #[cfg(feature = "arbitrary_precision")] Compound::Number { .. } => unreachable!(), @@ -894,12 +843,10 @@ where .formatter .write_i8(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i16(self, value: i16) -> Result<()> { @@ -913,12 +860,10 @@ where .formatter .write_i16(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i32(self, value: i32) -> Result<()> { @@ -932,12 +877,10 @@ where .formatter .write_i32(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i64(self, value: i64) -> Result<()> { @@ -951,12 +894,10 @@ where .formatter .write_i64(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_i128(self, value: i128) -> Result<()> { @@ -970,12 +911,10 @@ where .formatter .write_i128(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u8(self, value: u8) -> Result<()> { @@ -989,12 +928,10 @@ where .formatter .write_u8(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u16(self, value: u16) -> Result<()> { @@ -1008,12 +945,10 @@ where .formatter .write_u16(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u32(self, value: u32) -> Result<()> { @@ -1027,12 +962,10 @@ where .formatter .write_u32(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u64(self, value: u64) -> Result<()> { @@ -1046,12 +979,10 @@ where .formatter .write_u64(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_u128(self, value: u128) -> Result<()> { @@ -1065,12 +996,10 @@ where .formatter .write_u128(&mut self.ser.writer, value) .map_err(Error::io)); - tri!(self - .ser + self.ser .formatter .end_string(&mut self.ser.writer) - .map_err(Error::io)); - Ok(()) + .map_err(Error::io) } fn serialize_f32(self, _value: f32) -> Result<()> { @@ -1990,13 +1919,8 @@ impl<'a> Formatter for PrettyFormatter<'a> { where W: ?Sized + io::Write, { - if first { - tri!(writer.write_all(b"\n")); - } else { - tri!(writer.write_all(b",\n")); - } - tri!(indent(writer, self.current_indent, self.indent)); - Ok(()) + tri!(writer.write_all(if first { b"\n" } else { b",\n" })); + indent(writer, self.current_indent, self.indent) } #[inline] @@ -2038,11 +1962,7 @@ impl<'a> Formatter for PrettyFormatter<'a> { where W: ?Sized + io::Write, { - if first { - tri!(writer.write_all(b"\n")); - } else { - tri!(writer.write_all(b",\n")); - } + tri!(writer.write_all(if first { b"\n" } else { b",\n" })); indent(writer, self.current_indent, self.indent) } @@ -2071,8 +1991,7 @@ where { tri!(formatter.begin_string(writer)); tri!(format_escaped_str_contents(writer, formatter, value)); - tri!(formatter.end_string(writer)); - Ok(()) + formatter.end_string(writer) } fn format_escaped_str_contents( @@ -2104,11 +2023,11 @@ where start = i + 1; } - if start != bytes.len() { - tri!(formatter.write_string_fragment(writer, &value[start..])); + if start == bytes.len() { + return Ok(()); } - Ok(()) + formatter.write_string_fragment(writer, &value[start..]) } const BB: u8 = b'b'; // \x08 @@ -2157,8 +2076,7 @@ where T: ?Sized + Serialize, { let mut ser = Serializer::new(writer); - tri!(value.serialize(&mut ser)); - Ok(()) + value.serialize(&mut ser) } /// Serialize the given data structure as pretty-printed JSON into the IO @@ -2176,8 +2094,7 @@ where T: ?Sized + Serialize, { let mut ser = Serializer::pretty(writer); - tri!(value.serialize(&mut ser)); - Ok(()) + value.serialize(&mut ser) } /// Serialize the given data structure as a JSON byte vector. From 993e7a6eeaa89b39329d245e63879d7913ed1a41 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 19 Oct 2022 15:15:15 -0700 Subject: [PATCH 212/508] Release 1.0.87 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5380001c1..d528f3e8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.86" # remember to update html_root_url +version = "1.0.87" # 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 f90889702..3ced8d43f 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.86")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.87")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 4f194c91416e522a0d14fe208ae5e3e6b88090c2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Oct 2022 21:27:06 -0700 Subject: [PATCH 213/508] 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 214/508] 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 215/508] 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 216/508] 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 217/508] 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 218/508] 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 219/508] 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 220/508] 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 221/508] 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 222/508] 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 223/508] 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 224/508] 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 225/508] 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 226/508] 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 227/508] 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 228/508] 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 229/508] 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 230/508] 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 231/508] 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 232/508] 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 233/508] 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 234/508] 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 235/508] 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 236/508] 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 237/508] 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 238/508] 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 239/508] 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 240/508] 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 241/508] 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 242/508] 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 243/508] 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 244/508] 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 245/508] 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 246/508] 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 247/508] 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 248/508] 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 249/508] 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 250/508] 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 251/508] 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 252/508] 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 253/508] 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 254/508] 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 255/508] 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 256/508] 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 257/508] 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 258/508] 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 259/508] 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 260/508] 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 261/508] 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 262/508] 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 263/508] 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 264/508] 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 265/508] 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 266/508] 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 267/508] 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 268/508] 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 269/508] 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 270/508] 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 271/508] 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 272/508] 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 273/508] 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 274/508] 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 275/508] 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 276/508] 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 277/508] 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 278/508] 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 279/508] 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 280/508] 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 281/508] 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 282/508] 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 283/508] 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 284/508] 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 285/508] 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 286/508] 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 287/508] 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 288/508] 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 289/508] 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 290/508] 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 291/508] 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 292/508] 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 293/508] 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 294/508] 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 295/508] 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 296/508] 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 297/508] 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 298/508] 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 299/508] 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 300/508] 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 301/508] 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 302/508] 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 303/508] 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 304/508] 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 305/508] 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 306/508] 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 307/508] 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 308/508] 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 309/508] 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 310/508] 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 311/508] 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 312/508] 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 313/508] 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 314/508] 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 315/508] 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 316/508] 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 317/508] 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 318/508] 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 319/508] 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 320/508] 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 321/508] 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 322/508] 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 323/508] 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 324/508] 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 325/508] 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 326/508] 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 327/508] 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 328/508] 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 329/508] 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 330/508] 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 331/508] 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 332/508] 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 333/508] 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 334/508] 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 335/508] 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 336/508] 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 337/508] 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 338/508] 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 339/508] 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 340/508] 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 341/508] 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 342/508] 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 343/508] 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 344/508] 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 345/508] 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 346/508] 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 347/508] 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 348/508] 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 349/508] 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 350/508] 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 351/508] 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 352/508] 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 353/508] 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 354/508] 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, From 57d529b70fafd7bef24377507da277e73c18cf45 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 Sep 2023 10:53:44 -0700 Subject: [PATCH 355/508] Test docs.rs documentation build in CI --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dee0bfee1..d9979825e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,16 +103,17 @@ jobs: - run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic - run: cargo clippy --all-features --tests -- -Dclippy::all -Dclippy::pedantic - docs: + doc: name: Documentation runs-on: ubuntu-latest timeout-minutes: 45 + env: + RUSTDOCFLAGS: -Dwarnings steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@nightly - - run: cargo doc --features raw_value,unbounded_depth - env: - RUSTDOCFLAGS: --cfg docsrs + - uses: dtolnay/install@cargo-docs-rs + - run: cargo docs-rs fuzz: name: Fuzz From 9d86391170006bcbc98442b5e40fddbd57af3458 Mon Sep 17 00:00:00 2001 From: Michael Diamond Date: Fri, 6 Oct 2023 00:50:19 -0700 Subject: [PATCH 356/508] Remove a few unnecessary lifetimes --- src/value/from.rs | 6 +++--- src/value/index.rs | 2 +- src/value/partial_eq.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/value/from.rs b/src/value/from.rs index 159592ba8..9f996e0c4 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -91,7 +91,7 @@ impl From for Value { } } -impl<'a> From<&'a str> for Value { +impl From<&str> for Value { /// Convert string slice to `Value` /// /// # Examples @@ -181,7 +181,7 @@ impl> From> for Value { } } -impl<'a, T: Clone + Into> From<&'a [T]> for Value { +impl> From<&[T]> for Value { /// Convert a slice to `Value` /// /// # Examples @@ -192,7 +192,7 @@ impl<'a, T: Clone + Into> From<&'a [T]> for Value { /// let v: &[&str] = &["lorem", "ipsum", "dolor"]; /// let x: Value = v.into(); /// ``` - fn from(f: &'a [T]) -> Self { + fn from(f: &[T]) -> Self { Value::Array(f.iter().cloned().map(Into::into).collect()) } } diff --git a/src/value/index.rs b/src/value/index.rs index c74042b75..891ca8ef7 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -116,7 +116,7 @@ impl Index for String { } } -impl<'a, T> Index for &'a T +impl Index for &T where T: ?Sized + Index, { diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 6b2e350b6..46c1dbc33 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -34,7 +34,7 @@ impl PartialEq for Value { } } -impl<'a> PartialEq<&'a str> for Value { +impl PartialEq<&str> for Value { fn eq(&self, other: &&str) -> bool { eq_str(self, *other) } @@ -46,7 +46,7 @@ impl PartialEq for str { } } -impl<'a> PartialEq for &'a str { +impl PartialEq for &str { fn eq(&self, other: &Value) -> bool { eq_str(other, *self) } From 39f5ad15343a6a1b00660ef74157ad568a2a6c79 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 17 Oct 2023 21:05:31 -0700 Subject: [PATCH 357/508] Remove 'remember to update' reminder from Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c79f6a79f..27106ac55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.107" # remember to update html_root_url +version = "1.0.107" authors = ["Erick Tryzelaar ", "David Tolnay "] categories = ["encoding", "parser-implementations", "no-std"] description = "A JSON serialization file format" From 421a70d1a6c7e30232a526fdb071b2dc52ec9f07 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 24 Oct 2023 23:33:17 -0700 Subject: [PATCH 358/508] Fix unused imports warning: unused imports: `parse_concise_float`, `parse_truncated_float` --> tests/../src/lexical/mod.rs:38:23 | 38 | pub use self::parse::{parse_concise_float, parse_truncated_float}; | ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(unused_imports)]` on by default warning: unused import: `std::vec::Vec` --> tests/lexical.rs:30:13 | 30 | pub use std::vec::Vec; | ^^^^^^^^^^^^^ warning: unused imports: `cmp`, `iter`, `mem`, `ops` --> tests/lexical.rs:31:19 | 31 | pub use std::{cmp, iter, mem, ops}; | ^^^ ^^^^ ^^^ ^^^ --- tests/lexical.rs | 5 ----- tests/lexical/parse.rs | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/lexical.rs b/tests/lexical.rs index d3dfb852b..368c84478 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -26,11 +26,6 @@ extern crate alloc; #[path = "../src/lexical/mod.rs"] mod lexical; -mod lib { - pub use std::vec::Vec; - pub use std::{cmp, iter, mem, ops}; -} - #[path = "lexical/algorithm.rs"] mod algorithm; diff --git a/tests/lexical/parse.rs b/tests/lexical/parse.rs index 80ca25e77..03ec1a9a6 100644 --- a/tests/lexical/parse.rs +++ b/tests/lexical/parse.rs @@ -1,7 +1,7 @@ // Adapted from https://github.com/Alexhuszagh/rust-lexical. use crate::lexical::num::Float; -use crate::lexical::parse::{parse_concise_float, parse_truncated_float}; +use crate::lexical::{parse_concise_float, parse_truncated_float}; use core::f64; use core::fmt::Debug; From 4e091d5d6d9fd94881b64214ac5696b22028721d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 25 Oct 2023 10:41:10 -0700 Subject: [PATCH 359/508] Add test of negative NaN and negative infinity --- tests/test.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index e548b7dae..a38435069 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -160,17 +160,29 @@ fn test_write_f64() { #[test] fn test_encode_nonfinite_float_yields_null() { - let v = to_value(::std::f64::NAN).unwrap(); + let v = to_value(::std::f64::NAN.copysign(1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f64::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); let v = to_value(::std::f64::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::NAN).unwrap(); + let v = to_value(-::std::f64::INFINITY).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f32::NAN.copysign(1.0)).unwrap(); + assert!(v.is_null()); + + let v = to_value(::std::f32::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); let v = to_value(::std::f32::INFINITY).unwrap(); assert!(v.is_null()); + + let v = to_value(-::std::f32::INFINITY).unwrap(); + assert!(v.is_null()); } #[test] From ebaf61709aba7a3f2429a5d95a694514f180f565 Mon Sep 17 00:00:00 2001 From: fritzrehde Date: Mon, 30 Oct 2023 15:20:19 +0100 Subject: [PATCH 360/508] fixed typos --- src/read.rs | 2 +- src/value/from.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/read.rs b/src/read.rs index fc3a3ca74..7446f28b9 100644 --- a/src/read.rs +++ b/src/read.rs @@ -81,7 +81,7 @@ pub trait Read<'de>: private::Sealed { #[doc(hidden)] fn ignore_str(&mut self) -> Result<()>; - /// Assumes the previous byte was a hex escape sequnce ('\u') in a string. + /// Assumes the previous byte was a hex escape sequence ('\u') in a string. /// Parses next hexadecimal sequence. #[doc(hidden)] fn decode_hex_escape(&mut self) -> Result; diff --git a/src/value/from.rs b/src/value/from.rs index 9f996e0c4..fa3fb3acc 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -198,7 +198,7 @@ impl> From<&[T]> for Value { } impl> FromIterator for Value { - /// Convert an iteratable type to a `Value` + /// Convert an iterable type to a `Value` /// /// # Examples /// @@ -228,7 +228,7 @@ impl> FromIterator for Value { } impl, V: Into> FromIterator<(K, V)> for Value { - /// Convert an iteratable type to a `Value` + /// Convert an iterable type to a `Value` /// /// # Examples /// From 0f072fad735f03476c884b5257e894a54facc01b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Oct 2023 11:26:17 -0400 Subject: [PATCH 361/508] Improve Value From and FromIterator docs --- src/value/from.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/value/from.rs b/src/value/from.rs index fa3fb3acc..ed1e3338b 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -28,7 +28,8 @@ from_integer! { } impl From for Value { - /// Convert 32-bit floating point number to `Value` + /// Convert 32-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -44,7 +45,8 @@ impl From for Value { } impl From for Value { - /// Convert 64-bit floating point number to `Value` + /// Convert 64-bit floating point number to `Value::Number`, or + /// `Value::Null` if infinite or NaN. /// /// # Examples /// @@ -60,7 +62,7 @@ impl From for Value { } impl From for Value { - /// Convert boolean to `Value` + /// Convert boolean to `Value::Bool`. /// /// # Examples /// @@ -76,7 +78,7 @@ impl From for Value { } impl From for Value { - /// Convert `String` to `Value` + /// Convert `String` to `Value::String`. /// /// # Examples /// @@ -92,7 +94,7 @@ impl From for Value { } impl From<&str> for Value { - /// Convert string slice to `Value` + /// Convert string slice to `Value::String`. /// /// # Examples /// @@ -108,7 +110,7 @@ impl From<&str> for Value { } impl<'a> From> for Value { - /// Convert copy-on-write string to `Value` + /// Convert copy-on-write string to `Value::String`. /// /// # Examples /// @@ -133,7 +135,7 @@ impl<'a> From> for Value { } impl From for Value { - /// Convert `Number` to `Value` + /// Convert `Number` to `Value::Number`. /// /// # Examples /// @@ -149,7 +151,7 @@ impl From for Value { } impl From> for Value { - /// Convert map (with string keys) to `Value` + /// Convert map (with string keys) to `Value::Object`. /// /// # Examples /// @@ -166,7 +168,7 @@ impl From> for Value { } impl> From> for Value { - /// Convert a `Vec` to `Value` + /// Convert a `Vec` to `Value::Array`. /// /// # Examples /// @@ -182,7 +184,7 @@ impl> From> for Value { } impl> From<&[T]> for Value { - /// Convert a slice to `Value` + /// Convert a slice to `Value::Array`. /// /// # Examples /// @@ -198,7 +200,7 @@ impl> From<&[T]> for Value { } impl> FromIterator for Value { - /// Convert an iterable type to a `Value` + /// Create a `Value::Array` by collecting an iterator of array elements. /// /// # Examples /// @@ -228,7 +230,7 @@ impl> FromIterator for Value { } impl, V: Into> FromIterator<(K, V)> for Value { - /// Convert an iterable type to a `Value` + /// Create a `Value::Object` by collecting an iterator of key-value pairs. /// /// # Examples /// @@ -248,7 +250,7 @@ impl, V: Into> FromIterator<(K, V)> for Value { } impl From<()> for Value { - /// Convert `()` to `Value` + /// Convert `()` to `Value::Null`. /// /// # Examples /// From 4bc1eaa03a6160593575bc9bc60c94dba4cab1e3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 30 Oct 2023 11:36:54 -0400 Subject: [PATCH 362/508] Release 1.0.108 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 27106ac55..5851979e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.107" +version = "1.0.108" 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 347bbaf0b..ce377458f 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.107")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.108")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 05196caf16456cfe6e738e946f459691c5fbf0c6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 18 Nov 2023 18:14:15 -0800 Subject: [PATCH 363/508] Update ui test suite to nightly-2023-11-19 --- tests/ui/parse_key.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/parse_key.stderr b/tests/ui/parse_key.stderr index f10c21800..15662dc50 100644 --- a/tests/ui/parse_key.stderr +++ b/tests/ui/parse_key.stderr @@ -2,4 +2,4 @@ error[E0609]: no field `s` on type `&'static str` --> tests/ui/parse_key.rs:4:16 | 4 | json!({ "".s : true }); - | ^ + | ^ unknown field From fe031cd1de4dde5b47e0f18934632b41bd18b48d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Dec 2023 15:29:20 -0800 Subject: [PATCH 364/508] Delete trace_macros! functionality from test --- tests/test.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index a38435069..07b5755fe 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -14,9 +14,6 @@ clippy::vec_init_then_push, clippy::zero_sized_map_values )] -#![cfg_attr(feature = "trace-macros", feature(trace_macros))] -#[cfg(feature = "trace-macros")] -trace_macros!(true); #[macro_use] mod macros; From 7ff6c9e30c2ecc2cdcf089ed86c7ccfe7a41721c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Dec 2023 15:37:25 -0800 Subject: [PATCH 365/508] Use random hasher state for number hashing test --- tests/test.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 07b5755fe..08965fd04 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -30,12 +30,11 @@ use serde_json::{ from_reader, from_slice, from_str, from_value, json, to_string, to_string_pretty, to_value, to_vec, Deserializer, Number, Value, }; -use std::collections::hash_map::DefaultHasher; use std::collections::BTreeMap; #[cfg(feature = "raw_value")] use std::collections::HashMap; use std::fmt::{self, Debug}; -use std::hash::{Hash, Hasher}; +use std::hash::{BuildHasher, Hash, Hasher}; use std::io; use std::iter; use std::marker::PhantomData; @@ -2490,11 +2489,12 @@ fn test_value_into_deserializer() { #[test] fn hash_positive_and_negative_zero() { - fn hash(obj: impl Hash) -> u64 { - let mut hasher = DefaultHasher::new(); + let rand = std::hash::RandomState::new(); + let hash = |obj: Number| -> u64 { + let mut hasher = rand.build_hasher(); obj.hash(&mut hasher); hasher.finish() - } + }; let k1 = serde_json::from_str::("0.0").unwrap(); let k2 = serde_json::from_str::("-0.0").unwrap(); From b9bcbad3c094042f79f1a45189ec0edbb8f9f322 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Dec 2023 15:38:25 -0800 Subject: [PATCH 366/508] Use BuildHasher::hash_one --- tests/test.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 08965fd04..47b6f1c2f 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -34,7 +34,9 @@ use std::collections::BTreeMap; #[cfg(feature = "raw_value")] use std::collections::HashMap; use std::fmt::{self, Debug}; -use std::hash::{BuildHasher, Hash, Hasher}; +use std::hash::BuildHasher; +#[cfg(feature = "raw_value")] +use std::hash::{Hash, Hasher}; use std::io; use std::iter; use std::marker::PhantomData; @@ -2490,11 +2492,7 @@ fn test_value_into_deserializer() { #[test] fn hash_positive_and_negative_zero() { let rand = std::hash::RandomState::new(); - let hash = |obj: Number| -> u64 { - let mut hasher = rand.build_hasher(); - obj.hash(&mut hasher); - hasher.finish() - }; + let hash = |obj| rand.hash_one(obj); let k1 = serde_json::from_str::("0.0").unwrap(); let k2 = serde_json::from_str::("-0.0").unwrap(); From b328ee7df4dd772922f084600aa7cea39218b694 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 28 Dec 2023 15:39:40 -0800 Subject: [PATCH 367/508] Eliminate hash closure in favor of calling hash_one directly --- tests/test.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 47b6f1c2f..05b7f86fc 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2492,15 +2492,14 @@ fn test_value_into_deserializer() { #[test] fn hash_positive_and_negative_zero() { let rand = std::hash::RandomState::new(); - let hash = |obj| rand.hash_one(obj); let k1 = serde_json::from_str::("0.0").unwrap(); let k2 = serde_json::from_str::("-0.0").unwrap(); if cfg!(feature = "arbitrary_precision") { assert_ne!(k1, k2); - assert_ne!(hash(k1), hash(k2)); + assert_ne!(rand.hash_one(k1), rand.hash_one(k2)); } else { assert_eq!(k1, k2); - assert_eq!(hash(k1), hash(k2)); + assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); } } From df36d109fd9f9cdd22a874c0177cafec12237f95 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 31 Dec 2023 16:42:07 -0800 Subject: [PATCH 368/508] Restore doc cfg on re-exports --- src/de.rs | 1 + src/lib.rs | 2 ++ src/value/mod.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/src/de.rs b/src/de.rs index 9975b40a5..7154f84d2 100644 --- a/src/de.rs +++ b/src/de.rs @@ -22,6 +22,7 @@ use crate::number::NumberDeserializer; pub use crate::read::{Read, SliceRead, StrRead}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::read::IoRead; ////////////////////////////////////////////////////////////////////////////// diff --git a/src/lib.rs b/src/lib.rs index ce377458f..af1c788af 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -365,6 +365,7 @@ extern crate alloc; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::de::from_reader; #[doc(inline)] @@ -374,6 +375,7 @@ pub use crate::error::{Error, Result}; #[doc(inline)] pub use crate::ser::{to_string, to_string_pretty, to_vec, to_vec_pretty}; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] pub use crate::ser::{to_writer, to_writer_pretty, Serializer}; #[doc(inline)] diff --git a/src/value/mod.rs b/src/value/mod.rs index a565b2998..b3f51ea0d 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -106,6 +106,7 @@ pub use crate::map::Map; pub use crate::number::Number; #[cfg(feature = "raw_value")] +#[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. From f88bf1fccb05aa4de129675de44eb6aaf3fec0a0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 31 Dec 2023 16:46:22 -0800 Subject: [PATCH 369/508] Release 1.0.109 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5851979e7..310a69a3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.108" +version = "1.0.109" 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 af1c788af..ec8cad9df 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.108")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.109")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From c35856a93c37b48b3d6efc4fec8e05554dd3f9d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Jan 2024 23:09:24 -0800 Subject: [PATCH 370/508] Pull in proc-macro2 sccache fix --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 310a69a3a..08b1e805f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,14 @@ rust-version = "1.56" indexmap = { version = "2", optional = true } itoa = "1.0" ryu = "1.0" -serde = { version = "1.0.166", default-features = false } +serde = { version = "1.0.194", default-features = false } [dev-dependencies] 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 = { version = "1.0.194", features = ["derive"] } serde_bytes = "0.11.10" serde_derive = "1.0.166" serde_stacker = "0.1.8" From df5cf215b70fb6341b255e7c0a210c06f64c0669 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Jan 2024 23:09:59 -0800 Subject: [PATCH 371/508] Release 1.0.110 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08b1e805f..b9f092751 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.109" +version = "1.0.110" 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 ec8cad9df..b207935cc 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.109")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.110")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From c80dbaf8ff5902ca670e0a48bbe495065b9314f5 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Thu, 4 Jan 2024 14:53:35 +0800 Subject: [PATCH 372/508] Set limb width to 64 for loongarch64 --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 1a8c89828..dd09e62a3 100644 --- a/build.rs +++ b/build.rs @@ -7,7 +7,7 @@ fn main() { // 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" => { + "aarch64" | "mips64" | "powerpc64" | "x86_64" | "loongarch64" => { println!("cargo:rustc-cfg=limb_width_64"); } _ => { From 0131ac68212e8094bd14ee618587d731b4f9a68b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 3 Jan 2024 23:28:45 -0800 Subject: [PATCH 373/508] Release 1.0.111 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b9f092751..d4ea3c8d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.110" +version = "1.0.111" 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 b207935cc..3cdbd3ae3 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.110")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.111")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 6d44b9fac9269b4decf76acac5d68e8ec9d10c58 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 11 Jan 2024 20:16:54 -0800 Subject: [PATCH 374/508] Ignore unconditional_recursion clippy lint due to false positive https://github.com/rust-lang/rust-clippy/issues/12133 warning: function cannot return without recursing --> src/map.rs:276:5 | 276 | / fn eq(&self, other: &Self) -> bool { 277 | | self.map.eq(&other.map) 278 | | } | |_____^ | note: recursive call site --> src/map.rs:277:9 | 277 | self.map.eq(&other.map) | ^^^^^^^^^^^^^^^^^^^^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion = note: `-W clippy::unconditional-recursion` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::unconditional_recursion)]` --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 3cdbd3ae3..238027b6f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -316,6 +316,7 @@ clippy::needless_late_init, clippy::return_self_not_must_use, clippy::transmute_ptr_to_ptr, + clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133 clippy::unnecessary_wraps )] // Ignored clippy_pedantic lints From 2909b0bac6bce0316ae5d7a62edc819c8b9d1173 Mon Sep 17 00:00:00 2001 From: keienWang <42377006+keienWang@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:00:51 +0800 Subject: [PATCH 375/508] fix: Correct spelling error Fixed a spelling error in the second paragraph of the README file. - Changed "exponet" to "exponent" --- src/lexical/num.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexical/num.rs b/src/lexical/num.rs index e47e00341..af10afd7a 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -223,7 +223,7 @@ pub trait Float: Number { const NEGATIVE_INFINITY_BITS: Self::Unsigned; /// Size of the significand (mantissa) without hidden bit. const MANTISSA_SIZE: i32; - /// Bias of the exponet + /// Bias of the exponent const EXPONENT_BIAS: i32; /// Exponent portion of a denormal float. const DENORMAL_EXPONENT: i32; From 296fafb8f32e8442ef8e4d5725c15ffca726b288 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 26 Jan 2024 13:03:05 -0800 Subject: [PATCH 376/508] Factor out JSON-specific Display impl for serde::de::Unexpected --- src/error.rs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 03555eb4c..c260fe17e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -438,11 +438,11 @@ impl de::Error for Error { #[cold] fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { - if let de::Unexpected::Unit = unexp { - Error::custom(format_args!("invalid type: null, expected {}", exp)) - } else { - Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp)) - } + Error::custom(format_args!( + "invalid type: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) } } @@ -453,6 +453,18 @@ impl ser::Error for Error { } } +struct JsonUnexpected<'a>(de::Unexpected<'a>); + +impl<'a> Display for JsonUnexpected<'a> { + fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + if let de::Unexpected::Unit = self.0 { + formatter.write_str("null") + } else { + Display::fmt(&self.0, formatter) + } + } +} + // Parse our own error message that looks like "{} at line {} column {}" to work // around erased-serde round-tripping the error through de::Error::custom. fn make_error(mut msg: String) -> Error { From 62ca3e4c01c2e62cd5c2a32e9104f386e5ce7808 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 26 Jan 2024 13:04:35 -0800 Subject: [PATCH 377/508] Handle Unexpected::Unit in Error::invalid_value --- src/error.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/error.rs b/src/error.rs index c260fe17e..0e3323972 100644 --- a/src/error.rs +++ b/src/error.rs @@ -444,6 +444,15 @@ impl de::Error for Error { exp, )) } + + #[cold] + fn invalid_value(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self { + Error::custom(format_args!( + "invalid value: {}, expected {}", + JsonUnexpected(unexp), + exp, + )) + } } impl ser::Error for Error { From 83d7bad54ba5db3a44198d6df0ff2e81621683fa Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 26 Jan 2024 13:14:48 -0800 Subject: [PATCH 378/508] Format f64 in error messages using ryu --- src/error.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0e3323972..fbf9eb14e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -466,10 +466,14 @@ struct JsonUnexpected<'a>(de::Unexpected<'a>); impl<'a> Display for JsonUnexpected<'a> { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - if let de::Unexpected::Unit = self.0 { - formatter.write_str("null") - } else { - Display::fmt(&self.0, formatter) + match self.0 { + de::Unexpected::Unit => formatter.write_str("null"), + de::Unexpected::Float(value) => write!( + formatter, + "floating point `{}`", + ryu::Buffer::new().format(value), + ), + unexp => Display::fmt(&unexp, formatter), } } } From 7fece969e3b480ec620419d65c2aeb08776bebcb Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 26 Jan 2024 14:02:01 -0800 Subject: [PATCH 379/508] Release 1.0.112 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d4ea3c8d2..4166d7a87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.111" +version = "1.0.112" 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 238027b6f..9526fc1bb 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.111")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.112")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From ca3c2ca3696cab79b8b279be7569ee1647250f1e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 28 Jan 2024 19:36:33 -0800 Subject: [PATCH 380/508] Add swap_remove and shift_remove methods on Map --- Cargo.toml | 4 +-- src/map.rs | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4166d7a87..2e21afb3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/serde-rs/json" rust-version = "1.56" [dependencies] -indexmap = { version = "2", optional = true } +indexmap = { version = "2.2.1", optional = true } itoa = "1.0" ryu = "1.0" serde = { version = "1.0.194", default-features = false } @@ -32,7 +32,7 @@ trybuild = { version = "1.0.81", features = ["diff"] } doc-scrape-examples = false [package.metadata.docs.rs] -features = ["raw_value", "unbounded_depth"] +features = ["preserve_order", "raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] diff --git a/src/map.rs b/src/map.rs index 675058ba1..1d74e1b2b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -130,6 +130,12 @@ impl Map { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove(key)` is + /// equivalent to [`.swap_remove(key)`][Self::swap_remove], replacing this + /// entry's position with the last element. If you need to preserve the + /// relative order of the keys in the map, use + /// [`.shift_remove(key)`][Self::shift_remove] instead. #[inline] pub fn remove(&mut self, key: &Q) -> Option where @@ -137,7 +143,7 @@ impl Map { Q: ?Sized + Ord + Eq + Hash, { #[cfg(feature = "preserve_order")] - return self.map.swap_remove(key); + return self.swap_remove(key); #[cfg(not(feature = "preserve_order"))] return self.map.remove(key); } @@ -147,12 +153,86 @@ impl Map { /// /// The key may be any borrowed form of the map's key type, but the ordering /// on the borrowed form *must* match the ordering on the key type. + /// + /// If serde_json's "preserve_order" is enabled, `.remove_entry(key)` is + /// equivalent to [`.swap_remove_entry(key)`][Self::swap_remove_entry], + /// replacing this entry's position with the last element. If you need to + /// preserve the relative order of the keys in the map, use + /// [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead. + #[inline] pub fn remove_entry(&mut self, key: &Q) -> Option<(String, Value)> where String: Borrow, Q: ?Sized + Ord + Eq + Hash, { - self.map.remove_entry(key) + #[cfg(feature = "preserve_order")] + return self.swap_remove_entry(key); + #[cfg(not(feature = "preserve_order"))] + return self.map.remove_entry(key); + } + + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove(key) + } + + /// Remove and return the key-value pair. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.swap_remove_entry(key) + } + + /// Removes and returns the value corresponding to the key from the map. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove(&mut self, key: &Q) -> Option + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove(key) + } + + /// Remove and return the key-value pair. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(String, Value)> + where + String: Borrow, + Q: ?Sized + Ord + Eq + Hash, + { + self.map.shift_remove_entry(key) } /// Moves all elements from other into self, leaving other empty. From 09d865b34b9701be52764dc9bf571b1a16e9d3dc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 28 Jan 2024 19:44:38 -0800 Subject: [PATCH 381/508] Release 1.0.113 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2e21afb3a..cedde61e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.112" +version = "1.0.113" 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 9526fc1bb..aa3fcfc8d 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.112")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.113")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From ca05f69943e18d70c2219ea41fcb67f2849b7eed Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 7 Feb 2024 21:15:13 -0800 Subject: [PATCH 382/508] Remove unused Float::is_sign_negative trait method warning: method `is_sign_negative` is never used --> src/lexical/num.rs:251:8 | 175 | pub trait Float: Number { | ----- method in this trait ... 251 | fn is_sign_negative(self) -> bool; | ^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default --- src/lexical/num.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/lexical/num.rs b/src/lexical/num.rs index af10afd7a..75eee01b8 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -248,7 +248,6 @@ pub trait Float: Number { fn from_bits(u: Self::Unsigned) -> Self; fn to_bits(self) -> Self::Unsigned; fn is_sign_positive(self) -> bool; - fn is_sign_negative(self) -> bool; /// Returns true if the float is a denormal. #[inline] @@ -368,11 +367,6 @@ impl Float for f32 { fn is_sign_positive(self) -> bool { f32::is_sign_positive(self) } - - #[inline] - fn is_sign_negative(self) -> bool { - f32::is_sign_negative(self) - } } impl Float for f64 { @@ -432,9 +426,4 @@ impl Float for f64 { fn is_sign_positive(self) -> bool { f64::is_sign_positive(self) } - - #[inline] - fn is_sign_negative(self) -> bool { - f64::is_sign_negative(self) - } } From 34a04c5f7febdf80e05743e38d5cb1880a83b6f6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 9 Feb 2024 18:58:46 -0800 Subject: [PATCH 383/508] Ignore incompatible_msrv clippy false positives in test https://github.com/rust-lang/rust-clippy/issues/12257 warning: current MSRV (Minimum Supported Rust Version) is `1.56.0` but this item is stable since `1.71.0` --> tests/test.rs:2500:25 | 2500 | assert_ne!(rand.hash_one(k1), rand.hash_one(k2)); | ^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv = note: `-W clippy::incompatible-msrv` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::incompatible_msrv)]` warning: current MSRV (Minimum Supported Rust Version) is `1.56.0` but this item is stable since `1.71.0` --> tests/test.rs:2500:44 | 2500 | assert_ne!(rand.hash_one(k1), rand.hash_one(k2)); | ^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv warning: current MSRV (Minimum Supported Rust Version) is `1.56.0` but this item is stable since `1.71.0` --> tests/test.rs:2503:25 | 2503 | assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); | ^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv warning: current MSRV (Minimum Supported Rust Version) is `1.56.0` but this item is stable since `1.71.0` --> tests/test.rs:2503:44 | 2503 | assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); | ^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index 05b7f86fc..9a8e7ca73 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -5,6 +5,7 @@ clippy::derive_partial_eq_without_eq, clippy::excessive_precision, clippy::float_cmp, + clippy::incompatible_msrv, // https://github.com/rust-lang/rust-clippy/issues/12257 clippy::items_after_statements, clippy::let_underscore_untyped, clippy::shadow_unrelated, From 6fb70262e894168ac631b2232a1b7b3a7781ebe5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Feb 2024 15:44:01 -0800 Subject: [PATCH 384/508] Work around prelude redundant import warnings https://github.com/rust-lang/rust/pull/117772 warning: the item `String` is imported redundantly --> src/de.rs:8:5 | 8 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here | = note: `#[warn(unused_imports)]` on by default warning: the item `Vec` is imported redundantly --> src/de.rs:9:5 | 9 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `Box` is imported redundantly --> src/error.rs:4:5 | 4 | use alloc::boxed::Box; | ^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Box` is already defined here warning: the item `String` is imported redundantly --> src/error.rs:5:21 | 5 | use alloc::string::{String, ToString}; | ^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `ToString` is imported redundantly --> src/error.rs:5:29 | 5 | use alloc::string::{String, ToString}; | ^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToString` is already defined here warning: the item `String` is imported redundantly --> src/map.rs:10:5 | 10 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `indexmap` is imported redundantly --> src/map.rs:23:16 | 23 | use indexmap::{self, IndexMap}; | ^^^^ the item `indexmap` is already defined here | = note: `#[warn(unused_imports)]` on by default warning: the item `String` is imported redundantly --> src/ser.rs:5:21 | 5 | use alloc::string::{String, ToString}; | ^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `ToString` is imported redundantly --> src/ser.rs:5:29 | 5 | use alloc::string::{String, ToString}; | ^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToString` is already defined here warning: the item `Vec` is imported redundantly --> src/ser.rs:6:5 | 6 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `String` is imported redundantly --> src/value/mod.rs:95:5 | 95 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `Vec` is imported redundantly --> src/value/mod.rs:96:5 | 96 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `ToOwned` is imported redundantly --> src/value/de.rs:5:26 | 5 | use alloc::borrow::{Cow, ToOwned}; | ^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToOwned` is already defined here warning: the item `String` is imported redundantly --> src/value/de.rs:6:5 | 6 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `Vec` is imported redundantly --> src/value/de.rs:9:24 | 9 | use alloc::vec::{self, Vec}; | ^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `String` is imported redundantly --> src/value/from.rs:5:21 | 5 | use alloc::string::{String, ToString}; | ^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `ToString` is imported redundantly --> src/value/from.rs:5:29 | 5 | use alloc::string::{String, ToString}; | ^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToString` is already defined here warning: the item `Vec` is imported redundantly --> src/value/from.rs:6:5 | 6 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `ToOwned` is imported redundantly --> src/value/index.rs:3:5 | 3 | use alloc::borrow::ToOwned; | ^^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToOwned` is already defined here warning: the item `String` is imported redundantly --> src/value/index.rs:4:5 | 4 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `String` is imported redundantly --> src/value/partial_eq.rs:2:5 | 2 | use alloc::string::String; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `ToOwned` is imported redundantly --> src/value/ser.rs:4:5 | 4 | use alloc::borrow::ToOwned; | ^^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToOwned` is already defined here warning: the item `String` is imported redundantly --> src/value/ser.rs:5:21 | 5 | use alloc::string::{String, ToString}; | ^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `String` is already defined here warning: the item `ToString` is imported redundantly --> src/value/ser.rs:5:29 | 5 | use alloc::string::{String, ToString}; | ^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToString` is already defined here warning: the item `Vec` is imported redundantly --> src/value/ser.rs:6:5 | 6 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `Vec` is imported redundantly --> src/read.rs:2:5 | 2 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here warning: the item `ToString` is imported redundantly --> tests/test.rs:46:5 | 46 | use std::string::ToString; | ^^^^^^^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `ToString` is already defined here | = note: `#[warn(unused_imports)]` on by default warning: the item `Vec` is imported redundantly --> tests/../src/lexical/bignum.rs:6:5 | 6 | use alloc::vec::Vec; | ^^^^^^^^^^^^^^^ | ::: nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/prelude/mod.rs:125:13 | 125 | pub use super::v1::*; | --------- the item `Vec` is already defined here | = note: `#[warn(unused_imports)]` on by default --- src/lexical/bignum.rs | 1 + src/lib.rs | 5 ++++- src/map.rs | 10 +++++++++- src/read.rs | 2 ++ tests/test.rs | 1 - 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/lexical/bignum.rs b/src/lexical/bignum.rs index f9551f534..4fa7eed6d 100644 --- a/src/lexical/bignum.rs +++ b/src/lexical/bignum.rs @@ -3,6 +3,7 @@ //! Big integer type definition. use super::math::*; +#[allow(unused_imports)] use alloc::vec::Vec; /// Storage for a big integer type. diff --git a/src/lib.rs b/src/lib.rs index aa3fcfc8d..876ff199d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -360,11 +360,14 @@ #![deny(clippy::question_mark_used)] #![allow(non_upper_case_globals)] #![deny(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] +#![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] extern crate alloc; +#[cfg(feature = "std")] +extern crate std; + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] diff --git a/src/map.rs b/src/map.rs index 1d74e1b2b..520cd6cf5 100644 --- a/src/map.rs +++ b/src/map.rs @@ -20,7 +20,7 @@ use serde::de; #[cfg(not(feature = "preserve_order"))] use alloc::collections::{btree_map, BTreeMap}; #[cfg(feature = "preserve_order")] -use indexmap::{self, IndexMap}; +use indexmap::IndexMap; /// Represents a JSON key/value type. pub struct Map { @@ -176,6 +176,8 @@ impl Map { /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the /// last element of the map and popping it off. This perturbs the position /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove #[cfg(feature = "preserve_order")] #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] #[inline] @@ -192,6 +194,8 @@ impl Map { /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the /// last element of the map and popping it off. This perturbs the position /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove #[cfg(feature = "preserve_order")] #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] #[inline] @@ -208,6 +212,8 @@ impl Map { /// Like [`Vec::remove`], the entry is removed by shifting all of the /// elements that follow it, preserving their relative order. This perturbs /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove #[cfg(feature = "preserve_order")] #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] #[inline] @@ -224,6 +230,8 @@ impl Map { /// Like [`Vec::remove`], the entry is removed by shifting all of the /// elements that follow it, preserving their relative order. This perturbs /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove #[cfg(feature = "preserve_order")] #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] #[inline] diff --git a/src/read.rs b/src/read.rs index 7446f28b9..06ac907e2 100644 --- a/src/read.rs +++ b/src/read.rs @@ -14,6 +14,8 @@ use crate::iter::LineColIterator; use crate::raw::BorrowedRawDeserializer; #[cfg(all(feature = "raw_value", feature = "std"))] use crate::raw::OwnedRawDeserializer; +#[cfg(all(feature = "raw_value", feature = "std"))] +use alloc::string::String; #[cfg(feature = "raw_value")] use serde::de::Visitor; diff --git a/tests/test.rs b/tests/test.rs index 9a8e7ca73..543dbd0b1 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -43,7 +43,6 @@ use std::iter; use std::marker::PhantomData; use std::mem; use std::str::FromStr; -use std::string::ToString; use std::{f32, f64}; use std::{i16, i32, i64, i8}; use std::{u16, u32, u64, u8}; From e1b3a6d8a161ff5ec4865b487d148c17d0188e3e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 19 Feb 2024 16:12:45 -0800 Subject: [PATCH 385/508] Release 1.0.114 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cedde61e4..630f44846 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.113" +version = "1.0.114" 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 876ff199d..f10bedb1c 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.113")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.114")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From fedf8341eedd6fc0ad5c5336d1747463c7d85ece Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 25 Feb 2024 19:50:16 -0800 Subject: [PATCH 386/508] Ignore non_local_definitions false positive in test https://github.com/rust-lang/rust/issues/121621 warning: non-local `impl` definition, they should be avoided as they go against expectation --> tests/test.rs:2338:5 | 2338 | / impl<'de> Deserialize<'de> for &'de RawMapKey { 2339 | | fn deserialize(deserializer: D) -> Result 2340 | | where 2341 | | D: serde::Deserializer<'de>, ... | 2345 | | } 2346 | | } | |_____^ | = help: move this `impl` block outside the of the current function `test_raw_value_in_map_key` = note: an `impl` definition is non-local if it is nested inside an item and neither the type nor the trait are at the same nesting level as the `impl` block = note: one exception to the rule are anon-const (`const _: () = { ... }`) at top-level module and anon-const at the same nesting as the trait or type = note: this lint may become deny-by-default in the edition 2024 and higher, see the tracking issue = note: `#[warn(non_local_definitions)]` on by default --- tests/test.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 543dbd0b1..c227b9066 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2335,6 +2335,8 @@ fn test_raw_value_in_map_key() { #[repr(transparent)] struct RawMapKey(RawValue); + #[allow(unknown_lints)] + #[allow(non_local_definitions)] // false positive: https://github.com/rust-lang/rust/issues/121621 impl<'de> Deserialize<'de> for &'de RawMapKey { fn deserialize(deserializer: D) -> Result where From a25f6c6f2af1ac268175e79ad8d537106dbb3a3a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 22 Mar 2024 15:01:47 -0700 Subject: [PATCH 387/508] Remove conditional on repr(transparent) --- src/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raw.rs b/src/raw.rs index a2bf0ecbb..22d14441e 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -112,8 +112,8 @@ use serde::ser::{Serialize, SerializeStruct, Serializer}; /// raw_value: Box, /// } /// ``` -#[cfg_attr(not(doc), repr(transparent))] #[cfg_attr(docsrs, doc(cfg(feature = "raw_value")))] +#[repr(transparent)] pub struct RawValue { json: str, } From d2dbbf7055666b42957dee59b6a4ea57413517ff Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 23 Mar 2024 20:19:13 -0700 Subject: [PATCH 388/508] Ignore dead code lint in tests New in nightly-2024-03-24 from https://github.com/rust-lang/rust/pull/119552. warning: field `x` is never read --> tests/regression/issue795.rs:11:15 | 11 | Variant { x: u8 }, | ------- ^ | | | field in this variant | = note: `#[warn(dead_code)]` on by default warning: field `i` is never read --> tests/regression/issue845.rs:63:9 | 61 | pub struct Struct { | ------ field in this struct 62 | #[serde(deserialize_with = "deserialize_integer_or_string")] 63 | pub i: i64, | ^ | = note: `Struct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis --- tests/regression/issue795.rs | 5 ++++- tests/regression/issue845.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/regression/issue795.rs b/tests/regression/issue795.rs index bb82852c5..411e8af5d 100644 --- a/tests/regression/issue795.rs +++ b/tests/regression/issue795.rs @@ -8,7 +8,10 @@ use std::fmt; #[derive(Debug)] pub enum Enum { - Variant { x: u8 }, + Variant { + #[allow(dead_code)] + x: u8, + }, } impl<'de> Deserialize<'de> for Enum { diff --git a/tests/regression/issue845.rs b/tests/regression/issue845.rs index e8b0c0fd0..7b6564dad 100644 --- a/tests/regression/issue845.rs +++ b/tests/regression/issue845.rs @@ -60,6 +60,7 @@ where #[derive(Deserialize, Debug)] pub struct Struct { #[serde(deserialize_with = "deserialize_integer_or_string")] + #[allow(dead_code)] pub i: i64, } From 4a0be88b5ac6cda971a52df9f027b551fe566347 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 23 Mar 2024 20:24:22 -0700 Subject: [PATCH 389/508] Format regression tests with rustfmt --- tests/regression/issue520.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/issue520.rs b/tests/regression/issue520.rs index 9ed367731..730ecc60a 100644 --- a/tests/regression/issue520.rs +++ b/tests/regression/issue520.rs @@ -1,6 +1,6 @@ #![allow(clippy::float_cmp)] -use serde_derive::{Serialize, Deserialize}; +use serde_derive::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug)] #[serde(tag = "type", content = "data")] From 3a3f61b1c9a2dce973179fad1650f709f63bdaa5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 24 Mar 2024 19:53:11 -0700 Subject: [PATCH 390/508] Temporarily disable miri on doctests --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d9979825e..e4accd879 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,8 +89,8 @@ jobs: - uses: actions/checkout@v4 - 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 + - run: cargo miri test --all-targets # exclude doctests https://github.com/rust-lang/miri/issues/3404 + - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value --all-targets clippy: name: Clippy From 840da8e89241d46482c40038d3acdb6745ed4f05 Mon Sep 17 00:00:00 2001 From: Jon Heinritz Date: Mon, 25 Mar 2024 09:06:15 +0100 Subject: [PATCH 391/508] Fix missing backticks in doc comments This was leading to rustdocs markdown parser to interpret slices like &[u8] as & and a link to u8 --- README.md | 2 +- src/lexical/large_powers32.rs | 2 +- src/lexical/large_powers64.rs | 2 +- src/lib.rs | 2 +- src/read.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a3ba288f1..be70b7b06 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/lexical/large_powers32.rs b/src/lexical/large_powers32.rs index 799119726..eb8582f5f 100644 --- a/src/lexical/large_powers32.rs +++ b/src/lexical/large_powers32.rs @@ -2,7 +2,7 @@ //! Precalculated large powers for 32-bit limbs. -/// Large powers (&[u32]) for base5 operations. +/// Large powers (`&[u32]`) for base5 operations. const POW5_1: [u32; 1] = [5]; const POW5_2: [u32; 1] = [25]; const POW5_3: [u32; 1] = [625]; diff --git a/src/lexical/large_powers64.rs b/src/lexical/large_powers64.rs index ee3656108..96554eac1 100644 --- a/src/lexical/large_powers64.rs +++ b/src/lexical/large_powers64.rs @@ -2,7 +2,7 @@ //! Precalculated large powers for 64-bit limbs. -/// Large powers (&[u64]) for base5 operations. +/// Large powers (`&[u64]`) for base5 operations. const POW5_1: [u64; 1] = [5]; const POW5_2: [u64; 1] = [25]; const POW5_3: [u64; 1] = [625]; diff --git a/src/lib.rs b/src/lib.rs index f10bedb1c..da0da469f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,7 @@ //! //! 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 +//! for parsing from a byte slice `&[u8]` and [`from_reader`] for parsing from //! any `io::Read` like a File or a TCP stream. //! //! ``` diff --git a/src/read.rs b/src/read.rs index 06ac907e2..a426911c7 100644 --- a/src/read.rs +++ b/src/read.rs @@ -20,7 +20,7 @@ use alloc::string::String; use serde::de::Visitor; /// Trait used by the deserializer for iterating over input. This is manually -/// "specialized" for iterating over &[u8]. Once feature(specialization) is +/// "specialized" for iterating over `&[u8]`. Once feature(specialization) is /// stable we can use actual specialization. /// /// This trait is sealed and cannot be implemented for types outside of From 218770bb7531b0e491a2883eafc40ade5b1eeaf5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 Mar 2024 22:24:55 -0700 Subject: [PATCH 392/508] Explicitly install a Rust toolchain for cargo-outdated job Debugging a recent cargo-outdated bug, it would have been nice not to wonder whether a rustc version change in GitHub's runner image was a contributing factor. --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4accd879..75cc7bfe6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -132,6 +132,7 @@ jobs: timeout-minutes: 45 steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable - uses: dtolnay/install@cargo-outdated - run: cargo outdated --exit-code 1 - run: cargo outdated --manifest-path fuzz/Cargo.toml --exit-code 1 From b1ebf3888ed728c66c69581ba8d9c4aa7483f486 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 25 Mar 2024 23:03:48 -0700 Subject: [PATCH 393/508] Release 1.0.115 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 630f44846..38b6d6ec8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.114" +version = "1.0.115" 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 da0da469f..32c4fc456 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.114")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.115")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 0baba2877595e31d57adafd5db2f94074f4a4c2e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 5 Apr 2024 19:05:51 -0700 Subject: [PATCH 394/508] Resolve legacy_numeric_constants clippy lints warning: usage of a legacy numeric method --> src/de.rs:484:73 | 484 | ... if overflow!(significand * 10 + digit, u64::max_value()) { | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants = note: `#[warn(clippy::legacy_numeric_constants)]` on by default help: use the associated constant instead | 484 | if overflow!(significand * 10 + digit, u64::MAX) { | ~~~ warning: usage of a legacy numeric method --> src/de.rs:536:57 | 536 | if overflow!(significand * 10 + digit, u64::max_value()) { | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 536 | if overflow!(significand * 10 + digit, u64::MAX) { | ~~~ warning: usage of a legacy numeric method --> src/de.rs:600:49 | 600 | if overflow!(exp * 10 + digit, i32::max_value()) { | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 600 | if overflow!(exp * 10 + digit, i32::MAX) { | ~~~ warning: usage of a legacy numeric method --> src/number.rs:100:39 | 100 | N::PosInt(v) => v <= i64::max_value() as u64, | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 100 | N::PosInt(v) => v <= i64::MAX as u64, | ~~~ warning: usage of a legacy numeric method --> src/number.rs:192:30 | 192 | if n <= i64::max_value() as u64 { | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 192 | if n <= i64::MAX as u64 { | ~~~ warning: usage of a legacy numeric method --> tests/../src/lexical/exponent.rs:11:21 | 11 | if value > i32::max_value() as usize { | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants = note: `#[warn(clippy::legacy_numeric_constants)]` on by default help: use the associated constant instead | 11 | if value > i32::MAX as usize { | ~~~ warning: usage of a legacy numeric method --> tests/../src/lexical/exponent.rs:12:14 | 12 | i32::max_value() | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 12 | i32::MAX | ~~~ warning: usage of a legacy numeric method --> tests/../src/lexical/rounding.rs:28:14 | 28 | u64::max_value() | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 28 | u64::MAX | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:21:34 | 21 | scientific_exponent(i32::min_value(), 0, 0), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 21 | scientific_exponent(i32::MIN, 0, 0), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:22:14 | 22 | i32::min_value() | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 22 | i32::MIN | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:25:34 | 25 | scientific_exponent(i32::min_value(), 0, 5), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 25 | scientific_exponent(i32::MIN, 0, 5), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:26:14 | 26 | i32::min_value() | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 26 | i32::MIN | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:31:34 | 31 | scientific_exponent(i32::max_value(), 0, 0), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 31 | scientific_exponent(i32::MAX, 0, 0), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:32:14 | 32 | i32::max_value() - 1 | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 32 | i32::MAX - 1 | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:35:34 | 35 | scientific_exponent(i32::max_value(), 5, 0), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 35 | scientific_exponent(i32::MAX, 5, 0), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:36:14 | 36 | i32::max_value() | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 36 | i32::MAX | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:45:32 | 45 | mantissa_exponent(i32::max_value(), 5, 0), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 45 | mantissa_exponent(i32::MAX, 5, 0), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:46:14 | 46 | i32::max_value() - 5 | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 46 | i32::MAX - 5 | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:48:39 | 48 | assert_eq!(mantissa_exponent(i32::max_value(), 0, 5), i32::max_value()); | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 48 | assert_eq!(mantissa_exponent(i32::MAX, 0, 5), i32::max_value()); | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:48:64 | 48 | assert_eq!(mantissa_exponent(i32::max_value(), 0, 5), i32::max_value()); | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 48 | assert_eq!(mantissa_exponent(i32::max_value(), 0, 5), i32::MAX); | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:49:39 | 49 | assert_eq!(mantissa_exponent(i32::min_value(), 5, 0), i32::min_value()); | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 49 | assert_eq!(mantissa_exponent(i32::MIN, 5, 0), i32::min_value()); | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:49:64 | 49 | assert_eq!(mantissa_exponent(i32::min_value(), 5, 0), i32::min_value()); | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 49 | assert_eq!(mantissa_exponent(i32::min_value(), 5, 0), i32::MIN); | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:51:32 | 51 | mantissa_exponent(i32::min_value(), 0, 5), | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 51 | mantissa_exponent(i32::MIN, 0, 5), | ~~~ warning: usage of a legacy numeric method --> tests/lexical/exponent.rs:52:14 | 52 | i32::min_value() + 5 | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 52 | i32::MIN + 5 | ~~~ warning: importing legacy numeric constants --> tests/test.rs:47:11 | 47 | use std::{i16, i32, i64, i8}; | ^^^ | = help: remove this import = note: then `i16::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants = note: `#[warn(clippy::legacy_numeric_constants)]` on by default warning: importing legacy numeric constants --> tests/test.rs:47:16 | 47 | use std::{i16, i32, i64, i8}; | ^^^ | = help: remove this import = note: then `i32::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:47:21 | 47 | use std::{i16, i32, i64, i8}; | ^^^ | = help: remove this import = note: then `i64::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:47:26 | 47 | use std::{i16, i32, i64, i8}; | ^^ | = help: remove this import = note: then `i8::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:48:11 | 48 | use std::{u16, u32, u64, u8}; | ^^^ | = help: remove this import = note: then `u16::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:48:16 | 48 | use std::{u16, u32, u64, u8}; | ^^^ | = help: remove this import = note: then `u32::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:48:21 | 48 | use std::{u16, u32, u64, u8}; | ^^^ | = help: remove this import = note: then `u64::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: importing legacy numeric constants --> tests/test.rs:48:26 | 48 | use std::{u16, u32, u64, u8}; | ^^ | = help: remove this import = note: then `u8::` will resolve to the respective associated constant = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants warning: usage of a legacy numeric constant --> tests/test.rs:161:22 | 161 | let v = to_value(::std::f64::NAN.copysign(1.0)).unwrap(); | ^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 161 | let v = to_value(f64::NAN.copysign(1.0)).unwrap(); | ~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:164:22 | 164 | let v = to_value(::std::f64::NAN.copysign(-1.0)).unwrap(); | ^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 164 | let v = to_value(f64::NAN.copysign(-1.0)).unwrap(); | ~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:167:22 | 167 | let v = to_value(::std::f64::INFINITY).unwrap(); | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 167 | let v = to_value(f64::INFINITY).unwrap(); | ~~~~~~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:170:23 | 170 | let v = to_value(-::std::f64::INFINITY).unwrap(); | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 170 | let v = to_value(-f64::INFINITY).unwrap(); | ~~~~~~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:173:22 | 173 | let v = to_value(::std::f32::NAN.copysign(1.0)).unwrap(); | ^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 173 | let v = to_value(f32::NAN.copysign(1.0)).unwrap(); | ~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:176:22 | 176 | let v = to_value(::std::f32::NAN.copysign(-1.0)).unwrap(); | ^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 176 | let v = to_value(f32::NAN.copysign(-1.0)).unwrap(); | ~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:179:22 | 179 | let v = to_value(::std::f32::INFINITY).unwrap(); | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 179 | let v = to_value(f32::INFINITY).unwrap(); | ~~~~~~~~~~~~~ warning: usage of a legacy numeric constant --> tests/test.rs:182:23 | 182 | let v = to_value(-::std::f32::INFINITY).unwrap(); | ^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 182 | let v = to_value(-f32::INFINITY).unwrap(); | ~~~~~~~~~~~~~ warning: usage of a legacy numeric method --> tests/test.rs:2243:26 | 2243 | let signed = &[i128::min_value(), -1, 0, 1, i128::max_value()]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2243 | let signed = &[i128::MIN, -1, 0, 1, i128::max_value()]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2243:55 | 2243 | let signed = &[i128::min_value(), -1, 0, 1, i128::max_value()]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2243 | let signed = &[i128::min_value(), -1, 0, 1, i128::MAX]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2244:34 | 2244 | let unsigned = &[0, 1, u128::max_value()]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2244 | let unsigned = &[0, 1, u128::MAX]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2280:36 | 2280 | let signed = &[i128::from(i64::min_value()), i128::from(u64::max_value())]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2280 | let signed = &[i128::from(i64::MIN), i128::from(u64::max_value())]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2280:66 | 2280 | let signed = &[i128::from(i64::min_value()), i128::from(u64::max_value())]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2280 | let signed = &[i128::from(i64::min_value()), i128::from(u64::MAX)]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2281:41 | 2281 | let unsigned = &[0, u128::from(u64::max_value())]; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2281 | let unsigned = &[0, u128::from(u64::MAX)]; | ~~~ warning: usage of a legacy numeric method --> tests/test.rs:2294:44 | 2294 | let err = to_value(u128::from(u64::max_value()) + 1).unwrap_err(); | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants help: use the associated constant instead | 2294 | let err = to_value(u128::from(u64::MAX) + 1).unwrap_err(); | ~~~ --- src/de.rs | 8 ++++---- src/lexical/exponent.rs | 4 ++-- src/lexical/rounding.rs | 2 +- src/number.rs | 8 ++++---- tests/lexical/exponent.rs | 34 ++++++++-------------------------- tests/test.rs | 28 +++++++++++++--------------- 6 files changed, 32 insertions(+), 52 deletions(-) diff --git a/src/de.rs b/src/de.rs index 7154f84d2..c7774f687 100644 --- a/src/de.rs +++ b/src/de.rs @@ -481,7 +481,7 @@ impl<'de, R: Read<'de>> Deserializer { // try to keep the number as a `u64` until we grow // too large. At that point, switch to parsing the // value as a `f64`. - if overflow!(significand * 10 + digit, u64::max_value()) { + if overflow!(significand * 10 + digit, u64::MAX) { return Ok(ParserNumber::F64(tri!( self.parse_long_integer(positive, significand), ))); @@ -533,7 +533,7 @@ impl<'de, R: Read<'de>> Deserializer { while let c @ b'0'..=b'9' = tri!(self.peek_or_null()) { let digit = (c - b'0') as u64; - if overflow!(significand * 10 + digit, u64::max_value()) { + if overflow!(significand * 10 + digit, u64::MAX) { let exponent = exponent_before_decimal_point + exponent_after_decimal_point; return self.parse_decimal_overflow(positive, significand, exponent); } @@ -597,7 +597,7 @@ impl<'de, R: Read<'de>> Deserializer { self.eat_char(); let digit = (c - b'0') as i32; - if overflow!(exp * 10 + digit, i32::max_value()) { + if overflow!(exp * 10 + digit, i32::MAX) { let zero_significand = significand == 0; return self.parse_exponent_overflow(positive, zero_significand, positive_exp); } @@ -789,7 +789,7 @@ impl<'de, R: Read<'de>> Deserializer { self.eat_char(); let digit = (c - b'0') as i32; - if overflow!(exp * 10 + digit, i32::max_value()) { + if overflow!(exp * 10 + digit, i32::MAX) { let zero_significand = self.scratch.iter().all(|&digit| digit == b'0'); return self.parse_exponent_overflow(positive, zero_significand, positive_exp); } diff --git a/src/lexical/exponent.rs b/src/lexical/exponent.rs index 6fc51977e..5e27de893 100644 --- a/src/lexical/exponent.rs +++ b/src/lexical/exponent.rs @@ -8,8 +8,8 @@ /// the mantissa we do not overflow for comically-long exponents. #[inline] fn into_i32(value: usize) -> i32 { - if value > i32::max_value() as usize { - i32::max_value() + if value > i32::MAX as usize { + i32::MAX } else { value as i32 } diff --git a/src/lexical/rounding.rs b/src/lexical/rounding.rs index 6ec1292aa..634487522 100644 --- a/src/lexical/rounding.rs +++ b/src/lexical/rounding.rs @@ -25,7 +25,7 @@ pub(crate) fn lower_n_mask(n: u64) -> u64 { debug_assert!(n <= bits, "lower_n_mask() overflow in shl."); if n == bits { - u64::max_value() + u64::MAX } else { (1 << n) - 1 } diff --git a/src/number.rs b/src/number.rs index b0231a87b..878a3dcb2 100644 --- a/src/number.rs +++ b/src/number.rs @@ -82,7 +82,7 @@ impl Number { /// ``` /// # use serde_json::json; /// # - /// let big = i64::max_value() as u64 + 10; + /// let big = i64::MAX as u64 + 10; /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); /// /// assert!(v["a"].is_i64()); @@ -97,7 +97,7 @@ impl Number { pub fn is_i64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { - N::PosInt(v) => v <= i64::max_value() as u64, + N::PosInt(v) => v <= i64::MAX as u64, N::NegInt(_) => true, N::Float(_) => false, } @@ -177,7 +177,7 @@ impl Number { /// ``` /// # use serde_json::json; /// # - /// let big = i64::max_value() as u64 + 10; + /// let big = i64::MAX as u64 + 10; /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); /// /// assert_eq!(v["a"].as_i64(), Some(64)); @@ -189,7 +189,7 @@ impl Number { #[cfg(not(feature = "arbitrary_precision"))] match self.n { N::PosInt(n) => { - if n <= i64::max_value() as u64 { + if n <= i64::MAX as u64 { Some(n as i64) } else { None diff --git a/tests/lexical/exponent.rs b/tests/lexical/exponent.rs index f7a847be3..c109ff07d 100644 --- a/tests/lexical/exponent.rs +++ b/tests/lexical/exponent.rs @@ -17,38 +17,20 @@ fn scientific_exponent_test() { assert_eq!(scientific_exponent(-10, 2, 20), -9); // Underflow - assert_eq!( - scientific_exponent(i32::min_value(), 0, 0), - i32::min_value() - ); - assert_eq!( - scientific_exponent(i32::min_value(), 0, 5), - i32::min_value() - ); + assert_eq!(scientific_exponent(i32::MIN, 0, 0), i32::MIN); + assert_eq!(scientific_exponent(i32::MIN, 0, 5), i32::MIN); // Overflow - assert_eq!( - scientific_exponent(i32::max_value(), 0, 0), - i32::max_value() - 1 - ); - assert_eq!( - scientific_exponent(i32::max_value(), 5, 0), - i32::max_value() - ); + assert_eq!(scientific_exponent(i32::MAX, 0, 0), i32::MAX - 1); + assert_eq!(scientific_exponent(i32::MAX, 5, 0), i32::MAX); } #[test] fn mantissa_exponent_test() { assert_eq!(mantissa_exponent(10, 5, 0), 5); assert_eq!(mantissa_exponent(0, 5, 0), -5); - assert_eq!( - mantissa_exponent(i32::max_value(), 5, 0), - i32::max_value() - 5 - ); - assert_eq!(mantissa_exponent(i32::max_value(), 0, 5), i32::max_value()); - assert_eq!(mantissa_exponent(i32::min_value(), 5, 0), i32::min_value()); - assert_eq!( - mantissa_exponent(i32::min_value(), 0, 5), - i32::min_value() + 5 - ); + assert_eq!(mantissa_exponent(i32::MAX, 5, 0), i32::MAX - 5); + assert_eq!(mantissa_exponent(i32::MAX, 0, 5), i32::MAX); + assert_eq!(mantissa_exponent(i32::MIN, 5, 0), i32::MIN); + assert_eq!(mantissa_exponent(i32::MIN, 0, 5), i32::MIN + 5); } diff --git a/tests/test.rs b/tests/test.rs index c227b9066..7e6adad76 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -44,8 +44,6 @@ use std::marker::PhantomData; use std::mem; use std::str::FromStr; use std::{f32, f64}; -use std::{i16, i32, i64, i8}; -use std::{u16, u32, u64, u8}; macro_rules! treemap { () => { @@ -158,28 +156,28 @@ fn test_write_f64() { #[test] fn test_encode_nonfinite_float_yields_null() { - let v = to_value(::std::f64::NAN.copysign(1.0)).unwrap(); + let v = to_value(f64::NAN.copysign(1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f64::NAN.copysign(-1.0)).unwrap(); + let v = to_value(f64::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f64::INFINITY).unwrap(); + let v = to_value(f64::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(-::std::f64::INFINITY).unwrap(); + let v = to_value(-f64::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::NAN.copysign(1.0)).unwrap(); + let v = to_value(f32::NAN.copysign(1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::NAN.copysign(-1.0)).unwrap(); + let v = to_value(f32::NAN.copysign(-1.0)).unwrap(); assert!(v.is_null()); - let v = to_value(::std::f32::INFINITY).unwrap(); + let v = to_value(f32::INFINITY).unwrap(); assert!(v.is_null()); - let v = to_value(-::std::f32::INFINITY).unwrap(); + let v = to_value(-f32::INFINITY).unwrap(); assert!(v.is_null()); } @@ -2240,8 +2238,8 @@ fn null_invalid_type() { #[test] fn test_integer128() { - let signed = &[i128::min_value(), -1, 0, 1, i128::max_value()]; - let unsigned = &[0, 1, u128::max_value()]; + let signed = &[i128::MIN, -1, 0, 1, i128::MAX]; + let unsigned = &[0, 1, u128::MAX]; for integer128 in signed { let expected = integer128.to_string(); @@ -2277,8 +2275,8 @@ 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())]; + let signed = &[i128::from(i64::MIN), i128::from(u64::MAX)]; + let unsigned = &[0, u128::from(u64::MAX)]; for integer128 in signed { let expected = integer128.to_string(); @@ -2291,7 +2289,7 @@ fn test_integer128_to_value() { } if !cfg!(feature = "arbitrary_precision") { - let err = to_value(u128::from(u64::max_value()) + 1).unwrap_err(); + let err = to_value(u128::from(u64::MAX) + 1).unwrap_err(); assert_eq!(err.to_string(), "number out of range"); } } From 2e15e3d7d53a68f78ff559709c57e4fa70584bb7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 8 Apr 2024 11:57:46 -0700 Subject: [PATCH 395/508] Revert "Temporarily disable miri on doctests" This reverts commit 3a3f61b1c9a2dce973179fad1650f709f63bdaa5. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75cc7bfe6..e55d307dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,8 +89,8 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri - run: cargo miri setup - - run: cargo miri test --all-targets # exclude doctests https://github.com/rust-lang/miri/issues/3404 - - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value --all-targets + - run: cargo miri test + - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value clippy: name: Clippy From 25dc75050aee18ff42342bdb64c1e97542d17267 Mon Sep 17 00:00:00 2001 From: Michael Leonhard Date: Mon, 15 Apr 2024 13:07:18 -0700 Subject: [PATCH 396/508] Replace `features_check` mod with a call to `std::compile_error!`. Fixes https://github.com/serde-rs/json/issues/1123 . --- src/features_check/error.rs | 1 - src/features_check/mod.rs | 13 ------------- src/lib.rs | 5 +++-- 3 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 src/features_check/error.rs delete mode 100644 src/features_check/mod.rs diff --git a/src/features_check/error.rs b/src/features_check/error.rs deleted file mode 100644 index 22e58235c..000000000 --- a/src/features_check/error.rs +++ /dev/null @@ -1 +0,0 @@ -"serde_json requires that either `std` (default) or `alloc` feature is enabled" diff --git a/src/features_check/mod.rs b/src/features_check/mod.rs deleted file mode 100644 index d12032cef..000000000 --- a/src/features_check/mod.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! Shows a user-friendly compiler error on incompatible selected features. - -#[allow(unused_macros)] -macro_rules! hide_from_rustfmt { - ($mod:item) => { - $mod - }; -} - -#[cfg(not(any(feature = "std", feature = "alloc")))] -hide_from_rustfmt! { - mod error; -} diff --git a/src/lib.rs b/src/lib.rs index 32c4fc456..b6f183a17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -363,6 +363,9 @@ #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] +#[cfg(not(any(feature = "std", feature = "alloc")))] +compile_error!("serde_json requires that either `std` (default) or `alloc` feature is enabled"); + extern crate alloc; #[cfg(feature = "std")] @@ -409,8 +412,6 @@ pub mod ser; mod ser; pub mod value; -mod features_check; - mod io; #[cfg(feature = "std")] mod iter; From 12c8ee0ce6eaca3a809e83d9df768b67322a7f2a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 15 Apr 2024 22:08:33 -0700 Subject: [PATCH 397/508] Hide "non-exhaustive patterns" errors when crate fails to compile Fixes #1125. error: expected item, found `"serde_json requires that either `std` (default) or `alloc` feature is enabled"` --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/features_check/error.rs:1:1 | 1 | "serde_json requires that either `std` (default) or `alloc` feature is enabled" | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected item | = note: for a full list of items that can appear in modules, see error[E0004]: non-exhaustive patterns: `Value::String(_)` not covered --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/value/de.rs:216:15 | 216 | match self { | ^^^^ pattern `Value::String(_)` not covered | note: `Value` defined here --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/value/mod.rs:116:10 | 116 | pub enum Value { | ^^^^^ ... 151 | String(String), | ------ not covered = note: the matched value is of type `Value` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 223 ~ Value::Object(v) => visit_object(v, visitor), 224 ~ Value::String(_) => todo!(), | error[E0004]: non-exhaustive patterns: `Cow::Owned(_)` not covered --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/value/de.rs:1338:15 | 1338 | match self.value { | ^^^^^^^^^^ pattern `Cow::Owned(_)` not covered | note: `Cow<'_, str>` defined here --> /rustc/98aa3624be70462d6a25ed5544333e3df62f4c66/library/alloc/src/borrow.rs:180:1 ::: /rustc/98aa3624be70462d6a25ed5544333e3df62f4c66/library/alloc/src/borrow.rs:190:5 | = note: not covered = note: the matched value is of type `Cow<'_, str>` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 1339 ~ Cow::Borrowed(string) => visitor.visit_borrowed_str(string), 1340 ~ Cow::Owned(_) => todo!(), | error[E0004]: non-exhaustive patterns: `&Value::Object(_)` not covered --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/value/ser.rs:17:15 | 17 | match self { | ^^^^ pattern `&Value::Object(_)` not covered | note: `Value` defined here --> ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.115/src/value/mod.rs:116:10 | 116 | pub enum Value { | ^^^^^ ... 175 | Object(Map), | ------ not covered = note: the matched value is of type `&Value` help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown | 22 ~ Value::Array(v) => v.serialize(serializer), 23 ~ &Value::Object(_) => todo!(), | --- src/value/de.rs | 4 ++++ src/value/ser.rs | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/value/de.rs b/src/value/de.rs index 1e8b5acbb..936725635 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -219,6 +219,8 @@ impl<'de> serde::Deserializer<'de> for Value { Value::Number(n) => n.deserialize_any(visitor), #[cfg(any(feature = "std", feature = "alloc"))] Value::String(v) => visitor.visit_string(v), + #[cfg(not(any(feature = "std", feature = "alloc")))] + Value::String(_) => unreachable!(), Value::Array(v) => visit_array(v, visitor), Value::Object(v) => visit_object(v, visitor), } @@ -1339,6 +1341,8 @@ impl<'de> de::Deserializer<'de> for BorrowedCowStrDeserializer<'de> { Cow::Borrowed(string) => visitor.visit_borrowed_str(string), #[cfg(any(feature = "std", feature = "alloc"))] Cow::Owned(string) => visitor.visit_string(string), + #[cfg(not(any(feature = "std", feature = "alloc")))] + Cow::Owned(_) => unreachable!(), } } diff --git a/src/value/ser.rs b/src/value/ser.rs index 835fa9080..e869ae160 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -29,6 +29,8 @@ impl Serialize for Value { } map.end() } + #[cfg(not(any(feature = "std", feature = "alloc")))] + Value::Object(_) => unreachable!(), } } } From a3f62bb10ea870dafe3b49a77dc6c1713ca4b7e4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 15 Apr 2024 22:10:38 -0700 Subject: [PATCH 398/508] Release 1.0.116 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 38b6d6ec8..61e6335da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.115" +version = "1.0.116" 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 5d730102b..eabd169f5 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.115")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.116")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 16eb8723cd54b4cafd3d97a13ad3ac8a53d3f990 Mon Sep 17 00:00:00 2001 From: Youngchan Lee Date: Thu, 25 Apr 2024 15:26:36 +0900 Subject: [PATCH 399/508] impl `Hash` for `Value` --- src/map.rs | 19 +++++++++++++++++++ src/value/mod.rs | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 520cd6cf5..4a90830c4 100644 --- a/src/map.rs +++ b/src/map.rs @@ -8,6 +8,8 @@ use crate::value::Value; use alloc::string::String; +#[cfg(feature = "preserve_order")] +use alloc::vec::Vec; use core::borrow::Borrow; use core::fmt::{self, Debug}; use core::hash::Hash; @@ -368,6 +370,23 @@ impl PartialEq for Map { impl Eq for Map {} +#[cfg(not(feature = "preserve_order"))] +impl Hash for Map { + #[inline] + fn hash(&self, state: &mut H) { + self.map.hash(state) + } +} +#[cfg(feature = "preserve_order")] +impl Hash for Map { + #[inline] + fn hash(&self, state: &mut H) { + let mut kv = self.map.iter().collect::>(); + kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); + kv.hash(state); + } +} + /// Access an element of this map. Panics if the given key is not present in the /// map. /// diff --git a/src/value/mod.rs b/src/value/mod.rs index b3f51ea0d..026f10dcb 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -112,7 +112,7 @@ pub use crate::raw::{to_raw_value, RawValue}; /// Represents any valid JSON value. /// /// See the [`serde_json::value` module documentation](self) for usage examples. -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Hash)] pub enum Value { /// Represents a JSON null value. /// From 98f1a247de77176f605c39e225f81c6e80d93727 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 May 2024 16:38:34 -0700 Subject: [PATCH 400/508] Resolve unexpected_cfgs warning warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/mod.rs:31:7 | 31 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: expected names are: `clippy`, `debug_assertions`, `doc`, `docsrs`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/mod.rs:34:7 | 34 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/large_powers.rs:5:7 | 5 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/large_powers.rs:8:7 | 8 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/math.rs:40:7 | 40 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/math.rs:43:7 | 43 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/math.rs:46:7 | 46 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/math.rs:49:7 | 49 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/math.rs:53:7 | 53 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/math.rs:56:7 | 56 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/math.rs:59:7 | 59 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/math.rs:62:7 | 62 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/math.rs:82:7 | 82 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/math.rs:89:7 | 89 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/small_powers.rs:6:7 | 6 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> src/lexical/small_powers.rs:12:7 | 12 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> src/lexical/small_powers.rs:18:7 | 18 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/mod.rs:31:7 | 31 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: expected names are: `clippy`, `debug_assertions`, `doc`, `docsrs`, `doctest`, `feature`, `miri`, `overflow_checks`, `panic`, `proc_macro`, `relocation_model`, `rustfmt`, `sanitize`, `sanitizer_cfi_generalize_pointers`, `sanitizer_cfi_normalize_integers`, `target_abi`, `target_arch`, `target_endian`, `target_env`, `target_family`, `target_feature`, `target_has_atomic`, `target_has_atomic_equal_alignment`, `target_has_atomic_load_store`, `target_os`, `target_pointer_width`, `target_thread_local`, `target_vendor`, `test`, `ub_checks`, `unix`, `windows` = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/mod.rs:34:7 | 34 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/large_powers.rs:5:7 | 5 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/large_powers.rs:8:7 | 8 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/math.rs:40:7 | 40 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/math.rs:43:7 | 43 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/math.rs:46:7 | 46 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/math.rs:49:7 | 49 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/math.rs:53:7 | 53 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/math.rs:56:7 | 56 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/math.rs:59:7 | 59 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/math.rs:62:7 | 62 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/math.rs:82:7 | 82 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/math.rs:89:7 | 89 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/small_powers.rs:6:7 | 6 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/../src/lexical/small_powers.rs:12:7 | 12 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/../src/lexical/small_powers.rs:18:7 | 18 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_32` --> tests/lexical/math.rs:21:7 | 21 | #[cfg(limb_width_32)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_32)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `limb_width_64` --> tests/lexical/math.rs:26:7 | 26 | #[cfg(limb_width_64)] | ^^^^^^^^^^^^^ | = help: consider using a Cargo feature instead or adding `println!("cargo::rustc-check-cfg=cfg(limb_width_64)");` to the top of the `build.rs` = note: see for more information about checking conditional configuration --- build.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.rs b/build.rs index dd09e62a3..433ce12c1 100644 --- a/build.rs +++ b/build.rs @@ -3,6 +3,10 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rustc-check-cfg=cfg(integer128)"); + println!("cargo:rustc-check-cfg=cfg(limb_width_32)"); + println!("cargo:rustc-check-cfg=cfg(limb_width_64)"); + // 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 fdf99c7c38c1ee48554eaea66b701f470544d037 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 May 2024 16:51:35 -0700 Subject: [PATCH 401/508] Combine number PartialEq tests --- build.rs | 1 - tests/test.rs | 32 ++++++++++++++------------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/build.rs b/build.rs index 433ce12c1..f2124b43f 100644 --- a/build.rs +++ b/build.rs @@ -3,7 +3,6 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rustc-check-cfg=cfg(integer128)"); println!("cargo:rustc-check-cfg=cfg(limb_width_32)"); println!("cargo:rustc-check-cfg=cfg(limb_width_64)"); diff --git a/tests/test.rs b/tests/test.rs index 7e6adad76..16840660e 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2100,20 +2100,20 @@ fn issue_220() { assert_eq!(from_str::(r#"{"V": 0}"#).unwrap(), E::V(0)); } -macro_rules! number_partialeq_ok { - ($($n:expr)*) => { - $( - let value = to_value($n).unwrap(); - let s = $n.to_string(); - assert_eq!(value, $n); - assert_eq!($n, value); - assert_ne!(value, s); - )* - } -} - #[test] fn test_partialeq_number() { + macro_rules! number_partialeq_ok { + ($($n:expr)*) => { + $( + let value = to_value($n).unwrap(); + let s = $n.to_string(); + assert_eq!(value, $n); + assert_eq!($n, value); + assert_ne!(value, s); + )* + }; + } + number_partialeq_ok!(0 1 100 i8::MIN i8::MAX i16::MIN i16::MAX i32::MIN i32::MAX i64::MIN i64::MAX u8::MIN u8::MAX u16::MIN u16::MAX u32::MIN u32::MAX u64::MIN u64::MAX @@ -2122,13 +2122,9 @@ fn test_partialeq_number() { f32::consts::E f32::consts::PI f32::consts::LN_2 f32::consts::LOG2_E f64::consts::E f64::consts::PI f64::consts::LN_2 f64::consts::LOG2_E ); -} -#[test] -#[cfg(integer128)] -#[cfg(feature = "arbitrary_precision")] -fn test_partialeq_integer128() { - number_partialeq_ok!(i128::MIN i128::MAX u128::MIN u128::MAX) + #[cfg(feature = "arbitrary_precision")] + number_partialeq_ok!(i128::MIN i128::MAX u128::MIN u128::MAX); } #[test] From 4517c7a2d983a56aa403c651cabe2caf41136570 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 May 2024 17:00:26 -0700 Subject: [PATCH 402/508] PartialEq is not implemented between Value and 128-bit ints --- tests/test.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 16840660e..8c9af6b32 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2122,9 +2122,6 @@ fn test_partialeq_number() { f32::consts::E f32::consts::PI f32::consts::LN_2 f32::consts::LOG2_E f64::consts::E f64::consts::PI f64::consts::LN_2 f64::consts::LOG2_E ); - - #[cfg(feature = "arbitrary_precision")] - number_partialeq_ok!(i128::MIN i128::MAX u128::MIN u128::MAX); } #[test] From 0ae247ca63be75e6c7e6e0e9cd8916e618cf8f24 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 7 May 2024 17:05:54 -0700 Subject: [PATCH 403/508] Release 1.0.117 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 61e6335da..4817ac7a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.116" +version = "1.0.117" 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 eabd169f5..475cd4475 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.116")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.117")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From aaee69e86eedf51ded874b50c97c6b67a9a0f2d8 Mon Sep 17 00:00:00 2001 From: Sean Lynch <42618346+swlynch99@users.noreply.github.com> Date: Tue, 14 May 2024 14:52:58 -0700 Subject: [PATCH 404/508] Implement Deserializer for Map and &Map --- src/map.rs | 16 ++++ src/value/de.rs | 213 +++++++++++++++++++++++++++++++++--------------- 2 files changed, 163 insertions(+), 66 deletions(-) diff --git a/src/map.rs b/src/map.rs index 520cd6cf5..c4a2434d4 100644 --- a/src/map.rs +++ b/src/map.rs @@ -535,6 +535,22 @@ macro_rules! delegate_iterator { } } +impl<'de> de::IntoDeserializer<'de, crate::Error> for Map { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + +impl<'de> de::IntoDeserializer<'de, crate::Error> for &'de Map { + type Deserializer = Self; + + fn into_deserializer(self) -> Self::Deserializer { + self + } +} + ////////////////////////////////////////////////////////////////////////////// /// A view into a single entry in a map, which may either be vacant or occupied. diff --git a/src/value/de.rs b/src/value/de.rs index 936725635..f5bd2c9fe 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -191,17 +191,77 @@ fn visit_object<'de, V>(object: Map, visitor: V) -> Result, { - let len = object.len(); - let mut deserializer = MapDeserializer::new(object); - let map = tri!(visitor.visit_map(&mut deserializer)); - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(map) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in map", - )) + use serde::de::Deserializer; + + object.deserialize_any(visitor) +} + +impl<'de> serde::Deserializer<'de> for Map { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let len = self.len(); + let mut deserializer = MapDeserializer::new(self); + let map = tri!(visitor.visit_map(&mut deserializer)); + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in map", + )) + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + let mut iter = self.into_iter(); + let (variant, value) = match iter.next() { + Some(v) => v, + None => { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + }; + // enums are encoded in json as maps with a single key:value pair + if iter.next().is_some() { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + + visitor.visit_enum(EnumDeserializer { + variant, + value: Some(value), + }) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + drop(self); + visitor.visit_unit() + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct identifier } } @@ -253,34 +313,15 @@ impl<'de> serde::Deserializer<'de> for Value { #[inline] fn deserialize_enum( self, - _name: &str, - _variants: &'static [&'static str], + name: &'static str, + variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { let (variant, value) = match self { - Value::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - }; - // enums are encoded in json as maps with a single key:value pair - if iter.next().is_some() { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - (variant, Some(value)) - } + Value::Object(value) => return value.deserialize_enum(name, variants, visitor), Value::String(variant) => (variant, None), other => { return Err(serde::de::Error::invalid_type( @@ -696,17 +737,76 @@ fn visit_object_ref<'de, V>(object: &'de Map, visitor: V) -> Resu where V: Visitor<'de>, { - let len = object.len(); - let mut deserializer = MapRefDeserializer::new(object); - let map = tri!(visitor.visit_map(&mut deserializer)); - let remaining = deserializer.iter.len(); - if remaining == 0 { - Ok(map) - } else { - Err(serde::de::Error::invalid_length( - len, - &"fewer elements in map", - )) + use serde::de::Deserializer; + + object.deserialize_any(visitor) +} + +impl<'de> serde::Deserializer<'de> for &'de Map { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + let len = self.len(); + let mut deserializer = MapRefDeserializer::new(self); + let map = tri!(visitor.visit_map(&mut deserializer)); + let remaining = deserializer.iter.len(); + if remaining == 0 { + Ok(map) + } else { + Err(serde::de::Error::invalid_length( + len, + &"fewer elements in map", + )) + } + } + + fn deserialize_enum( + self, + _name: &'static str, + _variants: &'static [&'static str], + visitor: V, + ) -> Result + where + V: Visitor<'de>, + { + let mut iter = self.into_iter(); + let (variant, value) = match iter.next() { + Some(v) => v, + None => { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + }; + // enums are encoded in json as maps with a single key:value pair + if iter.next().is_some() { + return Err(serde::de::Error::invalid_value( + Unexpected::Map, + &"map with a single key", + )); + } + + visitor.visit_enum(EnumRefDeserializer { + variant, + value: Some(value), + }) + } + + fn deserialize_ignored_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_unit() + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct identifier } } @@ -752,34 +852,15 @@ impl<'de> serde::Deserializer<'de> for &'de Value { fn deserialize_enum( self, - _name: &str, - _variants: &'static [&'static str], + name: &'static str, + variants: &'static [&'static str], visitor: V, ) -> Result where V: Visitor<'de>, { let (variant, value) = match self { - Value::Object(value) => { - let mut iter = value.into_iter(); - let (variant, value) = match iter.next() { - Some(v) => v, - None => { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - }; - // enums are encoded in json as maps with a single key:value pair - if iter.next().is_some() { - return Err(serde::de::Error::invalid_value( - Unexpected::Map, - &"map with a single key", - )); - } - (variant, Some(value)) - } + Value::Object(value) => return value.deserialize_enum(name, variants, visitor), Value::String(variant) => (variant, None), other => { return Err(serde::de::Error::invalid_type( From 5e83bf7ef9f061fb07dbe4afbdf7df74457038d0 Mon Sep 17 00:00:00 2001 From: Sean Lynch <42618346+swlynch99@users.noreply.github.com> Date: Tue, 14 May 2024 14:59:01 -0700 Subject: [PATCH 405/508] Clean up visit_object and visit_object_ref These can now be replaced with a direct call to the Deserializer impl on Map (or &Map, as applicable). --- src/value/de.rs | 38 ++++++++++++-------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index f5bd2c9fe..98a2eb4f2 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -187,15 +187,6 @@ where } } -fn visit_object<'de, V>(object: Map, visitor: V) -> Result -where - V: Visitor<'de>, -{ - use serde::de::Deserializer; - - object.deserialize_any(visitor) -} - impl<'de> serde::Deserializer<'de> for Map { type Error = Error; @@ -282,7 +273,7 @@ impl<'de> serde::Deserializer<'de> for Value { #[cfg(not(any(feature = "std", feature = "alloc")))] Value::String(_) => unreachable!(), Value::Array(v) => visit_array(v, visitor), - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), } } @@ -461,7 +452,7 @@ impl<'de> serde::Deserializer<'de> for Value { V: Visitor<'de>, { match self { - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -477,7 +468,7 @@ impl<'de> serde::Deserializer<'de> for Value { { match self { Value::Array(v) => visit_array(v, visitor), - Value::Object(v) => visit_object(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -591,8 +582,10 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { where V: Visitor<'de>, { + use serde::de::Deserializer; + match self.value { - Some(Value::Object(v)) => visit_object(v, visitor), + Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", @@ -733,15 +726,6 @@ where } } -fn visit_object_ref<'de, V>(object: &'de Map, visitor: V) -> Result -where - V: Visitor<'de>, -{ - use serde::de::Deserializer; - - object.deserialize_any(visitor) -} - impl<'de> serde::Deserializer<'de> for &'de Map { type Error = Error; @@ -823,7 +807,7 @@ impl<'de> serde::Deserializer<'de> for &'de Value { Value::Number(n) => n.deserialize_any(visitor), Value::String(v) => visitor.visit_borrowed_str(v), Value::Array(v) => visit_array_ref(v, visitor), - Value::Object(v) => visit_object_ref(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), } } @@ -998,7 +982,7 @@ impl<'de> serde::Deserializer<'de> for &'de Value { V: Visitor<'de>, { match self { - Value::Object(v) => visit_object_ref(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -1014,7 +998,7 @@ impl<'de> serde::Deserializer<'de> for &'de Value { { match self { Value::Array(v) => visit_array_ref(v, visitor), - Value::Object(v) => visit_object_ref(v, visitor), + Value::Object(v) => v.deserialize_any(visitor), _ => Err(self.invalid_type(&visitor)), } } @@ -1111,8 +1095,10 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { where V: Visitor<'de>, { + use serde::de::Deserializer; + match self.value { - Some(Value::Object(v)) => visit_object_ref(v, visitor), + Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( other.unexpected(), &"struct variant", From c2540b035be6e6e790e54b0f810826837d9d0280 Mon Sep 17 00:00:00 2001 From: Sean Lynch <42618346+swlynch99@users.noreply.github.com> Date: Tue, 14 May 2024 15:05:12 -0700 Subject: [PATCH 406/508] Clean up deserialize_enum methods on Value and &Value --- src/value/de.rs | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index 98a2eb4f2..848d8ce1d 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -311,18 +311,17 @@ impl<'de> serde::Deserializer<'de> for Value { where V: Visitor<'de>, { - let (variant, value) = match self { - Value::Object(value) => return value.deserialize_enum(name, variants, visitor), - Value::String(variant) => (variant, None), - other => { - return Err(serde::de::Error::invalid_type( - other.unexpected(), - &"string or map", - )); - } - }; - - visitor.visit_enum(EnumDeserializer { variant, value }) + match self { + Value::Object(value) => value.deserialize_enum(name, variants, visitor), + Value::String(variant) => visitor.visit_enum(EnumDeserializer { + variant, + value: None, + }), + other => Err(serde::de::Error::invalid_type( + other.unexpected(), + &"string or map", + )), + } } #[inline] @@ -843,18 +842,17 @@ impl<'de> serde::Deserializer<'de> for &'de Value { where V: Visitor<'de>, { - let (variant, value) = match self { - Value::Object(value) => return value.deserialize_enum(name, variants, visitor), - Value::String(variant) => (variant, None), - other => { - return Err(serde::de::Error::invalid_type( - other.unexpected(), - &"string or map", - )); - } - }; - - visitor.visit_enum(EnumRefDeserializer { variant, value }) + match self { + Value::Object(value) => value.deserialize_enum(name, variants, visitor), + Value::String(variant) => visitor.visit_enum(EnumRefDeserializer { + variant, + value: None, + }), + other => Err(serde::de::Error::invalid_type( + other.unexpected(), + &"string or map", + )), + } } #[inline] From 8e475f13c2f8abb80e4b5072e45bf3914f229db5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 18 May 2024 21:06:37 -0700 Subject: [PATCH 407/508] Rely on docs.rs to define --cfg=docsrs by default --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4817ac7a1..4863c56d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,7 +34,7 @@ doc-scrape-examples = false [package.metadata.docs.rs] features = ["preserve_order", "raw_value", "unbounded_depth"] targets = ["x86_64-unknown-linux-gnu"] -rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"] +rustdoc-args = ["--generate-link-to-definition"] [package.metadata.playground] features = ["raw_value"] From 18e9b89acb567e5de624bbee289680187507a7a2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jun 2024 21:29:39 -0700 Subject: [PATCH 408/508] Resolve needless_raw_string_hashes pedantic clippy lint in test warning: unnecessary hashes around raw string literal --> tests/test.rs:2313:16 | 2313 | assert_eq!(r#"42"#, array_from_str[1].get()); | ^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes = note: `-W clippy::needless-raw-string-hashes` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::needless_raw_string_hashes)]` help: remove all the hashes around the string literal | 2313 - assert_eq!(r#"42"#, array_from_str[1].get()); 2313 + assert_eq!(r"42", array_from_str[1].get()); | warning: unnecessary hashes around raw string literal --> tests/test.rs:2315:16 | 2315 | assert_eq!(r#"null"#, array_from_str[3].get()); | ^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 2315 - assert_eq!(r#"null"#, array_from_str[3].get()); 2315 + assert_eq!(r"null", array_from_str[3].get()); | warning: unnecessary hashes around raw string literal --> tests/test.rs:2392:16 | 2392 | assert_eq!(r#"42"#, array_from_str[1].get()); | ^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 2392 - assert_eq!(r#"42"#, array_from_str[1].get()); 2392 + assert_eq!(r"42", array_from_str[1].get()); | warning: unnecessary hashes around raw string literal --> tests/test.rs:2394:16 | 2394 | assert_eq!(r#"null"#, array_from_str[3].get()); | ^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 2394 - assert_eq!(r#"null"#, array_from_str[3].get()); 2394 + assert_eq!(r"null", array_from_str[3].get()); | warning: unnecessary hashes around raw string literal --> tests/test.rs:2399:16 | 2399 | assert_eq!(r#"42"#, array_from_reader[1].get()); | ^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 2399 - assert_eq!(r#"42"#, array_from_reader[1].get()); 2399 + assert_eq!(r"42", array_from_reader[1].get()); | warning: unnecessary hashes around raw string literal --> tests/test.rs:2401:16 | 2401 | assert_eq!(r#"null"#, array_from_reader[3].get()); | ^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_raw_string_hashes help: remove all the hashes around the string literal | 2401 - assert_eq!(r#"null"#, array_from_reader[3].get()); 2401 + assert_eq!(r"null", array_from_reader[3].get()); | --- tests/test.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test.rs b/tests/test.rs index 8c9af6b32..ef2b12d0d 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2311,9 +2311,9 @@ fn test_borrowed_raw_value() { let array_from_str: Vec<&RawValue> = serde_json::from_str(r#"["a", 42, {"foo": "bar"}, null]"#).unwrap(); assert_eq!(r#""a""#, array_from_str[0].get()); - assert_eq!(r#"42"#, array_from_str[1].get()); + assert_eq!("42", array_from_str[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_str[2].get()); - assert_eq!(r#"null"#, array_from_str[3].get()); + assert_eq!("null", array_from_str[3].get()); let array_to_string = serde_json::to_string(&array_from_str).unwrap(); assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); @@ -2390,16 +2390,16 @@ fn test_boxed_raw_value() { let array_from_str: Vec> = serde_json::from_str(r#"["a", 42, {"foo": "bar"}, null]"#).unwrap(); assert_eq!(r#""a""#, array_from_str[0].get()); - assert_eq!(r#"42"#, array_from_str[1].get()); + assert_eq!("42", array_from_str[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_str[2].get()); - assert_eq!(r#"null"#, array_from_str[3].get()); + assert_eq!("null", array_from_str[3].get()); let array_from_reader: Vec> = serde_json::from_reader(br#"["a", 42, {"foo": "bar"}, null]"#.as_ref()).unwrap(); assert_eq!(r#""a""#, array_from_reader[0].get()); - assert_eq!(r#"42"#, array_from_reader[1].get()); + assert_eq!("42", array_from_reader[1].get()); assert_eq!(r#"{"foo": "bar"}"#, array_from_reader[2].get()); - assert_eq!(r#"null"#, array_from_reader[3].get()); + assert_eq!("null", array_from_reader[3].get()); let array_to_string = serde_json::to_string(&array_from_str).unwrap(); assert_eq!(r#"["a",42,{"foo": "bar"},null]"#, array_to_string); From b83d243e711b65e32925510343df566080776dd5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jun 2024 21:31:20 -0700 Subject: [PATCH 409/508] Ignore large_digit_groups pedantic clippy lint in test warning: digit groups should be smaller --> tests/test.rs:962:9 | 962 | 51.24817837550540_4, // 51.2481783755054_1 | ^^^^^^^^^^^^^^^^^^^ help: consider: `51.248_178_375_505_404` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups = note: `-W clippy::large-digit-groups` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::large_digit_groups)]` warning: digit groups should be smaller --> tests/test.rs:963:10 | 963 | -93.3113703768803_3, // -93.3113703768803_2 | ^^^^^^^^^^^^^^^^^^ help: consider: `93.311_370_376_880_33` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups warning: digit groups should be smaller --> tests/test.rs:964:10 | 964 | -36.5739948427534_36, // -36.5739948427534_4 | ^^^^^^^^^^^^^^^^^^^ help: consider: `36.573_994_842_753_436` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups warning: digit groups should be smaller --> tests/test.rs:965:9 | 965 | 52.31400820410624_4, // 52.31400820410624_ | ^^^^^^^^^^^^^^^^^^^ help: consider: `52.314_008_204_106_244` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups warning: digit groups should be smaller --> tests/test.rs:966:9 | 966 | 97.4536532003468_5, // 97.4536532003468_4 | ^^^^^^^^^^^^^^^^^^ help: consider: `97.453_653_200_346_85` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index ef2b12d0d..11f185eac 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -7,6 +7,7 @@ clippy::float_cmp, clippy::incompatible_msrv, // https://github.com/rust-lang/rust-clippy/issues/12257 clippy::items_after_statements, + clippy::large_digit_groups, clippy::let_underscore_untyped, clippy::shadow_unrelated, clippy::too_many_lines, From c9b9f88c1add99e20fefcd377a29b00715b4e8c3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jun 2024 21:24:45 -0700 Subject: [PATCH 410/508] Run more of test suite in preserve_order mode --- tests/stream.rs | 1 - tests/test.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/stream.rs b/tests/stream.rs index ec6b9e3d0..fa52cedeb 100644 --- a/tests/stream.rs +++ b/tests/stream.rs @@ -1,4 +1,3 @@ -#![cfg(not(feature = "preserve_order"))] #![allow(clippy::assertions_on_result_states)] use serde_json::{json, Deserializer, Value}; diff --git a/tests/test.rs b/tests/test.rs index 11f185eac..c18254a85 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,3 @@ -#![cfg(not(feature = "preserve_order"))] #![allow( clippy::assertions_on_result_states, clippy::cast_precision_loss, From fa8aa223c66018cae73efa57fd276af329a343f4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 1 Jun 2024 22:13:43 -0700 Subject: [PATCH 411/508] Fill in ignore reasons in all #[ignore] attributes --- tests/compiletest.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/compiletest.rs b/tests/compiletest.rs index 7974a6249..23a6a065e 100644 --- a/tests/compiletest.rs +++ b/tests/compiletest.rs @@ -1,5 +1,5 @@ -#[rustversion::attr(not(nightly), ignore)] -#[cfg_attr(miri, ignore)] +#[rustversion::attr(not(nightly), ignore = "requires nightly")] +#[cfg_attr(miri, ignore = "incompatible with miri")] #[test] fn ui() { let t = trybuild::TestCases::new(); From 4c894eaa18181860f3f5cf3fb11e18d4ee454120 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 7 Jun 2024 20:12:07 -0700 Subject: [PATCH 412/508] Delete unused associated constant from lexical warning: associated constant `NEGATIVE_INFINITY_BITS` is never used --> src/lexical/num.rs:223:11 | 175 | pub trait Float: Number { | ----- associated constant in this trait ... 223 | const NEGATIVE_INFINITY_BITS: Self::Unsigned; | ^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default --- src/lexical/num.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/lexical/num.rs b/src/lexical/num.rs index 75eee01b8..ca0d44eb9 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -219,8 +219,6 @@ pub trait Float: Number { /// Positive infinity as bits. const INFINITY_BITS: Self::Unsigned; - /// Positive infinity as bits. - const NEGATIVE_INFINITY_BITS: Self::Unsigned; /// Size of the significand (mantissa) without hidden bit. const MANTISSA_SIZE: i32; /// Bias of the exponent @@ -320,7 +318,6 @@ impl Float for f32 { const HIDDEN_BIT_MASK: u32 = 0x00800000; const MANTISSA_MASK: u32 = 0x007FFFFF; const INFINITY_BITS: u32 = 0x7F800000; - const NEGATIVE_INFINITY_BITS: u32 = Self::INFINITY_BITS | Self::SIGN_MASK; const MANTISSA_SIZE: i32 = 23; const EXPONENT_BIAS: i32 = 127 + Self::MANTISSA_SIZE; const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; @@ -379,7 +376,6 @@ impl Float for f64 { const HIDDEN_BIT_MASK: u64 = 0x0010000000000000; const MANTISSA_MASK: u64 = 0x000FFFFFFFFFFFFF; const INFINITY_BITS: u64 = 0x7FF0000000000000; - const NEGATIVE_INFINITY_BITS: u64 = Self::INFINITY_BITS | Self::SIGN_MASK; const MANTISSA_SIZE: i32 = 52; const EXPONENT_BIAS: i32 = 1023 + Self::MANTISSA_SIZE; const DENORMAL_EXPONENT: i32 = 1 - Self::EXPONENT_BIAS; From 24d868f4e9be428afa1c744f8218a32660a1e0bf Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 7 Jun 2024 20:23:56 -0700 Subject: [PATCH 413/508] Another lexical const that is unused, though not in test warning: associated constant `SIGN_MASK` is never used --> src/lexical/num.rs:210:11 | 175 | pub trait Float: Number { | ----- associated constant in this trait ... 210 | const SIGN_MASK: Self::Unsigned; | ^^^^^^^^^ | = note: `#[warn(dead_code)]` on by default --- src/lexical/num.rs | 4 ---- tests/lexical/num.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/src/lexical/num.rs b/src/lexical/num.rs index ca0d44eb9..3f3914021 100644 --- a/src/lexical/num.rs +++ b/src/lexical/num.rs @@ -206,8 +206,6 @@ pub trait Float: Number { // MASKS - /// Bitmask for the sign bit. - const SIGN_MASK: Self::Unsigned; /// Bitmask for the exponent, including the hidden bit. const EXPONENT_MASK: Self::Unsigned; /// Bitmask for the hidden bit in exponent, which is an implicit 1 in the fraction. @@ -313,7 +311,6 @@ impl Float for f32 { const ZERO: f32 = 0.0; const MAX_DIGITS: usize = 114; - const SIGN_MASK: u32 = 0x80000000; const EXPONENT_MASK: u32 = 0x7F800000; const HIDDEN_BIT_MASK: u32 = 0x00800000; const MANTISSA_MASK: u32 = 0x007FFFFF; @@ -371,7 +368,6 @@ impl Float for f64 { const ZERO: f64 = 0.0; const MAX_DIGITS: usize = 769; - const SIGN_MASK: u64 = 0x8000000000000000; const EXPONENT_MASK: u64 = 0x7FF0000000000000; const HIDDEN_BIT_MASK: u64 = 0x0010000000000000; const MANTISSA_MASK: u64 = 0x000FFFFFFFFFFFFF; diff --git a/tests/lexical/num.rs b/tests/lexical/num.rs index 1a94be013..e7d08652e 100644 --- a/tests/lexical/num.rs +++ b/tests/lexical/num.rs @@ -63,7 +63,6 @@ fn check_float(x: T) { assert!(T::from_bits(x.to_bits()) == x); // Check properties - let _ = x.to_bits() & T::SIGN_MASK; let _ = x.to_bits() & T::EXPONENT_MASK; let _ = x.to_bits() & T::HIDDEN_BIT_MASK; let _ = x.to_bits() & T::MANTISSA_MASK; From 168ef4bb14fdd4a28787b1cb025d8f28f12eecb3 Mon Sep 17 00:00:00 2001 From: Ingvuil Date: Mon, 17 Jun 2024 22:40:22 +0100 Subject: [PATCH 414/508] Add i128 deserialization Implemented serde::de::Visitor::visit_i128. This fixes compatability with Ipld::Integer when using DagJsonCodec. --- src/number.rs | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ src/value/de.rs | 5 ++++ 2 files changed, 66 insertions(+) diff --git a/src/number.rs b/src/number.rs index 878a3dcb2..07f2bdf16 100644 --- a/src/number.rs +++ b/src/number.rs @@ -202,6 +202,31 @@ impl Number { self.n.parse().ok() } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let big = i64::MAX as u64 + 10; + /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// assert_eq!(v["c"].as_i128(), None); + /// ``` + #[inline] + pub fn as_i128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as i128), + N::NegInt(n) => Some(n as i128), + N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. /// @@ -279,6 +304,33 @@ impl Number { } } + /// Converts an `i128` to a `Number`. Greater than u64::MAX values are not JSON + /// numbers. + /// + /// ``` + /// # use std::i128; + /// # + /// # use serde_json::Number; + /// # + /// assert!(Number::from_i128(256).is_some()); + /// ``` + #[inline] + pub fn from_i128(i: i128) -> Option { + match u64::try_from(i) { + Ok(u) => Some(Number{ n: N::PosInt(u) }), + Err(_) => match i64::try_from(i) { + Ok(i) => { + if i >= 0 { + Some(Number{ n: N::PosInt(i as u64) }) + } else { + Some(Number{ n: N::NegInt(i) }) + } + }, + Err(_) => None + } + } + } + /// Returns the exact original JSON representation that this Number was /// parsed from. /// @@ -414,6 +466,13 @@ impl<'de> Deserialize<'de> for Number { Ok(value.into()) } + fn visit_i128(self, value: i128) -> Result + where + E: de::Error, + { + Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(value.into()) @@ -543,6 +602,8 @@ macro_rules! deserialize_any { return visitor.visit_u64(u); } else if let Some(i) = self.as_i64() { return visitor.visit_i64(i); + } else if let Some(i) = self.as_i128() { + return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { if ryu::Buffer::new().format_finite(f) == self.n || f.to_string() == self.n { return visitor.visit_f64(f); diff --git a/src/value/de.rs b/src/value/de.rs index 936725635..c38253aff 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -44,6 +44,11 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + #[inline] + fn visit_i128(self, value: i128) -> Result { + Ok(Number::from_i128(value).map_or(Value::Null, Value::Number)) + } + #[inline] fn visit_u64(self, value: u64) -> Result { Ok(Value::Number(value.into())) From 4f12af0ce0498e3b9eed0840487171bd06fac317 Mon Sep 17 00:00:00 2001 From: Ingvuil Date: Mon, 17 Jun 2024 23:20:01 +0100 Subject: [PATCH 415/508] Fix tests --- src/number.rs | 42 ++++++++++++++++++++++++++++++++++++------ src/value/mod.rs | 18 ++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/src/number.rs b/src/number.rs index 07f2bdf16..1fccf2e3b 100644 --- a/src/number.rs +++ b/src/number.rs @@ -208,12 +208,10 @@ impl Number { /// ``` /// # use serde_json::json; /// # - /// let big = i64::MAX as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); + /// let v = json!({ "a": 64, "b": 256.0 }); /// /// assert_eq!(v["a"].as_i128(), Some(64)); /// assert_eq!(v["b"].as_i128(), None); - /// assert_eq!(v["c"].as_i128(), None); /// ``` #[inline] pub fn as_i128(&self) -> Option { @@ -317,13 +315,45 @@ impl Number { #[inline] pub fn from_i128(i: i128) -> Option { match u64::try_from(i) { - Ok(u) => Some(Number{ n: N::PosInt(u) }), + Ok(u) => { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(u) + } + #[cfg(feature = "arbitrary_precision")] + { + u.to_string() + } + }; + Some(Number{ n }) + }, Err(_) => match i64::try_from(i) { Ok(i) => { if i >= 0 { - Some(Number{ n: N::PosInt(i as u64) }) + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(i as u64) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) } else { - Some(Number{ n: N::NegInt(i) }) + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::NegInt(i) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number{ n }) } }, Err(_) => None diff --git a/src/value/mod.rs b/src/value/mod.rs index b3f51ea0d..4f246abf2 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -635,6 +635,24 @@ impl Value { } } + /// If the `Value` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + /// + /// ``` + /// # use serde_json::json; + /// # + /// let v = json!({ "a": 64, "b": 256.0 }); + /// + /// assert_eq!(v["a"].as_i128(), Some(64)); + /// assert_eq!(v["b"].as_i128(), None); + /// ``` + pub fn as_i128(&self) -> Option { + match self { + Value::Number(n) => n.as_i128(), + _ => None, + } + } + /// If the `Value` is an integer, represent it as u64 if possible. Returns /// None otherwise. /// From 0af2bdae9483a9c54f8e032fcbf357dbe7803c49 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Jun 2024 21:36:01 -0700 Subject: [PATCH 416/508] Resolve semicolon_if_nothing_returned pedantic clippy lint from PR 1127 warning: consider adding a `;` to the last statement for consistent formatting --> src/map.rs:377:9 | 377 | self.map.hash(state) | ^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `self.map.hash(state);` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned = note: `-W clippy::semicolon-if-nothing-returned` implied by `-W clippy::pedantic` = help: to override `-W clippy::pedantic` add `#[allow(clippy::semicolon_if_nothing_returned)]` --- src/map.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index 4a90830c4..5360a69e0 100644 --- a/src/map.rs +++ b/src/map.rs @@ -374,7 +374,7 @@ impl Eq for Map {} impl Hash for Map { #[inline] fn hash(&self, state: &mut H) { - self.map.hash(state) + self.map.hash(state); } } #[cfg(feature = "preserve_order")] From 5e7bedc0a0e19ecda1c15a412ab7c69569f4aa84 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Jun 2024 21:33:38 -0700 Subject: [PATCH 417/508] Touch up PR 1127 --- src/map.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/map.rs b/src/map.rs index 5360a69e0..c0ad32747 100644 --- a/src/map.rs +++ b/src/map.rs @@ -12,7 +12,7 @@ use alloc::string::String; use alloc::vec::Vec; use core::borrow::Borrow; use core::fmt::{self, Debug}; -use core::hash::Hash; +use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; #[cfg(feature = "preserve_order")] use core::mem; @@ -373,15 +373,15 @@ impl Eq for Map {} #[cfg(not(feature = "preserve_order"))] impl Hash for Map { #[inline] - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { self.map.hash(state); } } + #[cfg(feature = "preserve_order")] impl Hash for Map { - #[inline] - fn hash(&self, state: &mut H) { - let mut kv = self.map.iter().collect::>(); + fn hash(&self, state: &mut H) { + let mut kv = Vec::from_iter(&self.map); kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); kv.hash(state); } From 51d94ebdc07127de22fb655bdcf6a01d119492d5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Jun 2024 21:34:37 -0700 Subject: [PATCH 418/508] Combine Map's Hash into one impl --- src/map.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/map.rs b/src/map.rs index c0ad32747..d62513daf 100644 --- a/src/map.rs +++ b/src/map.rs @@ -370,20 +370,19 @@ impl PartialEq for Map { impl Eq for Map {} -#[cfg(not(feature = "preserve_order"))] impl Hash for Map { - #[inline] fn hash(&self, state: &mut H) { - self.map.hash(state); - } -} + #[cfg(not(feature = "preserve_order"))] + { + self.map.hash(state); + } -#[cfg(feature = "preserve_order")] -impl Hash for Map { - fn hash(&self, state: &mut H) { - let mut kv = Vec::from_iter(&self.map); - kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); - kv.hash(state); + #[cfg(feature = "preserve_order")] + { + let mut kv = Vec::from_iter(&self.map); + kv.sort_unstable_by(|a, b| a.0.cmp(b.0)); + kv.hash(state); + } } } From c4f24f3be29a3d096d3ac7b1d5594777a613ec0d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 24 Jun 2024 21:39:20 -0700 Subject: [PATCH 419/508] Release 1.0.118 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4863c56d9..a840f7915 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.117" +version = "1.0.118" 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 475cd4475..9d4b6497a 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.117")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.118")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From a83fe96ae2a202925f1caa7abc51991f321d7c22 Mon Sep 17 00:00:00 2001 From: haouvw Date: Wed, 26 Jun 2024 15:29:16 +0800 Subject: [PATCH 420/508] chore: remove repeat words Signed-off-by: haouvw --- src/lexical/bhcomp.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lexical/bhcomp.rs b/src/lexical/bhcomp.rs index 1f2a7bbde..e52b1c9b9 100644 --- a/src/lexical/bhcomp.rs +++ b/src/lexical/bhcomp.rs @@ -75,13 +75,13 @@ where // FLOAT OPS -/// Calculate `b` from a a representation of `b` as a float. +/// Calculate `b` from a representation of `b` as a float. #[inline] pub(super) fn b_extended(f: F) -> ExtendedFloat { ExtendedFloat::from_float(f) } -/// Calculate `b+h` from a a representation of `b` as a float. +/// Calculate `b+h` from a representation of `b` as a float. #[inline] pub(super) fn bh_extended(f: F) -> ExtendedFloat { // None of these can overflow. From 309ef6b8870e47622a283061cbda3f5514bfaf0d Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sun, 30 Jun 2024 09:53:11 -0700 Subject: [PATCH 421/508] Add Map::shift_insert() This method inserts a key-value pair in the map at the given index. If the map did not have this key present, `None` is returned. If the map did have this key present, the key is moved to the new position, the value is updated, and the old value is returned. This is useful when you want to insert a key-value pair at a specific position in the map, and is a necessary method when writing a JSON editor that can mutate the keys in a JSON object. --- src/map.rs | 11 +++++++++++ tests/map.rs | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/src/map.rs b/src/map.rs index d62513daf..505be0eae 100644 --- a/src/map.rs +++ b/src/map.rs @@ -127,6 +127,17 @@ impl Map { self.map.insert(k, v) } + /// Insert a key-value pair in the map at the given index. + /// + /// If the map did not have this key present, `None` is returned. + /// + /// If the map did have this key present, the key is moved to the new + /// position, the value is updated, and the old value is returned. + #[cfg(feature = "preserve_order")] + pub fn shift_insert(&mut self, index: usize, k: String, v: Value) -> Option { + self.map.shift_insert(index, k, v) + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// diff --git a/tests/map.rs b/tests/map.rs index 538cd16ae..c3c64d70b 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -15,6 +15,17 @@ fn test_preserve_order() { assert_eq!(keys, EXPECTED); } +#[test] +#[cfg(feature = "preserve_order")] +fn test_shift_insert() { + let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); + let val = v.as_object_mut().unwrap(); + val.shift_insert(0, "d".to_string(), Value::Null); + + let keys: Vec<_> = val.keys().collect(); + assert_eq!(keys, &["d", "b", "a", "c"]); +} + #[test] fn test_append() { // Sorted order From 352b7abf007cf3b9b063b01e0b1e8f6af62a4e39 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 30 Jun 2024 11:14:46 -0700 Subject: [PATCH 422/508] Document the cfg required for Map::shift_insert to exist --- src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map.rs b/src/map.rs index 505be0eae..b36806e06 100644 --- a/src/map.rs +++ b/src/map.rs @@ -134,6 +134,7 @@ impl Map { /// If the map did have this key present, the key is moved to the new /// position, the value is updated, and the old value is returned. #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] pub fn shift_insert(&mut self, index: usize, k: String, v: Value) -> Option { self.map.shift_insert(index, k, v) } From 8878cd7c042a5f94ae4ee9889cbcbd12cc5ce334 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 30 Jun 2024 11:15:08 -0700 Subject: [PATCH 423/508] Make shift_insert available for inlining like other Map methods --- src/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/map.rs b/src/map.rs index b36806e06..3ffff15f8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -135,6 +135,7 @@ impl Map { /// position, the value is updated, and the old value is returned. #[cfg(feature = "preserve_order")] #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] pub fn shift_insert(&mut self, index: usize, k: String, v: Value) -> Option { self.map.shift_insert(index, k, v) } From b48b9a3a0c09952579e98c8940fe0d1ee4aae588 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 30 Jun 2024 11:19:43 -0700 Subject: [PATCH 424/508] Release 1.0.119 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a840f7915..e8ee3f27a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.118" +version = "1.0.119" 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 9d4b6497a..a4958efa1 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.118")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.119")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 3480feda7b572d33992544061a8e0fbf8610a803 Mon Sep 17 00:00:00 2001 From: Christophe Forycki Date: Mon, 1 Jul 2024 18:00:31 +0200 Subject: [PATCH 425/508] fix: indexmap minimal version with Map::shift_insert() --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e8ee3f27a..90e09adec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/serde-rs/json" rust-version = "1.56" [dependencies] -indexmap = { version = "2.2.1", optional = true } +indexmap = { version = "2.2.3", optional = true } itoa = "1.0" ryu = "1.0" serde = { version = "1.0.194", default-features = false } From bcedc3d96bcc33184f16d63eab397295e2193350 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 1 Jul 2024 10:29:33 -0700 Subject: [PATCH 426/508] Release 1.0.120 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90e09adec..6a8e74526 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.119" +version = "1.0.120" 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 a4958efa1..f3da68dd6 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.119")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.120")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From fcb5e83e44abe0f9c27c755a240a6ad56312c090 Mon Sep 17 00:00:00 2001 From: David Pathakjee Date: Tue, 2 Jul 2024 12:22:42 -0700 Subject: [PATCH 427/508] Correct documentation URL for Value's Index impl. The current id doesn't exist, so the link just goes to the top of the docs for Value, rather than to the intended section covering `impl Index for Value`. --- src/value/index.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/index.rs b/src/value/index.rs index 891ca8ef7..c56135b33 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -14,7 +14,7 @@ use core::ops; /// /// [`get`]: ../enum.Value.html#method.get /// [`get_mut`]: ../enum.Value.html#method.get_mut -/// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E +/// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E-for-Value /// /// This trait is sealed and cannot be implemented for types outside of /// `serde_json`. From 3f1c6de4af28b1f6c5100da323f2bffaf7c2083f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 11 Jul 2024 20:09:09 -0700 Subject: [PATCH 428/508] Ignore byte_char_slices clippy lint in test warning: can be more succinctly written as a byte str --> tests/test.rs:1108:13 | 1108 | &[b'"', b'\n', b'"'], | ^^^^^^^^^^^^^^^^^^^^ help: try: `b"\"\n\""` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices = note: `-W clippy::byte-char-slices` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::byte_char_slices)]` warning: can be more succinctly written as a byte str --> tests/test.rs:1112:13 | 1112 | &[b'"', b'\x1F', b'"'], | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\"\x1F\""` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices --- tests/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test.rs b/tests/test.rs index c18254a85..71087162b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,5 +1,6 @@ #![allow( clippy::assertions_on_result_states, + clippy::byte_char_slices, clippy::cast_precision_loss, clippy::derive_partial_eq_without_eq, clippy::excessive_precision, From 6a306e6ee9f47f3b37088217ffe3ebe9bbb54e5a Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Fri, 26 Jul 2024 20:00:43 +0300 Subject: [PATCH 429/508] Move call to tri! out of check_recursion! --- src/de.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/de.rs b/src/de.rs index c7774f687..bfde371a1 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1870,8 +1870,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { Some(b'{') => { check_recursion! { self.eat_char(); - let value = tri!(visitor.visit_enum(VariantAccess::new(self))); + let ret = visitor.visit_enum(VariantAccess::new(self)); } + let value = tri!(ret); match tri!(self.parse_whitespace()) { Some(b'}') => { From b1edc7d13f72880fd0ac569403a409e5f7961d5f Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sat, 27 Jul 2024 00:22:11 +0300 Subject: [PATCH 430/508] Optimize position search in error path Translating index into a line/column pair takes considerable time. Notably, the JSON benchmark modified to run on malformed data spends around 50% of the CPU time generating the error object. While it is generally assumed that the cold path is quite slow, such a drastic pessimization may be unexpected, especially when a faster implementation exists. Using vectorized routines provided by the memchr crate increases performance of the failure path by 2x on average. Old implementation: DOM STRUCT data/canada.json 122 MB/s 168 MB/s data/citm_catalog.json 135 MB/s 195 MB/s data/twitter.json 142 MB/s 226 MB/s New implementation: DOM STRUCT data/canada.json 216 MB/s 376 MB/s data/citm_catalog.json 238 MB/s 736 MB/s data/twitter.json 210 MB/s 492 MB/s In comparison, the performance of the happy path is: DOM STRUCT data/canada.json 283 MB/s 416 MB/s data/citm_catalog.json 429 MB/s 864 MB/s data/twitter.json 275 MB/s 541 MB/s While this introduces a new dependency, memchr is much faster to compile than serde, so compile time does not increase significantly. Additionally, memchr provides a more efficient SWAR-based implementation of both the memchr and count routines even without std, providing benefits for embedded uses as well. --- Cargo.toml | 3 ++- src/read.rs | 19 +++++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a8e74526..8f8b45582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ rust-version = "1.56" [dependencies] indexmap = { version = "2.2.3", optional = true } itoa = "1.0" +memchr = { version = "2", default-features = false } ryu = "1.0" serde = { version = "1.0.194", default-features = false } @@ -45,7 +46,7 @@ features = ["raw_value"] [features] default = ["std"] -std = ["serde/std"] +std = ["memchr/std", "serde/std"] # Provide integration for heap-allocated collections without depending on the # rest of the Rust standard library. diff --git a/src/read.rs b/src/read.rs index a426911c7..e03e13f28 100644 --- a/src/read.rs +++ b/src/read.rs @@ -415,19 +415,14 @@ impl<'a> SliceRead<'a> { } fn position_of_index(&self, i: usize) -> Position { - let mut position = Position { line: 1, column: 0 }; - for ch in &self.slice[..i] { - match *ch { - b'\n' => { - position.line += 1; - position.column = 0; - } - _ => { - position.column += 1; - } - } + let start_of_line = match memchr::memrchr(b'\n', &self.slice[..i]) { + Some(position) => position + 1, + None => 0, + }; + Position { + line: 1 + memchr::memchr_iter(b'\n', &self.slice[..start_of_line]).count(), + column: i - start_of_line, } - position } /// The big optimization here over IoRead is that if the string contains no From eca2658a22cb39952783cb6914eb18242659f66a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 28 Jul 2024 14:03:27 -0700 Subject: [PATCH 431/508] Release 1.0.121 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8f8b45582..9fd5fc7f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.120" +version = "1.0.121" 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 f3da68dd6..3225e26f0 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.120")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.121")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 3faae037e9b025a656464d61a05988915488e291 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 29 Jul 2024 11:54:21 +0300 Subject: [PATCH 432/508] Vectorize string parsing --- src/read.rs | 58 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/read.rs b/src/read.rs index e03e13f28..7435dc702 100644 --- a/src/read.rs +++ b/src/read.rs @@ -2,6 +2,7 @@ use crate::error::{Error, ErrorCode, Result}; use alloc::vec::Vec; use core::char; use core::cmp; +use core::mem; use core::ops::Deref; use core::str; @@ -425,6 +426,51 @@ impl<'a> SliceRead<'a> { } } + #[inline(always)] + fn skip_to_escape(&mut self, forbid_control_characters: bool) { + let rest = &self.slice[self.index..]; + let end = self.index + memchr::memchr2(b'"', b'\\', rest).unwrap_or(rest.len()); + + if !forbid_control_characters { + self.index = end; + return; + } + + // We now wish to check if the chunk contains a byte in range 0x00..=0x1F. Ideally, this + // would be integrated this into the memchr2 check above, but memchr does not support this + // at the moment. Therefore, use a variation on Mycroft's algorithm [1] to provide + // performance better than a naive loop. It runs faster than just a single memchr call on + // benchmarks and is faster than both SSE2 and AVX-based code, and it's cross-platform, so + // probably the right fit. + // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ + + // Pad the chunk to a whole count of units if possible. This ensures that SWAR code is used + // to handle the tail in the hot path. + let block_end = (self.index + (end - self.index).next_multiple_of(mem::size_of::())) + .min(self.slice.len()); + let mut block = &self.slice[self.index..block_end]; + + while let Some((chars, block_remainder)) = block.split_first_chunk() { + const ONE_BYTES: usize = usize::MAX / 255; + let chars = usize::from_ne_bytes(*chars); + let mask = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars & (ONE_BYTES << 7); + + if mask != 0 { + let control_index = block_end - block.len() + mask.trailing_zeros() as usize / 8; + self.index = control_index.min(end); + return; + } + + block = block_remainder; + } + + if let Some(offset) = block.iter().position(|&c| c <= 0x1F) { + self.index = (block_end - block.len() + offset).min(end); + } else { + self.index = end; + } + } + /// The big optimization here over IoRead is that if the string contains no /// backslash escape sequences, the returned &str is a slice of the raw JSON /// data so we avoid copying into the scratch space. @@ -442,9 +488,7 @@ impl<'a> SliceRead<'a> { let mut start = self.index; loop { - while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] { - self.index += 1; - } + self.skip_to_escape(validate); if self.index == self.slice.len() { return error(self, ErrorCode::EofWhileParsingString); } @@ -470,9 +514,7 @@ impl<'a> SliceRead<'a> { } _ => { self.index += 1; - if validate { - return error(self, ErrorCode::ControlCharacterWhileParsingString); - } + return error(self, ErrorCode::ControlCharacterWhileParsingString); } } } @@ -538,9 +580,7 @@ impl<'a> Read<'a> for SliceRead<'a> { fn ignore_str(&mut self) -> Result<()> { loop { - while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] { - self.index += 1; - } + self.skip_to_escape(true); if self.index == self.slice.len() { return error(self, ErrorCode::EofWhileParsingString); } From 03ceee9eb1be8a2f792da8ed4c5992cb61219396 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 29 Jul 2024 12:17:33 +0300 Subject: [PATCH 433/508] Replace ESCAPE array with is_escape fn This is not backed by benchmarks, but it seems reasonable that we'd be more starved for cache than CPU in IO-bound tasks. It also simplifies code a bit and frees up some memory, which is probably a good thing. --- src/read.rs | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) diff --git a/src/read.rs b/src/read.rs index 7435dc702..a79d64c9b 100644 --- a/src/read.rs +++ b/src/read.rs @@ -222,7 +222,7 @@ where { loop { let ch = tri!(next_or_eof(self)); - if !ESCAPE[ch as usize] { + if !is_escape(ch) { scratch.push(ch); continue; } @@ -343,7 +343,7 @@ where fn ignore_str(&mut self) -> Result<()> { loop { let ch = tri!(next_or_eof(self)); - if !ESCAPE[ch as usize] { + if !is_escape(ch) { continue; } match ch { @@ -819,33 +819,11 @@ pub trait Fused: private::Sealed {} impl<'a> Fused for SliceRead<'a> {} impl<'a> Fused for StrRead<'a> {} -// Lookup table of bytes that must be escaped. A value of true at index i means -// that byte i requires an escape sequence in the input. -static ESCAPE: [bool; 256] = { - const CT: bool = true; // control character \x00..=\x1F - const QU: bool = true; // quote \x22 - const BS: bool = true; // backslash \x5C - const __: bool = false; // allow unescaped - [ - // 1 2 3 4 5 6 7 8 9 A B C D E F - CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 0 - CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 1 - __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 - __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F - ] -}; +// This is only used in IoRead. SliceRead hardcodes the arguments to memchr. +#[cfg(feature = "std")] +fn is_escape(ch: u8) -> bool { + ch == b'"' || ch == b'\\' || ch < 0x20 +} fn next_or_eof<'de, R>(read: &mut R) -> Result where From 63cb04d74b38ca1915d6996fd1a6291a6edb987f Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 29 Jul 2024 13:05:39 +0300 Subject: [PATCH 434/508] Bring MSRV down --- src/read.rs | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/read.rs b/src/read.rs index a79d64c9b..adb17850c 100644 --- a/src/read.rs +++ b/src/read.rs @@ -443,32 +443,36 @@ impl<'a> SliceRead<'a> { // benchmarks and is faster than both SSE2 and AVX-based code, and it's cross-platform, so // probably the right fit. // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ + const STEP: usize = mem::size_of::(); + + // Moving this to a local variable removes a spill in the hot loop. + let mut index = self.index; + + if self.slice.len() >= STEP { + while index < end.min(self.slice.len() - STEP + 1) { + // We can safely overread past end in most cases. This ensures that SWAR code is + // used to handle the tail in the hot path. + const ONE_BYTES: usize = usize::MAX / 255; + let chars = usize::from_ne_bytes(self.slice[index..][..STEP].try_into().unwrap()); + let mask = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars & (ONE_BYTES << 7); + + if mask != 0 { + index += mask.trailing_zeros() as usize / 8; + break; + } - // Pad the chunk to a whole count of units if possible. This ensures that SWAR code is used - // to handle the tail in the hot path. - let block_end = (self.index + (end - self.index).next_multiple_of(mem::size_of::())) - .min(self.slice.len()); - let mut block = &self.slice[self.index..block_end]; - - while let Some((chars, block_remainder)) = block.split_first_chunk() { - const ONE_BYTES: usize = usize::MAX / 255; - let chars = usize::from_ne_bytes(*chars); - let mask = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars & (ONE_BYTES << 7); + index += STEP; + } + } - if mask != 0 { - let control_index = block_end - block.len() + mask.trailing_zeros() as usize / 8; - self.index = control_index.min(end); + if index < end { + if let Some(offset) = self.slice[index..end].iter().position(|&c| c <= 0x1F) { + self.index = index + offset; return; } - - block = block_remainder; } - if let Some(offset) = block.iter().position(|&c| c <= 0x1F) { - self.index = (block_end - block.len() + offset).min(end); - } else { - self.index = end; - } + self.index = end; } /// The big optimization here over IoRead is that if the string contains no From 3063d69fd53f075d06318c0b19a7ddd700fe57a6 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 29 Jul 2024 13:23:01 +0300 Subject: [PATCH 435/508] Add better tests --- tests/test.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test.rs b/tests/test.rs index 71087162b..6923e6e38 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2497,3 +2497,22 @@ fn hash_positive_and_negative_zero() { assert_eq!(rand.hash_one(k1), rand.hash_one(k2)); } } + +#[test] +fn test_control_character_search() { + // Different space circumstances + for n in 0..16 { + for m in 0..16 { + test_parse_err::(&[( + &format!("\"{}\n{}\"", ".".repeat(n), ".".repeat(m)), + "control character (\\u0000-\\u001F) found while parsing a string at line 2 column 0", + )]); + } + } + + // Multiple occurrences + test_parse_err::(&[( + &"\"\t\n\r\"", + "control character (\\u0000-\\u001F) found while parsing a string at line 1 column 2", + )]); +} From 7633cb7f0511d7e4962f5a378171cc1ea0a0820d Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Aug 2024 14:12:44 -0700 Subject: [PATCH 436/508] Eliminate local_inner_macros in favor of non-ident macro paths --- src/macros.rs | 71 +++++++++++++++++------------------ tests/ui/missing_colon.stderr | 2 +- tests/ui/missing_value.stderr | 2 +- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index e8c6cd2ca..68e071026 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -50,11 +50,11 @@ /// "comma -->", /// ]); /// ``` -#[macro_export(local_inner_macros)] +#[macro_export] macro_rules! json { // Hide distracting implementation details from the generated rustdoc. ($($json:tt)+) => { - json_internal!($($json)+) + $crate::json_internal!($($json)+) }; } @@ -65,7 +65,7 @@ macro_rules! json { // // Changes are fine as long as `json_internal!` does not call any new helper // macros and can still be invoked as `json_internal!($($json)+)`. -#[macro_export(local_inner_macros)] +#[macro_export] #[doc(hidden)] macro_rules! json_internal { ////////////////////////////////////////////////////////////////////////// @@ -77,57 +77,57 @@ macro_rules! json_internal { // Done with trailing comma. (@array [$($elems:expr,)*]) => { - json_internal_vec![$($elems,)*] + vec![$($elems,)*] }; // Done without trailing comma. (@array [$($elems:expr),*]) => { - json_internal_vec![$($elems),*] + vec![$($elems),*] }; // Next element is `null`. (@array [$($elems:expr,)*] null $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(null)] $($rest)*) }; // Next element is `true`. (@array [$($elems:expr,)*] true $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(true)] $($rest)*) }; // Next element is `false`. (@array [$($elems:expr,)*] false $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!(false)] $($rest)*) }; // Next element is an array. (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!([$($array)*])] $($rest)*) }; // Next element is a map. (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!({$($map)*})] $($rest)*) }; // Next element is an expression followed by comma. (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { - json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!($next),] $($rest)*) }; // Last element is an expression with no trailing comma. (@array [$($elems:expr,)*] $last:expr) => { - json_internal!(@array [$($elems,)* json_internal!($last)]) + $crate::json_internal!(@array [$($elems,)* $crate::json_internal!($last)]) }; // Comma after the most recent element. (@array [$($elems:expr),*] , $($rest:tt)*) => { - json_internal!(@array [$($elems,)*] $($rest)*) + $crate::json_internal!(@array [$($elems,)*] $($rest)*) }; // Unexpected token after most recent element. (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { - json_unexpected!($unexpected) + $crate::json_unexpected!($unexpected) }; ////////////////////////////////////////////////////////////////////////// @@ -146,12 +146,12 @@ macro_rules! json_internal { // Insert the current entry followed by trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { let _ = $object.insert(($($key)+).into(), $value); - json_internal!(@object $object () ($($rest)*) ($($rest)*)); + $crate::json_internal!(@object $object () ($($rest)*) ($($rest)*)); }; // Current entry followed by unexpected token. (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { - json_unexpected!($unexpected); + $crate::json_unexpected!($unexpected); }; // Insert the last entry without trailing comma. @@ -161,78 +161,78 @@ macro_rules! json_internal { // Next value is `null`. (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(null)) $($rest)*); }; // Next value is `true`. (@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(true)) $($rest)*); }; // Next value is `false`. (@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!(false)) $($rest)*); }; // Next value is an array. (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!([$($array)*])) $($rest)*); }; // Next value is a map. (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!({$($map)*})) $($rest)*); }; // Next value is an expression followed by comma. (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!($value)) , $($rest)*); }; // Last value is an expression with no trailing comma. (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { - json_internal!(@object $object [$($key)+] (json_internal!($value))); + $crate::json_internal!(@object $object [$($key)+] ($crate::json_internal!($value))); }; // Missing value for last entry. Trigger a reasonable error message. (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { // "unexpected end of macro invocation" - json_internal!(); + $crate::json_internal!(); }; // Missing colon and value for last entry. Trigger a reasonable error // message. (@object $object:ident ($($key:tt)+) () $copy:tt) => { // "unexpected end of macro invocation" - json_internal!(); + $crate::json_internal!(); }; // Misplaced colon. Trigger a reasonable error message. (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `:`". - json_unexpected!($colon); + $crate::json_unexpected!($colon); }; // Found a comma inside a key. Trigger a reasonable error message. (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `,`". - json_unexpected!($comma); + $crate::json_unexpected!($comma); }; // Key is fully parenthesized. This avoids clippy double_parens false // positives because the parenthesization may be necessary here. (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); + $crate::json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*)); }; // Refuse to absorb colon token into key expression. (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { - json_expect_expr_comma!($($unexpected)+); + $crate::json_expect_expr_comma!($($unexpected)+); }; // Munch a token into the current key. (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { - json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); + $crate::json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*)); }; ////////////////////////////////////////////////////////////////////////// @@ -254,11 +254,11 @@ macro_rules! json_internal { }; ([]) => { - $crate::Value::Array(json_internal_vec![]) + $crate::Value::Array(vec![]) }; ([ $($tt:tt)+ ]) => { - $crate::Value::Array(json_internal!(@array [] $($tt)+)) + $crate::Value::Array($crate::json_internal!(@array [] $($tt)+)) }; ({}) => { @@ -268,7 +268,7 @@ macro_rules! json_internal { ({ $($tt:tt)+ }) => { $crate::Value::Object({ let mut object = $crate::Map::new(); - json_internal!(@object object () ($($tt)+) ($($tt)+)); + $crate::json_internal!(@object object () ($($tt)+) ($($tt)+)); object }) }; @@ -280,9 +280,8 @@ macro_rules! json_internal { }; } -// The json_internal macro above cannot invoke vec directly because it uses -// local_inner_macros. A vec invocation there would resolve to $crate::vec. -// Instead invoke vec here outside of local_inner_macros. +// Used by old versions of Rocket. +// Unused since https://github.com/rwf2/Rocket/commit/c74bcfd40a47b35330db6cafb88e4f3da83e0d17 #[macro_export] #[doc(hidden)] macro_rules! json_internal_vec { diff --git a/tests/ui/missing_colon.stderr b/tests/ui/missing_colon.stderr index 1515211ad..d5f6466e4 100644 --- a/tests/ui/missing_colon.stderr +++ b/tests/ui/missing_colon.stderr @@ -9,4 +9,4 @@ note: while trying to match `@` | | (@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) + = note: this error originates in the macro `$crate::json_internal` which comes from the expansion of the macro `json` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui/missing_value.stderr b/tests/ui/missing_value.stderr index 9c9de99ca..69f6ca361 100644 --- a/tests/ui/missing_value.stderr +++ b/tests/ui/missing_value.stderr @@ -9,4 +9,4 @@ note: while trying to match `@` | | (@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) + = note: this error originates in the macro `$crate::json_internal` which comes from the expansion of the macro `json` (in Nightly builds, run with -Z macro-backtrace for more info) From 6827c7b3c582a8fe30beb8ddb9e2446bfc391ea7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Aug 2024 14:22:10 -0700 Subject: [PATCH 437/508] Fix json! invocations when std prelude is not in scope --- src/lib.rs | 6 ++++++ src/macros.rs | 6 +++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3225e26f0..4ef91095f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -373,6 +373,12 @@ extern crate alloc; #[cfg(feature = "std")] extern crate std; +// Not public API. Used from macro-generated code. +#[doc(hidden)] +pub mod __private { + pub use alloc::vec; +} + #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] #[doc(inline)] diff --git a/src/macros.rs b/src/macros.rs index 68e071026..c47bdf929 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -77,12 +77,12 @@ macro_rules! json_internal { // Done with trailing comma. (@array [$($elems:expr,)*]) => { - vec![$($elems,)*] + $crate::__private::vec![$($elems,)*] }; // Done without trailing comma. (@array [$($elems:expr),*]) => { - vec![$($elems),*] + $crate::__private::vec![$($elems),*] }; // Next element is `null`. @@ -254,7 +254,7 @@ macro_rules! json_internal { }; ([]) => { - $crate::Value::Array(vec![]) + $crate::Value::Array($crate::__private::vec![]) }; ([ $($tt:tt)+ ]) => { From 16fb6e0b853590ed065f2a1ce4450a71a433717b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Aug 2024 14:28:39 -0700 Subject: [PATCH 438/508] Work around buggy rust-analyzer behavior As far as I can tell there is still no way to block it from autoimporting the private macros here. --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4ef91095f..b27f5d976 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -376,6 +376,7 @@ extern crate std; // Not public API. Used from macro-generated code. #[doc(hidden)] pub mod __private { + #[doc(hidden)] pub use alloc::vec; } From 54381d6fee21cb05439937a0f5f286177c21d3f6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 1 Aug 2024 14:29:36 -0700 Subject: [PATCH 439/508] Release 1.0.122 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9fd5fc7f2..765ef78e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.121" +version = "1.0.122" 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 b27f5d976..64ace00c8 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.121")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.122")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 5496579070cddd5fe14e23b27044936138b3f73c Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 14:27:09 +0300 Subject: [PATCH 440/508] Inline memchr2 logic into Mycroft's algorithm --- src/read.rs | 68 ++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/read.rs b/src/read.rs index adb17850c..caa27d310 100644 --- a/src/read.rs +++ b/src/read.rs @@ -426,53 +426,49 @@ impl<'a> SliceRead<'a> { } } - #[inline(always)] fn skip_to_escape(&mut self, forbid_control_characters: bool) { let rest = &self.slice[self.index..]; - let end = self.index + memchr::memchr2(b'"', b'\\', rest).unwrap_or(rest.len()); if !forbid_control_characters { - self.index = end; + self.index += memchr::memchr2(b'"', b'\\', rest).unwrap_or(rest.len()); return; } - // We now wish to check if the chunk contains a byte in range 0x00..=0x1F. Ideally, this - // would be integrated this into the memchr2 check above, but memchr does not support this - // at the moment. Therefore, use a variation on Mycroft's algorithm [1] to provide - // performance better than a naive loop. It runs faster than just a single memchr call on - // benchmarks and is faster than both SSE2 and AVX-based code, and it's cross-platform, so - // probably the right fit. + // We wish to find the first byte in range 0x00..=0x1F or " or \. Ideally, we'd use + // something akin to memchr3, but the memchr crate does not support this at the moment. + // Therefore, we use a variation on Mycroft's algorithm [1] to provide performance better + // than a naive loop. It runs faster than equivalent two-pass memchr2+SWAR code on + // benchmarks and it's cross-platform, so probably the right fit. // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ - const STEP: usize = mem::size_of::(); - - // Moving this to a local variable removes a spill in the hot loop. - let mut index = self.index; - - if self.slice.len() >= STEP { - while index < end.min(self.slice.len() - STEP + 1) { - // We can safely overread past end in most cases. This ensures that SWAR code is - // used to handle the tail in the hot path. - const ONE_BYTES: usize = usize::MAX / 255; - let chars = usize::from_ne_bytes(self.slice[index..][..STEP].try_into().unwrap()); - let mask = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars & (ONE_BYTES << 7); - - if mask != 0 { - index += mask.trailing_zeros() as usize / 8; - break; - } - - index += STEP; - } - } - - if index < end { - if let Some(offset) = self.slice[index..end].iter().position(|&c| c <= 0x1F) { - self.index = index + offset; + type Chunk = usize; + const STEP: usize = mem::size_of::(); + const ONE_BYTES: Chunk = Chunk::MAX / 255; // 0x0101...01 + + for chunk in rest.chunks_exact(STEP) { + let chars = Chunk::from_ne_bytes(chunk.try_into().unwrap()); + let contains_ctrl = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars; + let chars_quote = chars ^ (ONE_BYTES * Chunk::from(b'"')); + let contains_quote = chars_quote.wrapping_sub(ONE_BYTES) & !chars_quote; + let chars_backslash = chars ^ (ONE_BYTES * Chunk::from(b'\\')); + let contains_backslash = chars_backslash.wrapping_sub(ONE_BYTES) & !chars_backslash; + let masked = (contains_ctrl | contains_quote | contains_backslash) & (ONE_BYTES << 7); + if masked != 0 { + // SAFETY: chunk is in-bounds for slice + self.index = unsafe { chunk.as_ptr().offset_from(self.slice.as_ptr()) } as usize + + masked.trailing_zeros() as usize / 8; return; } } - self.index = end; + self.skip_to_escape_slow(); + } + + #[cold] + #[inline(never)] + fn skip_to_escape_slow(&mut self) { + while self.index < self.slice.len() && !is_escape(self.slice[self.index]) { + self.index += 1; + } } /// The big optimization here over IoRead is that if the string contains no @@ -823,8 +819,6 @@ pub trait Fused: private::Sealed {} impl<'a> Fused for SliceRead<'a> {} impl<'a> Fused for StrRead<'a> {} -// This is only used in IoRead. SliceRead hardcodes the arguments to memchr. -#[cfg(feature = "std")] fn is_escape(ch: u8) -> bool { ch == b'"' || ch == b'\\' || ch < 0x20 } From a95d6df9d08611c9a11ac6524903d693921b8eae Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 15:30:26 +0300 Subject: [PATCH 441/508] Big endian support --- src/read.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/read.rs b/src/read.rs index caa27d310..96784b827 100644 --- a/src/read.rs +++ b/src/read.rs @@ -453,9 +453,14 @@ impl<'a> SliceRead<'a> { let contains_backslash = chars_backslash.wrapping_sub(ONE_BYTES) & !chars_backslash; let masked = (contains_ctrl | contains_quote | contains_backslash) & (ONE_BYTES << 7); if masked != 0 { + let addresswise_first_bit = if cfg!(target_endian = "little") { + masked.trailing_zeros() + } else { + masked.leading_zeros() + }; // SAFETY: chunk is in-bounds for slice self.index = unsafe { chunk.as_ptr().offset_from(self.slice.as_ptr()) } as usize - + masked.trailing_zeros() as usize / 8; + + addresswise_first_bit as usize / 8; return; } } From 1f0dcf791ab1756d7ad07c20889e50bd9a7887fb Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 15:38:14 +0300 Subject: [PATCH 442/508] Allow clippy::items_after_statements --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 3225e26f0..4931f00da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -340,6 +340,7 @@ clippy::wildcard_imports, // things are often more readable this way clippy::cast_lossless, + clippy::items_after_statements, clippy::module_name_repetitions, clippy::redundant_else, clippy::shadow_unrelated, From 8389d8a11293616ce5a4358651aede271871248d Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 16:10:35 +0300 Subject: [PATCH 443/508] Don't run the slow algorithm from the beginning --- src/read.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/read.rs b/src/read.rs index 96784b827..9e652d760 100644 --- a/src/read.rs +++ b/src/read.rs @@ -465,6 +465,7 @@ impl<'a> SliceRead<'a> { } } + self.index += rest.len() / STEP * STEP; self.skip_to_escape_slow(); } From e43da5ee0e64819972f08254e8ce799796238791 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 19:21:45 +0300 Subject: [PATCH 444/508] Immediately bail-out on empty strings --- src/read.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/read.rs b/src/read.rs index 9e652d760..1494ec6d2 100644 --- a/src/read.rs +++ b/src/read.rs @@ -222,7 +222,7 @@ where { loop { let ch = tri!(next_or_eof(self)); - if !is_escape(ch) { + if !is_escape(ch, true) { scratch.push(ch); continue; } @@ -343,7 +343,7 @@ where fn ignore_str(&mut self) -> Result<()> { loop { let ch = tri!(next_or_eof(self)); - if !is_escape(ch) { + if !is_escape(ch, true) { continue; } match ch { @@ -427,6 +427,14 @@ impl<'a> SliceRead<'a> { } fn skip_to_escape(&mut self, forbid_control_characters: bool) { + // Immediately bail-out on empty strings and consecutive escapes (e.g. \u041b\u0435) + if self.index == self.slice.len() + || is_escape(self.slice[self.index], forbid_control_characters) + { + return; + } + self.index += 1; + let rest = &self.slice[self.index..]; if !forbid_control_characters { @@ -472,7 +480,7 @@ impl<'a> SliceRead<'a> { #[cold] #[inline(never)] fn skip_to_escape_slow(&mut self) { - while self.index < self.slice.len() && !is_escape(self.slice[self.index]) { + while self.index < self.slice.len() && !is_escape(self.slice[self.index], true) { self.index += 1; } } @@ -825,8 +833,8 @@ pub trait Fused: private::Sealed {} impl<'a> Fused for SliceRead<'a> {} impl<'a> Fused for StrRead<'a> {} -fn is_escape(ch: u8) -> bool { - ch == b'"' || ch == b'\\' || ch < 0x20 +fn is_escape(ch: u8, including_control_characters: bool) -> bool { + ch == b'"' || ch == b'\\' || (including_control_characters && ch < 0x20) } fn next_or_eof<'de, R>(read: &mut R) -> Result From 346189a524694b98b92ccccb07775868d34b144c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Aug 2024 10:45:34 -0700 Subject: [PATCH 445/508] Fix needless_borrow clippy lint in new control character test warning: this expression creates a reference which is immediately dereferenced by the compiler --> tests/test.rs:2515:9 | 2515 | &"\"\t\n\r\"", | ^^^^^^^^^^^^^ help: change this to: `"\"\t\n\r\""` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow = note: `-W clippy::needless-borrow` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_borrow)]` --- tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.rs b/tests/test.rs index 6923e6e38..6baf7e5d9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2512,7 +2512,7 @@ fn test_control_character_search() { // Multiple occurrences test_parse_err::(&[( - &"\"\t\n\r\"", + "\"\t\n\r\"", "control character (\\u0000-\\u001F) found while parsing a string at line 1 column 2", )]); } From 2cab07e68607ab0e11c3a8b0461a472c37886210 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Aug 2024 11:00:48 -0700 Subject: [PATCH 446/508] Release 1.0.123 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 765ef78e6..0596c306c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.122" +version = "1.0.123" 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 97f3265be..beefec18d 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.122")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.123")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 8eba7863b126584f4b9a5b1d3cc4cbc0d0f59976 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 23:45:41 +0300 Subject: [PATCH 447/508] Fix skip_to_escape on BE architectures --- src/read.rs | 9 ++------- tests/test.rs | 2 +- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/read.rs b/src/read.rs index 1494ec6d2..6c663768e 100644 --- a/src/read.rs +++ b/src/read.rs @@ -453,7 +453,7 @@ impl<'a> SliceRead<'a> { const ONE_BYTES: Chunk = Chunk::MAX / 255; // 0x0101...01 for chunk in rest.chunks_exact(STEP) { - let chars = Chunk::from_ne_bytes(chunk.try_into().unwrap()); + let chars = Chunk::from_le_bytes(chunk.try_into().unwrap()); let contains_ctrl = chars.wrapping_sub(ONE_BYTES * 0x20) & !chars; let chars_quote = chars ^ (ONE_BYTES * Chunk::from(b'"')); let contains_quote = chars_quote.wrapping_sub(ONE_BYTES) & !chars_quote; @@ -461,14 +461,9 @@ impl<'a> SliceRead<'a> { let contains_backslash = chars_backslash.wrapping_sub(ONE_BYTES) & !chars_backslash; let masked = (contains_ctrl | contains_quote | contains_backslash) & (ONE_BYTES << 7); if masked != 0 { - let addresswise_first_bit = if cfg!(target_endian = "little") { - masked.trailing_zeros() - } else { - masked.leading_zeros() - }; // SAFETY: chunk is in-bounds for slice self.index = unsafe { chunk.as_ptr().offset_from(self.slice.as_ptr()) } as usize - + addresswise_first_bit as usize / 8; + + masked.trailing_zeros() as usize / 8; return; } } diff --git a/tests/test.rs b/tests/test.rs index 6baf7e5d9..9f2159513 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -2504,7 +2504,7 @@ fn test_control_character_search() { for n in 0..16 { for m in 0..16 { test_parse_err::(&[( - &format!("\"{}\n{}\"", ".".repeat(n), ".".repeat(m)), + &format!("\"{}\n{}\"", " ".repeat(n), " ".repeat(m)), "control character (\\u0000-\\u001F) found while parsing a string at line 2 column 0", )]); } From cf771a0471dd797b6fead77e767f2f7943740c98 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 11 Aug 2024 14:05:55 -0700 Subject: [PATCH 448/508] Release 1.0.124 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0596c306c..90fd2b5ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.123" +version = "1.0.124" 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 beefec18d..c1d645654 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.123")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.124")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 81b1b618864075ac8d8f30c9e43bb8be16fc08c4 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 01:00:46 +0300 Subject: [PATCH 449/508] Test on BE and 32-bit platforms on CI via Miri --- .github/workflows/ci.yml | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e55d307dd..0ad18762b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,15 +82,27 @@ jobs: miri: name: Miri runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-unknown-linux-gnu + - powerpc64-unknown-linux-gnu + - i686-unknown-linux-gnu + - mips-unknown-linux-gnu env: MIRIFLAGS: -Zmiri-strict-provenance timeout-minutes: 45 steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@miri + - run: sudo apt-get install gcc-powerpc-linux-gnu + if: matrix.target == 'powerpc64-unknown-linux-gnu' + - run: sudo apt-get install gcc-mips-linux-gnu + if: matrix.target == 'mips-unknown-linux-gnu' - run: cargo miri setup - - run: cargo miri test - - run: cargo miri test --features preserve_order,float_roundtrip,arbitrary_precision,raw_value + - run: cargo miri test --target ${{matrix.target}} + - run: cargo miri test --target ${{matrix.target}} --features preserve_order,float_roundtrip,arbitrary_precision,raw_value clippy: name: Clippy From 86d0e114e1370deb0b00cc97f5aec8c3869d835e Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Sun, 11 Aug 2024 22:13:13 +0300 Subject: [PATCH 450/508] Parse \uXXXX escapes faster When ignoring *War and Peace* (in Russian), this increases performance from 640 MB/s to 1080 MB/s (+70%). When parsing into String, the savings are moderate but still significant: 275 MB/s to 320 MB/s (+15%). --- src/read.rs | 106 +++++++++++++++++++++++++------------------------- tests/test.rs | 6 +-- 2 files changed, 57 insertions(+), 55 deletions(-) diff --git a/src/read.rs b/src/read.rs index 6c663768e..0eec0b3bb 100644 --- a/src/read.rs +++ b/src/read.rs @@ -361,16 +361,14 @@ where } fn decode_hex_escape(&mut self) -> Result { - let mut n = 0; - for _ in 0..4 { - match decode_hex_val(tri!(next_or_eof(self))) { - None => return error(self, ErrorCode::InvalidEscape), - Some(val) => { - n = (n << 4) + val; - } - } + let a = tri!(next_or_eof(self)); + let b = tri!(next_or_eof(self)); + let c = tri!(next_or_eof(self)); + let d = tri!(next_or_eof(self)); + match decode_four_hex_digits(a, b, c, d) { + Some(val) => Ok(val), + None => error(self, ErrorCode::InvalidEscape), } - Ok(n) } #[cfg(feature = "raw_value")] @@ -609,24 +607,21 @@ impl<'a> Read<'a> for SliceRead<'a> { } } + #[inline] fn decode_hex_escape(&mut self) -> Result { - if self.index + 4 > self.slice.len() { - self.index = self.slice.len(); - return error(self, ErrorCode::EofWhileParsingString); - } - - let mut n = 0; - for _ in 0..4 { - let ch = decode_hex_val(self.slice[self.index]); - self.index += 1; - match ch { - None => return error(self, ErrorCode::InvalidEscape), - Some(val) => { - n = (n << 4) + val; + match self.slice[self.index..] { + [a, b, c, d, ..] => { + self.index += 4; + match decode_four_hex_digits(a, b, c, d) { + Some(val) => Ok(val), + None => error(self, ErrorCode::InvalidEscape), } } + _ => { + self.index = self.slice.len(); + error(self, ErrorCode::EofWhileParsingString) + } } - Ok(n) } #[cfg(feature = "raw_value")] @@ -993,34 +988,41 @@ where Ok(()) } -static HEX: [u8; 256] = { - const __: u8 = 255; // not a hex digit - [ - // 1 2 3 4 5 6 7 8 9 A B C D E F - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 0 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 1 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 - 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, __, __, __, __, __, __, // 3 - __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 4 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 5 - __, 10, 11, 12, 13, 14, 15, __, __, __, __, __, __, __, __, __, // 6 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E - __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F - ] -}; - -fn decode_hex_val(val: u8) -> Option { - let n = HEX[val as usize] as u16; - if n == 255 { - None - } else { - Some(n) +const fn decode_hex_val_slow(val: u8) -> Option { + match val { + b'0'..=b'9' => Some(val - b'0'), + b'A'..=b'F' => Some(val - b'A' + 10), + b'a'..=b'f' => Some(val - b'a' + 10), + _ => None, + } +} + +const fn build_hex_table(shift: usize) -> [i16; 256] { + let mut table = [0; 256]; + let mut ch = 0; + while ch < 256 { + table[ch] = match decode_hex_val_slow(ch as u8) { + Some(val) => (val as i16) << shift, + None => -1, + }; + ch += 1; } + table +} + +static HEX0: [i16; 256] = build_hex_table(0); +static HEX1: [i16; 256] = build_hex_table(4); + +fn decode_four_hex_digits(a: u8, b: u8, c: u8, d: u8) -> Option { + let a = HEX1[a as usize]; + let b = HEX0[b as usize]; + let c = HEX1[c as usize]; + let d = HEX0[d as usize]; + + // A single sign bit check. + if (a | b | c | d) < 0 { + return None; + } + + Some((((a | b) << 8) | c | d) as u16) } diff --git a/tests/test.rs b/tests/test.rs index 9f2159513..5576317f9 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1091,15 +1091,15 @@ fn test_parse_string() { ), ( &[b'"', b'\\', b'u', 250, 48, 51, 48, b'"'], - "invalid escape at line 1 column 4", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 250, 51, 48, b'"'], - "invalid escape at line 1 column 5", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 48, 250, 48, b'"'], - "invalid escape at line 1 column 6", + "invalid escape at line 1 column 7", ), ( &[b'"', b'\\', b'u', 48, 48, 51, 250, b'"'], From a38dbf3708724519d7ef4e847bc0aed8606ecad0 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 19:34:06 +0300 Subject: [PATCH 451/508] Mark \u parsing as cold This counterintuitively speeds up War and Peace 275 -> 290 MB/s (+5%) by enabling inlining of encode_utf8 and extend_from_slice. --- src/read.rs | 152 ++++++++++++++++++++++++++++------------------------ 1 file changed, 81 insertions(+), 71 deletions(-) diff --git a/src/read.rs b/src/read.rs index 6c663768e..2d9c8f865 100644 --- a/src/read.rs +++ b/src/read.rs @@ -882,87 +882,97 @@ fn parse_escape<'de, R: Read<'de>>( b'n' => scratch.push(b'\n'), b'r' => scratch.push(b'\r'), b't' => scratch.push(b'\t'), - b'u' => { - fn encode_surrogate(scratch: &mut Vec, n: u16) { - scratch.extend_from_slice(&[ - (n >> 12 & 0b0000_1111) as u8 | 0b1110_0000, - (n >> 6 & 0b0011_1111) as u8 | 0b1000_0000, - (n & 0b0011_1111) as u8 | 0b1000_0000, - ]); - } - - let c = match tri!(read.decode_hex_escape()) { - n @ 0xDC00..=0xDFFF => { - return if validate { - error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) - } else { - encode_surrogate(scratch, n); - Ok(()) - }; - } + b'u' => return parse_unicode_escape(read, validate, scratch), + _ => { + return error(read, ErrorCode::InvalidEscape); + } + } - // Non-BMP characters are encoded as a sequence of two hex - // escapes, representing UTF-16 surrogates. If deserializing a - // utf-8 string the surrogates are required to be paired, - // whereas deserializing a byte string accepts lone surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(peek_or_eof(read)) == b'\\' { - read.discard(); - } else { - return if validate { - read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) - } else { - encode_surrogate(scratch, n1); - Ok(()) - }; - } + Ok(()) +} - if tri!(peek_or_eof(read)) == b'u' { - read.discard(); - } else { - return if validate { - read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) - } else { - encode_surrogate(scratch, n1); - // The \ prior to this byte started an escape sequence, - // so we need to parse that now. This recursive call - // does not blow the stack on malicious input because - // the escape is not \u, so it will be handled by one - // of the easy nonrecursive cases. - parse_escape(read, validate, scratch) - }; - } +/// Parses a JSON \u escape and appends it into the scratch space. Assumes \u +/// has just been read. +#[cold] +fn parse_unicode_escape<'de, R: Read<'de>>( + read: &mut R, + validate: bool, + scratch: &mut Vec, +) -> Result<()> { + fn encode_surrogate(scratch: &mut Vec, n: u16) { + scratch.extend_from_slice(&[ + (n >> 12 & 0b0000_1111) as u8 | 0b1110_0000, + (n >> 6 & 0b0011_1111) as u8 | 0b1000_0000, + (n & 0b0011_1111) as u8 | 0b1000_0000, + ]); + } + + let c = match tri!(read.decode_hex_escape()) { + n @ 0xDC00..=0xDFFF => { + return if validate { + error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) + } else { + encode_surrogate(scratch, n); + Ok(()) + }; + } - let n2 = tri!(read.decode_hex_escape()); + // Non-BMP characters are encoded as a sequence of two hex + // escapes, representing UTF-16 surrogates. If deserializing a + // utf-8 string the surrogates are required to be paired, + // whereas deserializing a byte string accepts lone surrogates. + n1 @ 0xD800..=0xDBFF => { + if tri!(peek_or_eof(read)) == b'\\' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + encode_surrogate(scratch, n1); + Ok(()) + }; + } - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } + if tri!(peek_or_eof(read)) == b'u' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + encode_surrogate(scratch, n1); + // The \ prior to this byte started an escape sequence, + // so we need to parse that now. This recursive call + // does not blow the stack on malicious input because + // the escape is not \u, so it will be handled by one + // of the easy nonrecursive cases. + parse_escape(read, validate, scratch) + }; + } - let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; + let n2 = tri!(read.decode_hex_escape()); - match char::from_u32(n) { - Some(c) => c, - None => { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - } - } + if n2 < 0xDC00 || n2 > 0xDFFF { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } - // Every u16 outside of the surrogate ranges above is guaranteed - // to be a legal char. - n => char::from_u32(n as u32).unwrap(), - }; + let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; - scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes()); - } - _ => { - return error(read, ErrorCode::InvalidEscape); + match char::from_u32(n) { + Some(c) => c, + None => { + return error(read, ErrorCode::InvalidUnicodeCodePoint); + } + } } - } + // Every u16 outside of the surrogate ranges above is guaranteed + // to be a legal char. + n => char::from_u32(n as u32).unwrap(), + }; + + scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes()); Ok(()) } From 0e90b61b8cefdd699adbed9c0d4d8a1538d77183 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 19:44:29 +0300 Subject: [PATCH 452/508] Format UTF-8 strings manually This speeds up War and Peace 290 MB/s -> 330 MB/s (+15%). --- src/read.rs | 60 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/src/read.rs b/src/read.rs index 2d9c8f865..9f2d91404 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1,6 +1,5 @@ use crate::error::{Error, ErrorCode, Result}; use alloc::vec::Vec; -use core::char; use core::cmp; use core::mem; use core::ops::Deref; @@ -957,25 +956,64 @@ fn parse_unicode_escape<'de, R: Read<'de>>( return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); } - let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; - - match char::from_u32(n) { - Some(c) => c, - None => { - return error(read, ErrorCode::InvalidUnicodeCodePoint); - } - } + // This value is in range U+10000..=U+10FFFF, which is always a + // valid codepoint. + (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000 } // Every u16 outside of the surrogate ranges above is guaranteed // to be a legal char. - n => char::from_u32(n as u32).unwrap(), + n => n as u32, }; - scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes()); + // SAFETY: c is always a codepoint. + unsafe { + push_utf8_codepoint(c, scratch); + } Ok(()) } +/// Adds a UTF-8 codepoint to the end of the buffer. This is a more efficient +/// implementation of String::push. n must be a valid codepoint. +#[inline] +unsafe fn push_utf8_codepoint(n: u32, scratch: &mut Vec) { + if n < 0x80 { + scratch.push(n as u8); + return; + } + + scratch.reserve(4); + + unsafe { + let ptr = scratch.as_mut_ptr().add(scratch.len()); + + let encoded_len = match n { + 0..=0x7F => unreachable!(), + 0x80..=0x7FF => { + ptr.write((n >> 6 & 0b0001_1111) as u8 | 0b1100_0000); + 2 + } + 0x800..=0xFFFF => { + ptr.write((n >> 12 & 0b0000_1111) as u8 | 0b1110_0000); + ptr.add(1).write((n >> 6 & 0b0011_1111) as u8 | 0b1000_0000); + 3 + } + 0x1_0000..=0x10_FFFF => { + ptr.write((n >> 18 & 0b0000_0111) as u8 | 0b1111_0000); + ptr.add(1) + .write((n >> 12 & 0b0011_1111) as u8 | 0b1000_0000); + ptr.add(2).write((n >> 6 & 0b0011_1111) as u8 | 0b1000_0000); + 4 + } + 0x11_0000.. => unreachable!(), + }; + ptr.add(encoded_len - 1) + .write((n & 0b0011_1111) as u8 | 0b1000_0000); + + scratch.set_len(scratch.len() + encoded_len); + } +} + /// Parses a JSON escape sequence and discards the value. Assumes the previous /// byte read was a backslash. fn ignore_escape<'de, R>(read: &mut R) -> Result<()> From 2f28d106e68e214cfa19043e65b1bd178b3c2ced Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 19:46:31 +0300 Subject: [PATCH 453/508] Use the same UTF-8/WTF-8 impl for surrogates This does not affect performance. --- src/read.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/read.rs b/src/read.rs index 9f2d91404..e9f3f58e7 100644 --- a/src/read.rs +++ b/src/read.rs @@ -898,20 +898,12 @@ fn parse_unicode_escape<'de, R: Read<'de>>( validate: bool, scratch: &mut Vec, ) -> Result<()> { - fn encode_surrogate(scratch: &mut Vec, n: u16) { - scratch.extend_from_slice(&[ - (n >> 12 & 0b0000_1111) as u8 | 0b1110_0000, - (n >> 6 & 0b0011_1111) as u8 | 0b1000_0000, - (n & 0b0011_1111) as u8 | 0b1000_0000, - ]); - } - let c = match tri!(read.decode_hex_escape()) { n @ 0xDC00..=0xDFFF => { return if validate { error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) } else { - encode_surrogate(scratch, n); + push_wtf8_codepoint(n as u32, scratch); Ok(()) }; } @@ -928,7 +920,7 @@ fn parse_unicode_escape<'de, R: Read<'de>>( read.discard(); error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { - encode_surrogate(scratch, n1); + push_wtf8_codepoint(n1 as u32, scratch); Ok(()) }; } @@ -940,7 +932,7 @@ fn parse_unicode_escape<'de, R: Read<'de>>( read.discard(); error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { - encode_surrogate(scratch, n1); + push_wtf8_codepoint(n1 as u32, scratch); // The \ prior to this byte started an escape sequence, // so we need to parse that now. This recursive call // does not blow the stack on malicious input because @@ -966,17 +958,14 @@ fn parse_unicode_escape<'de, R: Read<'de>>( n => n as u32, }; - // SAFETY: c is always a codepoint. - unsafe { - push_utf8_codepoint(c, scratch); - } + push_wtf8_codepoint(c, scratch); Ok(()) } -/// Adds a UTF-8 codepoint to the end of the buffer. This is a more efficient -/// implementation of String::push. n must be a valid codepoint. +/// Adds a WTF-8 codepoint to the end of the buffer. This is a more efficient +/// implementation of String::push. The codepoint may be a surrogate. #[inline] -unsafe fn push_utf8_codepoint(n: u32, scratch: &mut Vec) { +fn push_wtf8_codepoint(n: u32, scratch: &mut Vec) { if n < 0x80 { scratch.push(n as u8); return; From 236cc8247d32a5cb337850d75f68265fdb4bc14e Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 19:53:51 +0300 Subject: [PATCH 454/508] Simplify unicode escape handling This does not affect performance. --- src/read.rs | 107 ++++++++++++++++++++++++++-------------------------- 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src/read.rs b/src/read.rs index e9f3f58e7..6b5abdc22 100644 --- a/src/read.rs +++ b/src/read.rs @@ -898,67 +898,66 @@ fn parse_unicode_escape<'de, R: Read<'de>>( validate: bool, scratch: &mut Vec, ) -> Result<()> { - let c = match tri!(read.decode_hex_escape()) { - n @ 0xDC00..=0xDFFF => { - return if validate { - error(read, ErrorCode::LoneLeadingSurrogateInHexEscape) - } else { - push_wtf8_codepoint(n as u32, scratch); - Ok(()) - }; - } + let n = tri!(read.decode_hex_escape()); - // Non-BMP characters are encoded as a sequence of two hex - // escapes, representing UTF-16 surrogates. If deserializing a - // utf-8 string the surrogates are required to be paired, - // whereas deserializing a byte string accepts lone surrogates. - n1 @ 0xD800..=0xDBFF => { - if tri!(peek_or_eof(read)) == b'\\' { - read.discard(); - } else { - return if validate { - read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) - } else { - push_wtf8_codepoint(n1 as u32, scratch); - Ok(()) - }; - } + // Non-BMP characters are encoded as a sequence of two hex + // escapes, representing UTF-16 surrogates. If deserializing a + // utf-8 string the surrogates are required to be paired, + // whereas deserializing a byte string accepts lone surrogates. + if validate && n >= 0xDC00 && n <= 0xDFFF { + // XXX: This is actually a trailing surrogate. + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } - if tri!(peek_or_eof(read)) == b'u' { - read.discard(); - } else { - return if validate { - read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) - } else { - push_wtf8_codepoint(n1 as u32, scratch); - // The \ prior to this byte started an escape sequence, - // so we need to parse that now. This recursive call - // does not blow the stack on malicious input because - // the escape is not \u, so it will be handled by one - // of the easy nonrecursive cases. - parse_escape(read, validate, scratch) - }; - } + if n < 0xD800 || n > 0xDBFF { + // Every u16 outside of the surrogate ranges is guaranteed to be a + // legal char. + push_wtf8_codepoint(n as u32, scratch); + return Ok(()); + } - let n2 = tri!(read.decode_hex_escape()); + // n is a leading surrogate, we now expect a trailing surrogate. + let n1 = n; - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } + if tri!(peek_or_eof(read)) == b'\\' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + Ok(()) + }; + } - // This value is in range U+10000..=U+10FFFF, which is always a - // valid codepoint. - (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000 - } + if tri!(peek_or_eof(read)) == b'u' { + read.discard(); + } else { + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + // The \ prior to this byte started an escape sequence, + // so we need to parse that now. This recursive call + // does not blow the stack on malicious input because + // the escape is not \u, so it will be handled by one + // of the easy nonrecursive cases. + parse_escape(read, validate, scratch) + }; + } - // Every u16 outside of the surrogate ranges above is guaranteed - // to be a legal char. - n => n as u32, - }; + let n2 = tri!(read.decode_hex_escape()); + + if n2 < 0xDC00 || n2 > 0xDFFF { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } - push_wtf8_codepoint(c, scratch); + // This value is in range U+10000..=U+10FFFF, which is always a + // valid codepoint. + let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; + push_wtf8_codepoint(n, scratch); Ok(()) } From 96ae60445df67f6903b53b2fcfe1d60f3bd8f778 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 12 Aug 2024 20:45:44 +0300 Subject: [PATCH 455/508] Correct WTF-8 parsing Closes #877. This is a good time to make ByteBuf parsing more consistent as I'm rewriting it anyway. This commit integrates the changes from #877 and also handles a leading surrogate followed by a surrogate pair correctly. This does not affect performance significantly. Co-authored-by: Luca Casonato --- src/de.rs | 5 ++- src/read.rs | 90 ++++++++++++++++++++++++++++----------------------- tests/test.rs | 58 ++++++++++++++++++++++++++++----- 3 files changed, 103 insertions(+), 50 deletions(-) diff --git a/src/de.rs b/src/de.rs index bfde371a1..bd6f2e50c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1575,7 +1575,10 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { /// /// The behavior of serde_json is specified to fail on non-UTF-8 strings /// when deserializing into Rust UTF-8 string types such as String, and - /// succeed with non-UTF-8 bytes when deserializing using this method. + /// succeed with the bytes representing the [WTF-8] encoding of code points + /// when deserializing using this method. + /// + /// [WTF-8]: https://simonsapin.github.io/wtf-8 /// /// Escape sequences are processed as usual, and for `\uXXXX` escapes it is /// still checked if the hex number represents a valid Unicode code point. diff --git a/src/read.rs b/src/read.rs index 6b5abdc22..9e8ffaacf 100644 --- a/src/read.rs +++ b/src/read.rs @@ -898,7 +898,7 @@ fn parse_unicode_escape<'de, R: Read<'de>>( validate: bool, scratch: &mut Vec, ) -> Result<()> { - let n = tri!(read.decode_hex_escape()); + let mut n = tri!(read.decode_hex_escape()); // Non-BMP characters are encoded as a sequence of two hex // escapes, representing UTF-16 surrogates. If deserializing a @@ -909,56 +909,64 @@ fn parse_unicode_escape<'de, R: Read<'de>>( return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); } - if n < 0xD800 || n > 0xDBFF { - // Every u16 outside of the surrogate ranges is guaranteed to be a - // legal char. - push_wtf8_codepoint(n as u32, scratch); - return Ok(()); - } + loop { + if n < 0xD800 || n > 0xDBFF { + // Every u16 outside of the surrogate ranges is guaranteed to be a + // legal char. + push_wtf8_codepoint(n as u32, scratch); + return Ok(()); + } - // n is a leading surrogate, we now expect a trailing surrogate. - let n1 = n; + // n is a leading surrogate, we now expect a trailing surrogate. + let n1 = n; - if tri!(peek_or_eof(read)) == b'\\' { - read.discard(); - } else { - return if validate { + if tri!(peek_or_eof(read)) == b'\\' { read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { - push_wtf8_codepoint(n1 as u32, scratch); - Ok(()) - }; - } + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + Ok(()) + }; + } - if tri!(peek_or_eof(read)) == b'u' { - read.discard(); - } else { - return if validate { + if tri!(peek_or_eof(read)) == b'u' { read.discard(); - error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { - push_wtf8_codepoint(n1 as u32, scratch); - // The \ prior to this byte started an escape sequence, - // so we need to parse that now. This recursive call - // does not blow the stack on malicious input because - // the escape is not \u, so it will be handled by one - // of the easy nonrecursive cases. - parse_escape(read, validate, scratch) - }; - } + return if validate { + read.discard(); + error(read, ErrorCode::UnexpectedEndOfHexEscape) + } else { + push_wtf8_codepoint(n1 as u32, scratch); + // The \ prior to this byte started an escape sequence, + // so we need to parse that now. This recursive call + // does not blow the stack on malicious input because + // the escape is not \u, so it will be handled by one + // of the easy nonrecursive cases. + parse_escape(read, validate, scratch) + }; + } - let n2 = tri!(read.decode_hex_escape()); + let n2 = tri!(read.decode_hex_escape()); - if n2 < 0xDC00 || n2 > 0xDFFF { - return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); - } + if n2 < 0xDC00 || n2 > 0xDFFF { + if validate { + return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); + } + push_wtf8_codepoint(n1 as u32, scratch); + // If n2 is a leading surrogate, we need to restart. + n = n2; + continue; + } - // This value is in range U+10000..=U+10FFFF, which is always a - // valid codepoint. - let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; - push_wtf8_codepoint(n, scratch); - Ok(()) + // This value is in range U+10000..=U+10FFFF, which is always a + // valid codepoint. + let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; + push_wtf8_codepoint(n, scratch); + return Ok(()); + } } /// Adds a WTF-8 codepoint to the end of the buffer. This is a more efficient diff --git a/tests/test.rs b/tests/test.rs index 9f2159513..4290cb3ef 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1707,7 +1707,7 @@ fn test_byte_buf_de() { } #[test] -fn test_byte_buf_de_lone_surrogate() { +fn test_byte_buf_de_invalid_surrogates() { let bytes = ByteBuf::from(vec![237, 160, 188]); let v: ByteBuf = from_str(r#""\ud83c""#).unwrap(); assert_eq!(v, bytes); @@ -1720,23 +1720,54 @@ fn test_byte_buf_de_lone_surrogate() { let v: ByteBuf = from_str(r#""\ud83c ""#).unwrap(); assert_eq!(v, bytes); - let bytes = ByteBuf::from(vec![237, 176, 129]); - let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); - assert_eq!(v, bytes); - let res = from_str::(r#""\ud83c\!""#); assert!(res.is_err()); let res = from_str::(r#""\ud83c\u""#); assert!(res.is_err()); - let res = from_str::(r#""\ud83c\ud83c""#); - assert!(res.is_err()); + // lone trailing surrogate + let bytes = ByteBuf::from(vec![237, 176, 129]); + let v: ByteBuf = from_str(r#""\udc01""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by other leading surrogate + let bytes = ByteBuf::from(vec![237, 160, 188, 237, 160, 188]); + let v: ByteBuf = from_str(r#""\ud83c\ud83c""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by "a" (U+0061) in \u encoding + let bytes = ByteBuf::from(vec![237, 160, 188, 97]); + let v: ByteBuf = from_str(r#""\ud83c\u0061""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+0080 + let bytes = ByteBuf::from(vec![237, 160, 188, 194, 128]); + let v: ByteBuf = from_str(r#""\ud83c\u0080""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by U+FFFF + let bytes = ByteBuf::from(vec![237, 160, 188, 239, 191, 191]); + let v: ByteBuf = from_str(r#""\ud83c\uffff""#).unwrap(); + assert_eq!(v, bytes); +} + +#[test] +fn test_byte_buf_de_surrogate_pair() { + // leading surrogate followed by trailing surrogate + let bytes = ByteBuf::from(vec![240, 159, 128, 128]); + let v: ByteBuf = from_str(r#""\ud83c\udc00""#).unwrap(); + assert_eq!(v, bytes); + + // leading surrogate followed by a surrogate pair + let bytes = ByteBuf::from(vec![237, 160, 188, 240, 159, 128, 128]); + let v: ByteBuf = from_str(r#""\ud83c\ud83c\udc00""#).unwrap(); + assert_eq!(v, bytes); } #[cfg(feature = "raw_value")] #[test] -fn test_raw_de_lone_surrogate() { +fn test_raw_de_invalid_surrogates() { use serde_json::value::RawValue; assert!(from_str::>(r#""\ud83c""#).is_ok()); @@ -1746,6 +1777,17 @@ fn test_raw_de_lone_surrogate() { assert!(from_str::>(r#""\udc01\!""#).is_err()); assert!(from_str::>(r#""\udc01\u""#).is_err()); assert!(from_str::>(r#""\ud83c\ud83c""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0061""#).is_ok()); + assert!(from_str::>(r#""\ud83c\u0080""#).is_ok()); + assert!(from_str::>(r#""\ud83c\uffff""#).is_ok()); +} + +#[cfg(feature = "raw_value")] +#[test] +fn test_raw_de_surrogate_pair() { + use serde_json::value::RawValue; + + assert!(from_str::>(r#""\ud83c\udc00""#).is_ok()); } #[test] From 94a2aad7b79543454da3ecdb2e6170b98773ccc2 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 12 Aug 2024 12:44:06 -0700 Subject: [PATCH 456/508] Improve job names for miri jobs --- .github/workflows/ci.yml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ad18762b..4620ce8ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,16 +80,20 @@ jobs: - run: cargo check --locked miri: - name: Miri + name: Miri (${{matrix.name}}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: - target: - - x86_64-unknown-linux-gnu - - powerpc64-unknown-linux-gnu - - i686-unknown-linux-gnu - - mips-unknown-linux-gnu + include: + - name: 64-bit little endian + target: x86_64-unknown-linux-gnu + - name: 64-bit big endian + target: powerpc64-unknown-linux-gnu + - name: 32-bit little endian + target: i686-unknown-linux-gnu + - name: 32-bit big endian + target: mips-unknown-linux-gnu env: MIRIFLAGS: -Zmiri-strict-provenance timeout-minutes: 45 From cc7a1608c9bb7736c884926e016421af41a1ebe7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 14 Aug 2024 22:29:30 -0700 Subject: [PATCH 457/508] Touch up PR 1175 --- src/read.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/read.rs b/src/read.rs index 19f01f058..6d65a3e56 100644 --- a/src/read.rs +++ b/src/read.rs @@ -877,15 +877,13 @@ fn parse_escape<'de, R: Read<'de>>( b'r' => scratch.push(b'\r'), b't' => scratch.push(b'\t'), b'u' => return parse_unicode_escape(read, validate, scratch), - _ => { - return error(read, ErrorCode::InvalidEscape); - } + _ => return error(read, ErrorCode::InvalidEscape), } Ok(()) } -/// Parses a JSON \u escape and appends it into the scratch space. Assumes \u +/// Parses a JSON \u escape and appends it into the scratch space. Assumes `\u` /// has just been read. #[cold] fn parse_unicode_escape<'de, R: Read<'de>>( @@ -895,10 +893,10 @@ fn parse_unicode_escape<'de, R: Read<'de>>( ) -> Result<()> { let mut n = tri!(read.decode_hex_escape()); - // Non-BMP characters are encoded as a sequence of two hex - // escapes, representing UTF-16 surrogates. If deserializing a - // utf-8 string the surrogates are required to be paired, - // whereas deserializing a byte string accepts lone surrogates. + // Non-BMP characters are encoded as a sequence of two hex escapes, + // representing UTF-16 surrogates. If deserializing a utf-8 string the + // surrogates are required to be paired, whereas deserializing a byte string + // accepts lone surrogates. if validate && n >= 0xDC00 && n <= 0xDFFF { // XXX: This is actually a trailing surrogate. return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape); @@ -935,11 +933,10 @@ fn parse_unicode_escape<'de, R: Read<'de>>( error(read, ErrorCode::UnexpectedEndOfHexEscape) } else { push_wtf8_codepoint(n1 as u32, scratch); - // The \ prior to this byte started an escape sequence, - // so we need to parse that now. This recursive call - // does not blow the stack on malicious input because - // the escape is not \u, so it will be handled by one - // of the easy nonrecursive cases. + // The \ prior to this byte started an escape sequence, so we + // need to parse that now. This recursive call does not blow the + // stack on malicious input because the escape is not \u, so it + // will be handled by one of the easy nonrecursive cases. parse_escape(read, validate, scratch) }; } @@ -956,8 +953,8 @@ fn parse_unicode_escape<'de, R: Read<'de>>( continue; } - // This value is in range U+10000..=U+10FFFF, which is always a - // valid codepoint. + // This value is in range U+10000..=U+10FFFF, which is always a valid + // codepoint. let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000; push_wtf8_codepoint(n, scratch); return Ok(()); From 6130f9b358dea483f10e671343f0098fbe55611b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 14 Aug 2024 22:30:33 -0700 Subject: [PATCH 458/508] Release 1.0.125 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 90fd2b5ca..1df846f77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.124" +version = "1.0.125" 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 c1d645654..1d5d299ca 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.124")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.125")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 9ffb43a1e4c4528af333beb888f48ca64867cfb1 Mon Sep 17 00:00:00 2001 From: Alisa Sireneva Date: Mon, 19 Aug 2024 11:40:06 +0300 Subject: [PATCH 459/508] Optimize Unicode decoding by 1% --- src/read.rs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/read.rs b/src/read.rs index 6d65a3e56..9a489fba3 100644 --- a/src/read.rs +++ b/src/read.rs @@ -1055,15 +1055,17 @@ static HEX0: [i16; 256] = build_hex_table(0); static HEX1: [i16; 256] = build_hex_table(4); fn decode_four_hex_digits(a: u8, b: u8, c: u8, d: u8) -> Option { - let a = HEX1[a as usize]; - let b = HEX0[b as usize]; - let c = HEX1[c as usize]; - let d = HEX0[d as usize]; + let a = HEX1[a as usize] as i32; + let b = HEX0[b as usize] as i32; + let c = HEX1[c as usize] as i32; + let d = HEX0[d as usize] as i32; + + let codepoint = ((a | b) << 8) | c | d; // A single sign bit check. - if (a | b | c | d) < 0 { - return None; + if codepoint >= 0 { + Some(codepoint as u16) + } else { + None } - - Some((((a | b) << 8) | c | d) as u16) } From 0ceb9d84fa3777d95f263da9c9a65c74bf76795e Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 19 Aug 2024 21:39:10 +0100 Subject: [PATCH 460/508] Add `OccupiedEntry::remove_entry()` (and shift/swap versions) --- src/map.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/map.rs b/src/map.rs index 3ffff15f8..d63d1a60c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -900,6 +900,69 @@ impl<'a> OccupiedEntry<'a> { #[cfg(not(feature = "preserve_order"))] return self.occupied.remove(); } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// If serde_json's "preserve_order" is enabled, `.remove_entry()` is + /// equivalent to [`.swap_remove_entry()`][Self::swap_remove_entry], + /// replacing this entry's position with the last element. If you need to + /// preserve the relative order of the keys in the map, use + /// [`.shift_remove_entry()`][Self::shift_remove_entry] instead. + /// + /// # Examples + /// + /// ``` + /// # use serde_json::json; + /// # + /// use serde_json::map::Entry; + /// + /// let mut map = serde_json::Map::new(); + /// map.insert("serde".to_owned(), json!(12)); + /// + /// match map.entry("serde") { + /// Entry::Occupied(occupied) => { + /// let (key, value) = occupied.remove_entry(); + /// assert_eq!(key, "serde"); + /// assert_eq!(value, 12); + /// } + /// Entry::Vacant(_) => unimplemented!(), + /// } + /// ``` + #[inline] + pub fn remove_entry(self) -> (String, Value) { + #[cfg(feature = "preserve_order")] + return self.swap_remove_entry(); + #[cfg(not(feature = "preserve_order"))] + return self.occupied.remove_entry(); + } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove_entry(self) -> (String, Value) { + self.occupied.swap_remove_entry() + } + + /// Removes the entry from the map, returning the stored key and value. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove_entry(self) -> (String, Value) { + self.occupied.shift_remove_entry() + } } ////////////////////////////////////////////////////////////////////////////// From 11fc61c7af7b59ea80fb2ef7d78db94465dfbd54 Mon Sep 17 00:00:00 2001 From: Graham Esau Date: Mon, 19 Aug 2024 21:43:12 +0100 Subject: [PATCH 461/508] Add `OccupiedEntry::shift_remove()` and `swap_remove()` --- src/map.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/map.rs b/src/map.rs index d63d1a60c..61864c3c2 100644 --- a/src/map.rs +++ b/src/map.rs @@ -876,6 +876,12 @@ impl<'a> OccupiedEntry<'a> { /// Takes the value of the entry out of the map, and returns it. /// + /// If serde_json's "preserve_order" is enabled, `.remove()` is + /// equivalent to [`.swap_remove()`][Self::swap_remove], replacing this + /// entry's position with the last element. If you need to preserve the + /// relative order of the keys in the map, use + /// [`.shift_remove()`][Self::shift_remove] instead. + /// /// # Examples /// /// ``` @@ -896,11 +902,39 @@ impl<'a> OccupiedEntry<'a> { #[inline] pub fn remove(self) -> Value { #[cfg(feature = "preserve_order")] - return self.occupied.swap_remove(); + return self.swap_remove(); #[cfg(not(feature = "preserve_order"))] return self.occupied.remove(); } + /// Takes the value of the entry out of the map, and returns it. + /// + /// Like [`Vec::remove`], the entry is removed by shifting all of the + /// elements that follow it, preserving their relative order. This perturbs + /// the index of all of those elements! + /// + /// [`Vec::remove`]: std::vec::Vec::remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn shift_remove(self) -> Value { + self.occupied.shift_remove() + } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// Like [`Vec::swap_remove`], the entry is removed by swapping it with the + /// last element of the map and popping it off. This perturbs the position + /// of what used to be the last element! + /// + /// [`Vec::swap_remove`]: std::vec::Vec::swap_remove + #[cfg(feature = "preserve_order")] + #[cfg_attr(docsrs, doc(cfg(feature = "preserve_order")))] + #[inline] + pub fn swap_remove(self) -> Value { + self.occupied.swap_remove() + } + /// Removes the entry from the map, returning the stored key and value. /// /// If serde_json's "preserve_order" is enabled, `.remove_entry()` is From 3d837e1cc4a0f1df56ba6645c3b6d144768b5d9d Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Fri, 23 Aug 2024 13:29:34 +0200 Subject: [PATCH 462/508] Ensure the SWAR chunks are 64-bit in more cases Various architectures have support for 64-bit integers, but there are Rust targets for those architectures where the pointer size is intentionally just 32-bit. For SWAR this smaller pointer size would negatively affect those targets, so this PR ensures the chunk size stays 64-bit on those targets. --- src/read.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/read.rs b/src/read.rs index 9a489fba3..09e1b41b9 100644 --- a/src/read.rs +++ b/src/read.rs @@ -445,7 +445,22 @@ impl<'a> SliceRead<'a> { // than a naive loop. It runs faster than equivalent two-pass memchr2+SWAR code on // benchmarks and it's cross-platform, so probably the right fit. // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ + + // The following architectures have native support for 64-bit integers, + // but have targets where usize is not 64-bit. + #[cfg(any( + target_arch = "aarch64", + target_arch = "x86_64", + target_arch = "wasm32", + ))] + type Chunk = u64; + #[cfg(not(any( + target_arch = "aarch64", + target_arch = "x86_64", + target_arch = "wasm32", + )))] type Chunk = usize; + const STEP: usize = mem::size_of::(); const ONE_BYTES: Chunk = Chunk::MAX / 255; // 0x0101...01 From f268173a9fb1f5f8a80f47af62b564525cf33764 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Aug 2024 09:28:06 -0700 Subject: [PATCH 463/508] Unify chunk size choice between float and string parsing --- build.rs | 34 ++++++++++++++++++++++------------ src/lexical/large_powers.rs | 4 ++-- src/lexical/math.rs | 20 ++++++++++---------- src/lexical/mod.rs | 4 ++-- src/lexical/small_powers.rs | 6 +++--- src/read.rs | 16 +++------------- tests/lexical/math.rs | 4 ++-- 7 files changed, 44 insertions(+), 44 deletions(-) diff --git a/build.rs b/build.rs index f2124b43f..2b7815ee4 100644 --- a/build.rs +++ b/build.rs @@ -3,18 +3,28 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rustc-check-cfg=cfg(limb_width_32)"); - println!("cargo:rustc-check-cfg=cfg(limb_width_64)"); + println!("cargo:rustc-check-cfg=cfg(arithmetic32)"); + println!("cargo:rustc-check-cfg=cfg(arithmetic64)"); - // 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" | "loongarch64" => { - println!("cargo:rustc-cfg=limb_width_64"); - } - _ => { - println!("cargo:rustc-cfg=limb_width_32"); - } + // Decide ideal limb width for arithmetic in the float parser and string + // parser. + let target_arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + let target_pointer_width = env::var_os("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap(); + if target_arch == "aarch64" + || target_arch == "loongarch64" + || target_arch == "mips64" + || target_arch == "powerpc64" + || target_arch == "wasm32" + || target_arch == "x86_64" + || target_pointer_width == "64" + { + // The above list of architectures are ones that have native support for + // 64-bit arithmetic, but which have some targets using a smaller + // pointer width. Examples include aarch64-unknown-linux-gnu_ilp32 and + // x86_64-unknown-linux-gnux32. So our choice of limb width is not + // equivalent to using usize everywhere. + println!("cargo:rustc-cfg=arithmetic64"); + } else { + println!("cargo:rustc-cfg=arithmetic32"); } } diff --git a/src/lexical/large_powers.rs b/src/lexical/large_powers.rs index c63ce1cf2..f96ea79c1 100644 --- a/src/lexical/large_powers.rs +++ b/src/lexical/large_powers.rs @@ -2,8 +2,8 @@ //! Precalculated large powers for limbs. -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub(crate) use super::large_powers32::*; -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub(crate) use super::large_powers64::*; diff --git a/src/lexical/math.rs b/src/lexical/math.rs index d7122bffa..a8d1d294a 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -37,29 +37,29 @@ use core::{cmp, iter, mem}; // sparc64 (`UMUL` only supported double-word arguments). // 32-BIT LIMB -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub type Limb = u32; -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub const POW5_LIMB: &[Limb] = &POW5_32; -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub const POW10_LIMB: &[Limb] = &POW10_32; -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] type Wide = u64; // 64-BIT LIMB -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub type Limb = u64; -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub const POW5_LIMB: &[Limb] = &POW5_64; -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub const POW10_LIMB: &[Limb] = &POW10_64; -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] type Wide = u128; /// Cast to limb type. @@ -79,14 +79,14 @@ fn as_wide(t: T) -> Wide { /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] 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(arithmetic64)] fn split_u64(x: u64) -> [Limb; 1] { [as_limb(x)] } diff --git a/src/lexical/mod.rs b/src/lexical/mod.rs index b1a45e218..f5ff13cee 100644 --- a/src/lexical/mod.rs +++ b/src/lexical/mod.rs @@ -28,10 +28,10 @@ pub(crate) mod rounding; mod shift; mod small_powers; -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] mod large_powers32; -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] mod large_powers64; // API diff --git a/src/lexical/small_powers.rs b/src/lexical/small_powers.rs index 219d82611..51d638a13 100644 --- a/src/lexical/small_powers.rs +++ b/src/lexical/small_powers.rs @@ -3,19 +3,19 @@ //! Pre-computed small powers. // 32 BIT -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub(crate) const POW5_32: [u32; 14] = [ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, ]; -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub(crate) const POW10_32: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // 64 BIT -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub(crate) const POW5_64: [u64; 28] = [ 1, 5, diff --git a/src/read.rs b/src/read.rs index 09e1b41b9..e16b99fdc 100644 --- a/src/read.rs +++ b/src/read.rs @@ -446,20 +446,10 @@ impl<'a> SliceRead<'a> { // benchmarks and it's cross-platform, so probably the right fit. // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ - // The following architectures have native support for 64-bit integers, - // but have targets where usize is not 64-bit. - #[cfg(any( - target_arch = "aarch64", - target_arch = "x86_64", - target_arch = "wasm32", - ))] + #[cfg(arithmetic64)] type Chunk = u64; - #[cfg(not(any( - target_arch = "aarch64", - target_arch = "x86_64", - target_arch = "wasm32", - )))] - type Chunk = usize; + #[cfg(arithmetic32)] + type Chunk = u32; const STEP: usize = mem::size_of::(); const ONE_BYTES: Chunk = Chunk::MAX / 255; // 0x0101...01 diff --git a/tests/lexical/math.rs b/tests/lexical/math.rs index 79d3ef3ee..b758339df 100644 --- a/tests/lexical/math.rs +++ b/tests/lexical/math.rs @@ -18,12 +18,12 @@ impl Math for Bigint { } } -#[cfg(limb_width_32)] +#[cfg(arithmetic32)] pub(crate) fn from_u32(x: &[u32]) -> Vec { x.iter().cloned().collect() } -#[cfg(limb_width_64)] +#[cfg(arithmetic64)] pub(crate) fn from_u32(x: &[u32]) -> Vec { let mut v = Vec::::default(); for xi in x.chunks(2) { From ffc4a43453029cdc5603cfe3ef08414488fd45de Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Aug 2024 12:41:44 -0700 Subject: [PATCH 464/508] Improve cfg names for fast arithmetic --- build.rs | 7 +++---- src/lexical/large_powers.rs | 4 ++-- src/lexical/math.rs | 20 ++++++++++---------- src/lexical/mod.rs | 4 ++-- src/lexical/small_powers.rs | 6 +++--- src/read.rs | 4 ++-- tests/lexical/math.rs | 4 ++-- 7 files changed, 24 insertions(+), 25 deletions(-) diff --git a/build.rs b/build.rs index 2b7815ee4..29907eaf7 100644 --- a/build.rs +++ b/build.rs @@ -3,8 +3,7 @@ use std::env; fn main() { println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rustc-check-cfg=cfg(arithmetic32)"); - println!("cargo:rustc-check-cfg=cfg(arithmetic64)"); + println!("cargo:rustc-check-cfg=cfg(fast_arithmetic, values(\"32\", \"64\"))"); // Decide ideal limb width for arithmetic in the float parser and string // parser. @@ -23,8 +22,8 @@ fn main() { // pointer width. Examples include aarch64-unknown-linux-gnu_ilp32 and // x86_64-unknown-linux-gnux32. So our choice of limb width is not // equivalent to using usize everywhere. - println!("cargo:rustc-cfg=arithmetic64"); + println!("cargo:rustc-cfg=fast_arithmetic=\"64\""); } else { - println!("cargo:rustc-cfg=arithmetic32"); + println!("cargo:rustc-cfg=fast_arithmetic=\"32\""); } } diff --git a/src/lexical/large_powers.rs b/src/lexical/large_powers.rs index f96ea79c1..af2c8a6a9 100644 --- a/src/lexical/large_powers.rs +++ b/src/lexical/large_powers.rs @@ -2,8 +2,8 @@ //! Precalculated large powers for limbs. -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub(crate) use super::large_powers32::*; -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub(crate) use super::large_powers64::*; diff --git a/src/lexical/math.rs b/src/lexical/math.rs index a8d1d294a..adacb42e1 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -37,29 +37,29 @@ use core::{cmp, iter, mem}; // sparc64 (`UMUL` only supported double-word arguments). // 32-BIT LIMB -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub type Limb = u32; -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub const POW5_LIMB: &[Limb] = &POW5_32; -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub const POW10_LIMB: &[Limb] = &POW10_32; -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] type Wide = u64; // 64-BIT LIMB -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub type Limb = u64; -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub const POW5_LIMB: &[Limb] = &POW5_64; -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub const POW10_LIMB: &[Limb] = &POW10_64; -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] type Wide = u128; /// Cast to limb type. @@ -79,14 +79,14 @@ fn as_wide(t: T) -> Wide { /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] fn split_u64(x: u64) -> [Limb; 2] { [as_limb(x), as_limb(x >> 32)] } /// Split u64 into limbs, in little-endian order. #[inline] -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] fn split_u64(x: u64) -> [Limb; 1] { [as_limb(x)] } diff --git a/src/lexical/mod.rs b/src/lexical/mod.rs index f5ff13cee..aeed4065e 100644 --- a/src/lexical/mod.rs +++ b/src/lexical/mod.rs @@ -28,10 +28,10 @@ pub(crate) mod rounding; mod shift; mod small_powers; -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] mod large_powers32; -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] mod large_powers64; // API diff --git a/src/lexical/small_powers.rs b/src/lexical/small_powers.rs index 51d638a13..29b83a159 100644 --- a/src/lexical/small_powers.rs +++ b/src/lexical/small_powers.rs @@ -3,19 +3,19 @@ //! Pre-computed small powers. // 32 BIT -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub(crate) const POW5_32: [u32; 14] = [ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, 48828125, 244140625, 1220703125, ]; -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub(crate) const POW10_32: [u32; 10] = [ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000, ]; // 64 BIT -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub(crate) const POW5_64: [u64; 28] = [ 1, 5, diff --git a/src/read.rs b/src/read.rs index e16b99fdc..b84ec223a 100644 --- a/src/read.rs +++ b/src/read.rs @@ -446,9 +446,9 @@ impl<'a> SliceRead<'a> { // benchmarks and it's cross-platform, so probably the right fit. // [1]: https://groups.google.com/forum/#!original/comp.lang.c/2HtQXvg7iKc/xOJeipH6KLMJ - #[cfg(arithmetic64)] + #[cfg(fast_arithmetic = "64")] type Chunk = u64; - #[cfg(arithmetic32)] + #[cfg(fast_arithmetic = "32")] type Chunk = u32; const STEP: usize = mem::size_of::(); diff --git a/tests/lexical/math.rs b/tests/lexical/math.rs index b758339df..454eaa606 100644 --- a/tests/lexical/math.rs +++ b/tests/lexical/math.rs @@ -18,12 +18,12 @@ impl Math for Bigint { } } -#[cfg(arithmetic32)] +#[cfg(fast_arithmetic = "32")] pub(crate) fn from_u32(x: &[u32]) -> Vec { x.iter().cloned().collect() } -#[cfg(arithmetic64)] +#[cfg(fast_arithmetic = "64")] pub(crate) fn from_u32(x: &[u32]) -> Vec { let mut v = Vec::::default(); for xi in x.chunks(2) { From ec980b02774abbff12fd3e26b0a1582eb14dcef7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Aug 2024 12:50:16 -0700 Subject: [PATCH 465/508] Release 1.0.126 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1df846f77..20820d4f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.125" +version = "1.0.126" 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 1d5d299ca..dbe48c5a0 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.125")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.126")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 5ebf65cc480f90714c94f82099ca9161d80cbb10 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 23 Aug 2024 13:45:06 -0700 Subject: [PATCH 466/508] Release 1.0.127 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 20820d4f3..5ee62f00d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.126" +version = "1.0.127" 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 dbe48c5a0..479e19cec 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.126")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.127")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 27a4ca9d7a62394fe8f0103f3d91de59f055a4c4 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 25 Aug 2024 12:12:25 -0700 Subject: [PATCH 467/508] Upload CI Cargo.lock for reproducing failures --- .github/workflows/ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4620ce8ed..72f1c9197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,11 @@ jobs: - run: cargo test --features float_roundtrip,arbitrary_precision --tests -- --skip ui --exact - run: cargo test --features raw_value --tests -- --skip ui --exact - run: cargo test --features unbounded_depth --tests -- --skip ui --exact + - uses: actions/upload-artifact@v4 + if: matrix.os == 'ubuntu' && always() + with: + name: Cargo.lock + path: Cargo.lock build: name: Rust ${{matrix.rust}} ${{matrix.os == 'windows' && '(windows)' || ''}} From 5416cee6c5ad355907f6e9d5f6039b994c69f768 Mon Sep 17 00:00:00 2001 From: Ivan Moiseev Date: Wed, 4 Sep 2024 20:55:39 +0200 Subject: [PATCH 468/508] feat: add support for 128 bit HashMap key serialization --- src/value/ser.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/value/ser.rs b/src/value/ser.rs index e869ae160..b292f9820 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -505,6 +505,10 @@ impl serde::Serializer for MapKeySerializer { Ok(value.to_string()) } + fn serialize_i128(self, value: i128) -> Result { + Ok(value.to_string()) + } + fn serialize_u8(self, value: u8) -> Result { Ok(value.to_string()) } @@ -521,6 +525,10 @@ impl serde::Serializer for MapKeySerializer { Ok(value.to_string()) } + fn serialize_u128(self, value: u128) -> Result { + Ok(value.to_string()) + } + fn serialize_f32(self, value: f32) -> Result { if value.is_finite() { Ok(ryu::Buffer::new().format_finite(value).to_owned()) From d96b1d9b643b49a0bb92a8cf280daf6aa08f37cc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:06:38 -0700 Subject: [PATCH 469/508] Release 1.0.128 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5ee62f00d..36f42c820 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.127" +version = "1.0.128" 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 479e19cec..8358c76d7 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.127")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.128")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 59112ae7b16c4acb424d3cf33ffa9c64d14499c9 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:19:23 -0700 Subject: [PATCH 470/508] Replace f64::to_string with ryu --- src/number.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/number.rs b/src/number.rs index 878a3dcb2..f8e0df73f 100644 --- a/src/number.rs +++ b/src/number.rs @@ -692,7 +692,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - f.to_string() + ryu::Buffer::new().format_finite(f).to_owned() } } ParserNumber::U64(u) => { From 4921906b1da650962e84a99f6a3c4eb954fb8b1b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:19:49 -0700 Subject: [PATCH 471/508] Replace integer to_string with itoa --- src/number.rs | 4 ++-- src/value/ser.rs | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/number.rs b/src/number.rs index f8e0df73f..b0863ed84 100644 --- a/src/number.rs +++ b/src/number.rs @@ -702,7 +702,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - u.to_string() + itoa::Buffer::new().format(u).to_owned() } } ParserNumber::I64(i) => { @@ -712,7 +712,7 @@ impl From for Number { } #[cfg(feature = "arbitrary_precision")] { - i.to_string() + itoa::Buffer::new().format(i).to_owned() } } #[cfg(feature = "arbitrary_precision")] diff --git a/src/value/ser.rs b/src/value/ser.rs index b292f9820..d1f5387d3 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -490,43 +490,43 @@ impl serde::Serializer for MapKeySerializer { } fn serialize_i8(self, value: i8) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i16(self, value: i16) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i32(self, value: i32) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i64(self, value: i64) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_i128(self, value: i128) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u8(self, value: u8) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u16(self, value: u16) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u32(self, value: u32) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u64(self, value: u64) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_u128(self, value: u128) -> Result { - Ok(value.to_string()) + Ok(itoa::Buffer::new().format(value).to_owned()) } fn serialize_f32(self, value: f32) -> Result { From 3f4e30a584cc4b81f288f83d79f5066cdba3af3b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:21:26 -0700 Subject: [PATCH 472/508] Replace char::to_string with encode_utf8 --- src/ser.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ser.rs b/src/ser.rs index 3742e0bef..6956671b4 100644 --- a/src/ser.rs +++ b/src/ser.rs @@ -2,7 +2,9 @@ use crate::error::{Error, ErrorCode, Result}; use crate::io; -use alloc::string::{String, ToString}; +use alloc::string::String; +#[cfg(feature = "raw_value")] +use alloc::string::ToString; use alloc::vec::Vec; use core::fmt::{self, Display}; use core::num::FpCategory; @@ -1057,7 +1059,7 @@ where } fn serialize_char(self, value: char) -> Result<()> { - self.ser.serialize_str(&value.to_string()) + self.ser.serialize_str(value.encode_utf8(&mut [0u8; 4])) } fn serialize_bytes(self, _value: &[u8]) -> Result<()> { From d2542811846a7a6eecb8fe286a1bb8ae69c44dd6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:22:02 -0700 Subject: [PATCH 473/508] Replace str::to_string with to_owned --- src/value/from.rs | 12 ++-- tests/map.rs | 2 +- tests/test.rs | 176 +++++++++++++++++++++++----------------------- 3 files changed, 95 insertions(+), 95 deletions(-) diff --git a/src/value/from.rs b/src/value/from.rs index ed1e3338b..2dae925a1 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -1,8 +1,8 @@ use super::Value; use crate::map::Map; use crate::number::Number; -use alloc::borrow::Cow; -use alloc::string::{String, ToString}; +use alloc::borrow::{Cow, ToOwned}; +use alloc::string::String; use alloc::vec::Vec; macro_rules! from_integer { @@ -85,7 +85,7 @@ impl From for Value { /// ``` /// use serde_json::Value; /// - /// let s: String = "lorem".to_string(); + /// let s: String = "lorem".to_owned(); /// let x: Value = s.into(); /// ``` fn from(f: String) -> Self { @@ -105,7 +105,7 @@ impl From<&str> for Value { /// let x: Value = s.into(); /// ``` fn from(f: &str) -> Self { - Value::String(f.to_string()) + Value::String(f.to_owned()) } } @@ -126,7 +126,7 @@ impl<'a> From> for Value { /// use serde_json::Value; /// use std::borrow::Cow; /// - /// let s: Cow = Cow::Owned("lorem".to_string()); + /// let s: Cow = Cow::Owned("lorem".to_owned()); /// let x: Value = s.into(); /// ``` fn from(f: Cow<'a, str>) -> Self { @@ -159,7 +159,7 @@ impl From> for Value { /// use serde_json::{Map, Value}; /// /// let mut m = Map::new(); - /// m.insert("Lorem".to_string(), "ipsum".into()); + /// m.insert("Lorem".to_owned(), "ipsum".into()); /// let x: Value = m.into(); /// ``` fn from(f: Map) -> Self { diff --git a/tests/map.rs b/tests/map.rs index c3c64d70b..aa3cb2549 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -20,7 +20,7 @@ fn test_preserve_order() { fn test_shift_insert() { let mut v: Value = from_str(r#"{"b":null,"a":null,"c":null}"#).unwrap(); let val = v.as_object_mut().unwrap(); - val.shift_insert(0, "d".to_string(), Value::Null); + val.shift_insert(0, "d".to_owned(), Value::Null); let keys: Vec<_> = val.keys().collect(); assert_eq!(keys, &["d", "b", "a", "c"]); diff --git a/tests/test.rs b/tests/test.rs index dc8c3c22f..d41a2336a 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -269,11 +269,11 @@ fn test_write_list() { fn test_write_object() { test_encode_ok(&[ (treemap!(), "{}"), - (treemap!("a".to_string() => true), "{\"a\":true}"), + (treemap!("a".to_owned() => true), "{\"a\":true}"), ( treemap!( - "a".to_string() => true, - "b".to_string() => false, + "a".to_owned() => true, + "b".to_owned() => false, ), "{\"a\":true,\"b\":false}", ), @@ -282,44 +282,44 @@ fn test_write_object() { test_encode_ok(&[ ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], "{\"a\":{},\"b\":{},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], "{\"a\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"b\":{},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "c".to_string() => treemap![], + "c".to_owned() => treemap![], ], "{\"a\":{},\"b\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}},\"c\":{}}", ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], ], "{\"a\":{},\"b\":{},\"c\":{\"a\":{\"a\":[1,2,3]},\"b\":{},\"c\":{}}}", @@ -331,9 +331,9 @@ fn test_write_object() { test_pretty_encode_ok(&[ ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], pretty_str!({ "a": {}, @@ -343,13 +343,13 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], pretty_str!({ "a": { @@ -369,13 +369,13 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], - "c".to_string() => treemap![], + "c".to_owned() => treemap![], ], pretty_str!({ "a": {}, @@ -395,12 +395,12 @@ fn test_write_object() { ), ( treemap![ - "a".to_string() => treemap![], - "b".to_string() => treemap![], - "c".to_string() => treemap![ - "a".to_string() => treemap!["a" => vec![1,2,3]], - "b".to_string() => treemap![], - "c".to_string() => treemap![], + "a".to_owned() => treemap![], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![ + "a".to_owned() => treemap!["a" => vec![1,2,3]], + "b".to_owned() => treemap![], + "c".to_owned() => treemap![], ], ], pretty_str!({ @@ -424,15 +424,15 @@ fn test_write_object() { test_pretty_encode_ok(&[ (treemap!(), "{}"), ( - treemap!("a".to_string() => true), + treemap!("a".to_owned() => true), pretty_str!({ "a": true }), ), ( treemap!( - "a".to_string() => true, - "b".to_string() => false, + "a".to_owned() => true, + "b".to_owned() => false, ), pretty_str!( { "a": true, @@ -493,26 +493,26 @@ fn test_write_enum() { test_encode_ok(&[ (Animal::Dog, "\"Dog\""), ( - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), "{\"Frog\":[\"Henry\",[]]}", ), ( - Animal::Frog("Henry".to_string(), vec![349]), + Animal::Frog("Henry".to_owned(), vec![349]), "{\"Frog\":[\"Henry\",[349]]}", ), ( - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), "{\"Frog\":[\"Henry\",[349,102]]}", ), ( Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, "{\"Cat\":{\"age\":5,\"name\":\"Kate\"}}", ), ( - Animal::AntHive(vec!["Bob".to_string(), "Stuart".to_string()]), + Animal::AntHive(vec!["Bob".to_owned(), "Stuart".to_owned()]), "{\"AntHive\":[\"Bob\",\"Stuart\"]}", ), ]); @@ -520,7 +520,7 @@ fn test_write_enum() { test_pretty_encode_ok(&[ (Animal::Dog, "\"Dog\""), ( - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), pretty_str!({ "Frog": [ "Henry", @@ -529,7 +529,7 @@ fn test_write_enum() { }), ), ( - Animal::Frog("Henry".to_string(), vec![349]), + Animal::Frog("Henry".to_owned(), vec![349]), pretty_str!({ "Frog": [ "Henry", @@ -540,7 +540,7 @@ fn test_write_enum() { }), ), ( - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), pretty_str!({ "Frog": [ "Henry", @@ -1117,16 +1117,16 @@ fn test_parse_string() { test_parse_ok(vec![ ("\"\"", String::new()), - ("\"foo\"", "foo".to_string()), - (" \"foo\" ", "foo".to_string()), - ("\"\\\"\"", "\"".to_string()), - ("\"\\b\"", "\x08".to_string()), - ("\"\\n\"", "\n".to_string()), - ("\"\\r\"", "\r".to_string()), - ("\"\\t\"", "\t".to_string()), - ("\"\\u12ab\"", "\u{12ab}".to_string()), - ("\"\\uAB12\"", "\u{AB12}".to_string()), - ("\"\\uD83C\\uDF95\"", "\u{1F395}".to_string()), + ("\"foo\"", "foo".to_owned()), + (" \"foo\" ", "foo".to_owned()), + ("\"\\\"\"", "\"".to_owned()), + ("\"\\b\"", "\x08".to_owned()), + ("\"\\n\"", "\n".to_owned()), + ("\"\\r\"", "\r".to_owned()), + ("\"\\t\"", "\t".to_owned()), + ("\"\\u12ab\"", "\u{12ab}".to_owned()), + ("\"\\uAB12\"", "\u{AB12}".to_owned()), + ("\"\\uD83C\\uDF95\"", "\u{1F395}".to_owned()), ]); } @@ -1184,24 +1184,24 @@ fn test_parse_object() { test_parse_ok(vec![ ("{}", treemap!()), ("{ }", treemap!()), - ("{\"a\":3}", treemap!("a".to_string() => 3u64)), - ("{ \"a\" : 3 }", treemap!("a".to_string() => 3)), + ("{\"a\":3}", treemap!("a".to_owned() => 3u64)), + ("{ \"a\" : 3 }", treemap!("a".to_owned() => 3)), ( "{\"a\":3,\"b\":4}", - treemap!("a".to_string() => 3, "b".to_string() => 4), + treemap!("a".to_owned() => 3, "b".to_owned() => 4), ), ( " { \"a\" : 3 , \"b\" : 4 } ", - treemap!("a".to_string() => 3, "b".to_string() => 4), + treemap!("a".to_owned() => 3, "b".to_owned() => 4), ), ]); test_parse_ok(vec![( "{\"a\": {\"b\": 3, \"c\": 4}}", treemap!( - "a".to_string() => treemap!( - "b".to_string() => 3u64, - "c".to_string() => 4, + "a".to_owned() => treemap!( + "b".to_owned() => 3u64, + "c".to_owned() => 4, ), ), )]); @@ -1248,7 +1248,7 @@ fn test_parse_struct() { inner: vec![Inner { a: (), b: 2, - c: vec!["abc".to_string(), "xyz".to_string()], + c: vec!["abc".to_owned(), "xyz".to_owned()], }], }, ), @@ -1269,7 +1269,7 @@ fn test_parse_struct() { inner: vec![Inner { a: (), b: 2, - c: vec!["abc".to_string(), "xyz".to_string()], + c: vec!["abc".to_owned(), "xyz".to_owned()], }], } ); @@ -1283,7 +1283,7 @@ fn test_parse_struct() { fn test_parse_option() { test_parse_ok(vec![ ("null", None::), - ("\"jodhpurs\"", Some("jodhpurs".to_string())), + ("\"jodhpurs\"", Some("jodhpurs".to_owned())), ]); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -1338,29 +1338,29 @@ fn test_parse_enum() { (" \"Dog\" ", Animal::Dog), ( "{\"Frog\":[\"Henry\",[]]}", - Animal::Frog("Henry".to_string(), vec![]), + Animal::Frog("Henry".to_owned(), vec![]), ), ( " { \"Frog\": [ \"Henry\" , [ 349, 102 ] ] } ", - Animal::Frog("Henry".to_string(), vec![349, 102]), + Animal::Frog("Henry".to_owned(), vec![349, 102]), ), ( "{\"Cat\": {\"age\": 5, \"name\": \"Kate\"}}", Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, ), ( " { \"Cat\" : { \"age\" : 5 , \"name\" : \"Kate\" } } ", Animal::Cat { age: 5, - name: "Kate".to_string(), + name: "Kate".to_owned(), }, ), ( " { \"AntHive\" : [\"Bob\", \"Stuart\"] } ", - Animal::AntHive(vec!["Bob".to_string(), "Stuart".to_string()]), + Animal::AntHive(vec!["Bob".to_owned(), "Stuart".to_owned()]), ), ]); @@ -1377,8 +1377,8 @@ fn test_parse_enum() { "}" ), treemap!( - "a".to_string() => Animal::Dog, - "b".to_string() => Animal::Frog("Henry".to_string(), vec![]), + "a".to_owned() => Animal::Dog, + "b".to_owned() => Animal::Frog("Henry".to_owned(), vec![]), ), )]); } @@ -1653,7 +1653,7 @@ fn test_deserialize_from_stream() { let mut stream = TcpStream::connect("localhost:20000").unwrap(); let request = Message { - message: "hi there".to_string(), + message: "hi there".to_owned(), }; to_writer(&mut stream, &request).unwrap(); @@ -1679,20 +1679,20 @@ fn test_serialize_rejects_adt_keys() { fn test_bytes_ser() { let buf = vec![]; let bytes = Bytes::new(&buf); - assert_eq!(to_string(&bytes).unwrap(), "[]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[]".to_owned()); let buf = vec![1, 2, 3]; let bytes = Bytes::new(&buf); - assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_owned()); } #[test] fn test_byte_buf_ser() { let bytes = ByteBuf::new(); - assert_eq!(to_string(&bytes).unwrap(), "[]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[]".to_owned()); let bytes = ByteBuf::from(vec![1, 2, 3]); - assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_string()); + assert_eq!(to_string(&bytes).unwrap(), "[1,2,3]".to_owned()); } #[test] From 1faf3a1db661c7fb1f3a9a141efa30fb67e9ab3a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 4 Sep 2024 15:27:48 -0700 Subject: [PATCH 474/508] Replace bool::to_string with if --- src/value/ser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/value/ser.rs b/src/value/ser.rs index d1f5387d3..a68193437 100644 --- a/src/value/ser.rs +++ b/src/value/ser.rs @@ -486,7 +486,7 @@ impl serde::Serializer for MapKeySerializer { } fn serialize_bool(self, value: bool) -> Result { - Ok(value.to_string()) + Ok(if value { "true" } else { "false" }.to_owned()) } fn serialize_i8(self, value: i8) -> Result { From 309cfc9f8c639b562670e3abb662cff8ba4b336b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 27 Sep 2024 09:39:33 -0700 Subject: [PATCH 475/508] Resolve empty_line_after_doc_comments clippy lint in lexical warning: empty line after doc comment --> tests/../src/lexical/math.rs:277:5 | 277 | / /// ADDITION 278 | | | |_ ... 284 | pub fn iadd_impl(x: &mut Vec, y: Limb, xstart: usize) { | ----------------------------------------------------------- the comment documents this function | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments = note: `-W clippy::empty-line-after-doc-comments` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::empty_line_after_doc_comments)]` = help: if the empty line is unintentional remove it help: if the documentation should include the empty line include it in the comment | 278 | /// | --- src/lexical/math.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lexical/math.rs b/src/lexical/math.rs index adacb42e1..2e900f1a1 100644 --- a/src/lexical/math.rs +++ b/src/lexical/math.rs @@ -272,9 +272,7 @@ mod scalar { mod small { use super::*; - // MULTIPLICATIION - - /// ADDITION + // ADDITION /// Implied AddAssign implementation for adding a small integer to bigint. /// From ef9ca277782a4d2d945299d185b9c5dc46108273 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 7 Oct 2024 20:42:46 +0200 Subject: [PATCH 476/508] Resolve some needless_lifetimes clippy lints warning: the following explicit lifetimes could be elided: 'a --> src/de.rs:1381:11 | 1381 | impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `-W clippy::needless-lifetimes` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_lifetimes)]` help: elide the lifetimes | 1381 - impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { 1381 + impl<'de, R: Read<'de>> de::Deserializer<'de> for &mut Deserializer { | warning: the following explicit lifetimes could be elided: 'a --> src/map.rs:418:6 | 418 | impl<'a, Q> ops::Index<&'a Q> for Map | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 418 - impl<'a, Q> ops::Index<&'a Q> for Map 418 + impl ops::Index<&Q> for Map | warning: the following explicit lifetimes could be elided: 'a --> src/map.rs:441:6 | 441 | impl<'a, Q> ops::IndexMut<&'a Q> for Map | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 441 - impl<'a, Q> ops::IndexMut<&'a Q> for Map 441 + impl ops::IndexMut<&Q> for Map | warning: the following explicit lifetimes could be elided: 'a --> src/value/index.rs:140:10 | 140 | impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 140 - impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} 140 + impl Sealed for &T where T: ?Sized + Sealed {} | warning: the following explicit lifetimes could be elided: 'a --> src/number.rs:612:11 | 612 | impl<'de, 'a> Deserializer<'de> for &'a Number { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 612 - impl<'de, 'a> Deserializer<'de> for &'a Number { 612 + impl<'de> Deserializer<'de> for &Number { | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:754:6 | 754 | impl<'a, 'de, R> private::Sealed for &'a mut R where R: Read<'de> {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 754 - impl<'a, 'de, R> private::Sealed for &'a mut R where R: Read<'de> {} 754 + impl<'de, R> private::Sealed for &mut R where R: Read<'de> {} | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:756:6 | 756 | impl<'a, 'de, R> Read<'de> for &'a mut R | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 756 - impl<'a, 'de, R> Read<'de> for &'a mut R 756 + impl<'de, R> Read<'de> for &mut R | --- src/de.rs | 2 +- src/map.rs | 4 ++-- src/number.rs | 2 +- src/read.rs | 4 ++-- src/value/index.rs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/de.rs b/src/de.rs index bd6f2e50c..7959a6635 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1378,7 +1378,7 @@ macro_rules! check_recursion { }; } -impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer { +impl<'de, R: Read<'de>> de::Deserializer<'de> for &mut Deserializer { type Error = Error; #[inline] diff --git a/src/map.rs b/src/map.rs index 49265c3fe..90da86fed 100644 --- a/src/map.rs +++ b/src/map.rs @@ -415,7 +415,7 @@ impl Hash for Map { /// } /// # ; /// ``` -impl<'a, Q> ops::Index<&'a Q> for Map +impl ops::Index<&Q> for Map where String: Borrow, Q: ?Sized + Ord + Eq + Hash, @@ -438,7 +438,7 @@ where /// # /// map["key"] = json!("value"); /// ``` -impl<'a, Q> ops::IndexMut<&'a Q> for Map +impl ops::IndexMut<&Q> for Map where String: Borrow, Q: ?Sized + Ord + Eq + Hash, diff --git a/src/number.rs b/src/number.rs index b0863ed84..3ba0846a3 100644 --- a/src/number.rs +++ b/src/number.rs @@ -609,7 +609,7 @@ impl<'de> Deserializer<'de> for Number { } } -impl<'de, 'a> Deserializer<'de> for &'a Number { +impl<'de> Deserializer<'de> for &Number { type Error = Error; deserialize_any!(ref); diff --git a/src/read.rs b/src/read.rs index b84ec223a..a3aa5d164 100644 --- a/src/read.rs +++ b/src/read.rs @@ -751,9 +751,9 @@ impl<'a> Read<'a> for StrRead<'a> { ////////////////////////////////////////////////////////////////////////////// -impl<'a, 'de, R> private::Sealed for &'a mut R where R: Read<'de> {} +impl<'de, R> private::Sealed for &mut R where R: Read<'de> {} -impl<'a, 'de, R> Read<'de> for &'a mut R +impl<'de, R> Read<'de> for &mut R where R: Read<'de>, { diff --git a/src/value/index.rs b/src/value/index.rs index c56135b33..4e41a39be 100644 --- a/src/value/index.rs +++ b/src/value/index.rs @@ -137,7 +137,7 @@ mod private { impl Sealed for usize {} impl Sealed for str {} impl Sealed for alloc::string::String {} - impl<'a, T> Sealed for &'a T where T: ?Sized + Sealed {} + impl Sealed for &T where T: ?Sized + Sealed {} } /// Used in panic messages. From faab2e8d2fcf781a3f77f329df836ffb3aaacfba Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 7 Oct 2024 20:45:41 +0200 Subject: [PATCH 477/508] Ignore needless_lifetimes clippy lint warning: the following explicit lifetimes could be elided: 'a --> src/de.rs:2169:11 | 2169 | impl<'de, 'a, R> MapKey<'a, R> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `-W clippy::needless-lifetimes` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_lifetimes)]` help: elide the lifetimes | 2169 - impl<'de, 'a, R> MapKey<'a, R> 2169 + impl<'de, R> MapKey<'_, R> | warning: the following explicit lifetimes could be elided: 'a --> src/de.rs:2176:11 | 2176 | impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 2176 - impl<'de, 'a, R> de::Deserializer<'de> for MapKey<'a, R> 2176 + impl<'de, R> de::Deserializer<'de> for MapKey<'_, R> | warning: the following explicit lifetimes could be elided: 'a --> src/error.rs:467:6 | 467 | impl<'a> Display for JsonUnexpected<'a> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 467 - impl<'a> Display for JsonUnexpected<'a> { 467 + impl Display for JsonUnexpected<'_> { | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:31:6 | 31 | impl<'a, W> Serializer> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 31 - impl<'a, W> Serializer> 31 + impl Serializer> | warning: the following explicit lifetimes could be elided: 'ser --> src/ser.rs:420:14 | 420 | impl<'ser, W, F> Write for Adapter<'ser, W, F> | ^^^^ ^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 420 - impl<'ser, W, F> Write for Adapter<'ser, W, F> 420 + impl Write for Adapter<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:480:6 | 480 | impl<'a, W, F> ser::SerializeSeq for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 480 - impl<'a, W, F> ser::SerializeSeq for Compound<'a, W, F> 480 + impl ser::SerializeSeq for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:527:6 | 527 | impl<'a, W, F> ser::SerializeTuple for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 527 - impl<'a, W, F> ser::SerializeTuple for Compound<'a, W, F> 527 + impl ser::SerializeTuple for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:549:6 | 549 | impl<'a, W, F> ser::SerializeTupleStruct for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 549 - impl<'a, W, F> ser::SerializeTupleStruct for Compound<'a, W, F> 549 + impl ser::SerializeTupleStruct for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:571:6 | 571 | impl<'a, W, F> ser::SerializeTupleVariant for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 571 - impl<'a, W, F> ser::SerializeTupleVariant for Compound<'a, W, F> 571 + impl ser::SerializeTupleVariant for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:609:6 | 609 | impl<'a, W, F> ser::SerializeMap for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 609 - impl<'a, W, F> ser::SerializeMap for Compound<'a, W, F> 609 + impl ser::SerializeMap for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:681:6 | 681 | impl<'a, W, F> ser::SerializeStruct for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 681 - impl<'a, W, F> ser::SerializeStruct for Compound<'a, W, F> 681 + impl ser::SerializeStruct for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:727:6 | 727 | impl<'a, W, F> ser::SerializeStructVariant for Compound<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 727 - impl<'a, W, F> ser::SerializeStructVariant for Compound<'a, W, F> 727 + impl ser::SerializeStructVariant for Compound<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:793:6 | 793 | impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F> | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 793 - impl<'a, W, F> ser::Serializer for MapKeySerializer<'a, W, F> 793 + impl ser::Serializer for MapKeySerializer<'_, W, F> | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:1157:6 | 1157 | impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, F> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1157 - impl<'a, W: io::Write, F: Formatter> ser::Serializer for NumberStrEmitter<'a, W, F> { 1157 + impl ser::Serializer for NumberStrEmitter<'_, W, F> { | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:1334:6 | 1334 | impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, W, F> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1334 - impl<'a, W: io::Write, F: Formatter> ser::Serializer for RawValueStrEmitter<'a, W, F> { 1334 + impl ser::Serializer for RawValueStrEmitter<'_, W, F> { | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:1951:6 | 1951 | impl<'a> Default for PrettyFormatter<'a> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1951 - impl<'a> Default for PrettyFormatter<'a> { 1951 + impl Default for PrettyFormatter<'_> { | warning: the following explicit lifetimes could be elided: 'a --> src/ser.rs:1957:6 | 1957 | impl<'a> Formatter for PrettyFormatter<'a> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1957 - impl<'a> Formatter for PrettyFormatter<'a> { 1957 + impl Formatter for PrettyFormatter<'_> { | warning: the following explicit lifetimes could be elided: 'a, 'b --> src/value/mod.rs:227:14 | 227 | impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { | ^^ ^^ ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 227 - impl<'a, 'b> io::Write for WriterFormatter<'a, 'b> { 227 + impl io::Write for WriterFormatter<'_, '_> { | warning: the following explicit lifetimes could be elided: 'de --> src/value/de.rs:479:6 | 479 | impl<'de> IntoDeserializer<'de, Error> for Value { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 479 - impl<'de> IntoDeserializer<'de, Error> for Value { 479 + impl IntoDeserializer<'_, Error> for Value { | warning: the following explicit lifetimes could be elided: 'de --> src/value/de.rs:1266:6 | 1266 | impl<'de> Visitor<'de> for KeyClassifier { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 1266 - impl<'de> Visitor<'de> for KeyClassifier { 1266 + impl Visitor<'_> for KeyClassifier { | warning: the following explicit lifetimes could be elided: 'a --> src/value/index.rs:146:6 | 146 | impl<'a> Display for Type<'a> { | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 146 - impl<'a> Display for Type<'a> { 146 + impl Display for Type<'_> { | Checking toml v0.8.19 warning: the following explicit lifetimes could be elided: 'de --> src/number.rs:460:14 | 460 | impl<'de> de::Visitor<'de> for FieldVisitor { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 460 - impl<'de> de::Visitor<'de> for FieldVisitor { 460 + impl de::Visitor<'_> for FieldVisitor { | warning: the following explicit lifetimes could be elided: 'de --> src/number.rs:497:14 | 497 | impl<'de> de::Visitor<'de> for Visitor { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 497 - impl<'de> de::Visitor<'de> for Visitor { 497 + impl de::Visitor<'_> for Visitor { | warning: the following explicit lifetimes could be elided: 'b, 'c --> src/read.rs:132:6 | 132 | impl<'b, 'c, T> Deref for Reference<'b, 'c, T> | ^^ ^^ ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 132 - impl<'b, 'c, T> Deref for Reference<'b, 'c, T> 132 + impl Deref for Reference<'_, '_, T> | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:535:6 | 535 | impl<'a> private::Sealed for SliceRead<'a> {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 535 - impl<'a> private::Sealed for SliceRead<'a> {} 535 + impl private::Sealed for SliceRead<'_> {} | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:673:6 | 673 | impl<'a> private::Sealed for StrRead<'a> {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 673 - impl<'a> private::Sealed for StrRead<'a> {} 673 + impl private::Sealed for StrRead<'_> {} | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:827:6 | 827 | impl<'a> Fused for SliceRead<'a> {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 827 - impl<'a> Fused for SliceRead<'a> {} 827 + impl Fused for SliceRead<'_> {} | warning: the following explicit lifetimes could be elided: 'a --> src/read.rs:828:6 | 828 | impl<'a> Fused for StrRead<'a> {} | ^^ ^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 828 - impl<'a> Fused for StrRead<'a> {} 828 + impl Fused for StrRead<'_> {} | warning: the following explicit lifetimes could be elided: 'de --> src/raw.rs:374:14 | 374 | impl<'de> Visitor<'de> for FieldVisitor { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 374 - impl<'de> Visitor<'de> for FieldVisitor { 374 + impl Visitor<'_> for FieldVisitor { | warning: the following explicit lifetimes could be elided: 'de --> src/raw.rs:439:6 | 439 | impl<'de> Visitor<'de> for BoxedFromString { | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes help: elide the lifetimes | 439 - impl<'de> Visitor<'de> for BoxedFromString { 439 + impl Visitor<'_> for BoxedFromString { | warning: the following explicit lifetimes could be elided: 'de --> tests/regression/issue845.rs:12:6 | 12 | impl<'de, T> serde::de::Visitor<'de> for NumberVisitor | ^^^ ^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes = note: `-W clippy::needless-lifetimes` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::needless_lifetimes)]` help: elide the lifetimes | 12 - impl<'de, T> serde::de::Visitor<'de> for NumberVisitor 12 + impl serde::de::Visitor<'_> for NumberVisitor | --- src/lib.rs | 1 + tests/regression.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8358c76d7..db4bde80b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -314,6 +314,7 @@ clippy::match_single_binding, clippy::needless_doctest_main, clippy::needless_late_init, + clippy::needless_lifetimes, clippy::return_self_not_must_use, clippy::transmute_ptr_to_ptr, clippy::unconditional_recursion, // https://github.com/rust-lang/rust-clippy/issues/12133 diff --git a/tests/regression.rs b/tests/regression.rs index fb2b25c89..22cca8243 100644 --- a/tests/regression.rs +++ b/tests/regression.rs @@ -1,3 +1,5 @@ +#![allow(clippy::needless_lifetimes)] + mod regression { automod::dir!("tests/regression"); } From c1a06c100e1e0a2a4587e629ca132782fca536dc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 17 Oct 2024 19:13:23 +0200 Subject: [PATCH 478/508] Add methods on Map and Value to destroy preserved ordering --- src/map.rs | 22 ++++++++++++++++++++++ src/value/mod.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/src/map.rs b/src/map.rs index 90da86fed..7ff325b9d 100644 --- a/src/map.rs +++ b/src/map.rs @@ -348,6 +348,28 @@ impl Map { { self.map.retain(f); } + + /// Sorts this map's entries in-place using `str`'s usual ordering. + /// + /// If serde_json's "preserve_order" feature is not enabled, this method + /// does no work because all JSON maps are always kept in a sorted state. + /// + /// If serde_json's "preserve_order" feature is enabled, this method + /// destroys the original source order or insertion order of this map in + /// favor of an alphanumerical order that matches how a BTreeMap with the + /// same contents would be ordered. This takes **O(n log n + c)** time where + /// _n_ is the length of the map and _c_ is the capacity. + /// + /// Other maps nested within the values of this map are not sorted. If you + /// need the entire data structure to be sorted at all levels, you must also + /// call `map.`[`values_mut`]`().for_each(`[`Value::sort_all_objects`]`)`. + /// + /// [`values_mut`]: Map::values_mut + #[inline] + pub fn sort_keys(&mut self) { + #[cfg(feature = "preserve_order")] + self.map.sort_unstable_keys(); + } } #[allow(clippy::derivable_impls)] // clippy bug: https://github.com/rust-lang/rust-clippy/issues/7655 diff --git a/src/value/mod.rs b/src/value/mod.rs index 026f10dcb..6b40f9a52 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -861,6 +861,32 @@ impl Value { pub fn take(&mut self) -> Value { mem::replace(self, Value::Null) } + + /// Reorders the entries of all `Value::Object` nested within this JSON + /// value according to `str`'s usual ordering. + /// + /// If serde_json's "preserve_order" feature is not enabled, this method + /// does no work because all JSON maps are always kept in a sorted state. + /// + /// If serde_json's "preserve_order" feature is enabled, this method + /// destroys the original source order or insertion order of the JSON + /// objects in favor of an alphanumerical order that matches how a BTreeMap + /// with the same contents would be ordered. + pub fn sort_all_objects(&mut self) { + #[cfg(feature = "preserve_order")] + { + match self { + Value::Object(map) => { + map.sort_keys(); + map.values_mut().for_each(Value::sort_all_objects); + } + Value::Array(list) => { + list.iter_mut().for_each(Value::sort_all_objects); + } + _ => {} + } + } + } } /// The default value is `Value::Null`. From fe499e869e283390e8b410d0b3bbbdc5866f165c Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 17 Oct 2024 20:03:08 +0200 Subject: [PATCH 479/508] Release 1.0.129 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 36f42c820..05671e9e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.128" +version = "1.0.129" 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 db4bde80b..bb0749452 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.128")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.129")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 8ceb8248b2a25c2813dee9730fb90be0a7381d8e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 07:13:53 -0700 Subject: [PATCH 480/508] Improve rendering of sort_keys documentation --- src/map.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/map.rs b/src/map.rs index 7ff325b9d..eb5297e43 100644 --- a/src/map.rs +++ b/src/map.rs @@ -362,9 +362,10 @@ impl Map { /// /// Other maps nested within the values of this map are not sorted. If you /// need the entire data structure to be sorted at all levels, you must also - /// call `map.`[`values_mut`]`().for_each(`[`Value::sort_all_objects`]`)`. + /// call + /// map.[values_mut]\().for_each([Value::sort_all_objects]). /// - /// [`values_mut`]: Map::values_mut + /// [values_mut]: Map::values_mut #[inline] pub fn sort_keys(&mut self) { #[cfg(feature = "preserve_order")] From 5fab5fc4992b65e5e4926c289be850905a487db0 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:05:49 -0700 Subject: [PATCH 481/508] Format PR 1141 with rustfmt --- src/number.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/number.rs b/src/number.rs index 4bb7882d3..0f43b8595 100644 --- a/src/number.rs +++ b/src/number.rs @@ -326,8 +326,8 @@ impl Number { u.to_string() } }; - Some(Number{ n }) - }, + Some(Number { n }) + } Err(_) => match i64::try_from(i) { Ok(i) => { if i >= 0 { @@ -341,7 +341,7 @@ impl Number { i.to_string() } }; - Some(Number{ n }) + Some(Number { n }) } else { let n = { #[cfg(not(feature = "arbitrary_precision"))] @@ -353,11 +353,11 @@ impl Number { i.to_string() } }; - Some(Number{ n }) + Some(Number { n }) } - }, - Err(_) => None - } + } + Err(_) => None, + }, } } From eb9ce291a4babc41808498f094295217ad6e5ade Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:07:22 -0700 Subject: [PATCH 482/508] Delete unreachable branch from Number::from_i128 --- src/number.rs | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/src/number.rs b/src/number.rs index 0f43b8595..c481b67d4 100644 --- a/src/number.rs +++ b/src/number.rs @@ -330,31 +330,17 @@ impl Number { } Err(_) => match i64::try_from(i) { Ok(i) => { - if i >= 0 { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { - N::PosInt(i as u64) - } - #[cfg(feature = "arbitrary_precision")] - { - i.to_string() - } - }; - Some(Number { n }) - } else { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { - N::NegInt(i) - } - #[cfg(feature = "arbitrary_precision")] - { - i.to_string() - } - }; - Some(Number { n }) - } + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::NegInt(i) + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) } Err(_) => None, }, From c3149efd62311ee64faacee85767ae709c402c36 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:10:50 -0700 Subject: [PATCH 483/508] Flatten logic in Number::from_i128 --- src/number.rs | 54 ++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/number.rs b/src/number.rs index c481b67d4..718c0d744 100644 --- a/src/number.rs +++ b/src/number.rs @@ -314,36 +314,32 @@ impl Number { /// ``` #[inline] pub fn from_i128(i: i128) -> Option { - match u64::try_from(i) { - Ok(u) => { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { - N::PosInt(u) - } - #[cfg(feature = "arbitrary_precision")] - { - u.to_string() - } - }; - Some(Number { n }) - } - Err(_) => match i64::try_from(i) { - Ok(i) => { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { - N::NegInt(i) - } - #[cfg(feature = "arbitrary_precision")] - { - i.to_string() - } - }; - Some(Number { n }) + if let Ok(u) = u64::try_from(i) { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::PosInt(u) + } + #[cfg(feature = "arbitrary_precision")] + { + u.to_string() + } + }; + Some(Number { n }) + } else if let Ok(i) = i64::try_from(i) { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + N::NegInt(i) } - Err(_) => None, - }, + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) + } else { + None } } From f69950635e780396223dc5766599837e1622fcce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:14:39 -0700 Subject: [PATCH 484/508] Allow arbitrarily large i128 in arbitrary_precision mode --- src/number.rs | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/number.rs b/src/number.rs index 718c0d744..c3e156e6e 100644 --- a/src/number.rs +++ b/src/number.rs @@ -314,33 +314,23 @@ impl Number { /// ``` #[inline] pub fn from_i128(i: i128) -> Option { - if let Ok(u) = u64::try_from(i) { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(u) = u64::try_from(i) { N::PosInt(u) - } - #[cfg(feature = "arbitrary_precision")] - { - u.to_string() - } - }; - Some(Number { n }) - } else if let Ok(i) = i64::try_from(i) { - let n = { - #[cfg(not(feature = "arbitrary_precision"))] - { + } else if let Ok(i) = i64::try_from(i) { N::NegInt(i) + } else { + return None; } - #[cfg(feature = "arbitrary_precision")] - { - i.to_string() - } - }; - Some(Number { n }) - } else { - None - } + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) } /// Returns the exact original JSON representation that this Number was From e64c3685054aaed65d6255b063a0ecd94e764aee Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:22:24 -0700 Subject: [PATCH 485/508] Delete #[inline] from some Number methods --- src/number.rs | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/src/number.rs b/src/number.rs index c3e156e6e..511f17ac6 100644 --- a/src/number.rs +++ b/src/number.rs @@ -93,7 +93,6 @@ impl Number { /// // Numbers with a decimal point are not considered integers. /// assert!(!v["c"].is_i64()); /// ``` - #[inline] pub fn is_i64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -123,7 +122,6 @@ impl Number { /// // Numbers with a decimal point are not considered integers. /// assert!(!v["c"].is_u64()); /// ``` - #[inline] pub fn is_u64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -153,7 +151,6 @@ impl Number { /// assert!(!v["b"].is_f64()); /// assert!(!v["c"].is_f64()); /// ``` - #[inline] pub fn is_f64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -184,7 +181,6 @@ impl Number { /// assert_eq!(v["b"].as_i64(), None); /// assert_eq!(v["c"].as_i64(), None); /// ``` - #[inline] pub fn as_i64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -213,7 +209,6 @@ impl Number { /// assert_eq!(v["a"].as_i128(), Some(64)); /// assert_eq!(v["b"].as_i128(), None); /// ``` - #[inline] pub fn as_i128(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -237,7 +232,6 @@ impl Number { /// assert_eq!(v["b"].as_u64(), None); /// assert_eq!(v["c"].as_u64(), None); /// ``` - #[inline] pub fn as_u64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -259,7 +253,6 @@ impl Number { /// assert_eq!(v["b"].as_f64(), Some(64.0)); /// assert_eq!(v["c"].as_f64(), Some(-64.0)); /// ``` - #[inline] pub fn as_f64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -283,7 +276,6 @@ impl Number { /// /// assert!(Number::from_f64(f64::NAN).is_none()); /// ``` - #[inline] pub fn from_f64(f: f64) -> Option { if f.is_finite() { let n = { @@ -312,7 +304,6 @@ impl Number { /// # /// assert!(Number::from_i128(256).is_some()); /// ``` - #[inline] pub fn from_i128(i: i128) -> Option { let n = { #[cfg(not(feature = "arbitrary_precision"))] @@ -422,7 +413,6 @@ impl Debug for Number { impl Serialize for Number { #[cfg(not(feature = "arbitrary_precision"))] - #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -435,7 +425,6 @@ impl Serialize for Number { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -463,7 +452,6 @@ impl<'de> Deserialize<'de> for Number { formatter.write_str("a JSON number") } - #[inline] fn visit_i64(self, value: i64) -> Result { Ok(value.into()) } @@ -475,12 +463,10 @@ impl<'de> Deserialize<'de> for Number { Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) } - #[inline] fn visit_u64(self, value: u64) -> Result { Ok(value.into()) } - #[inline] fn visit_f64(self, value: f64) -> Result where E: de::Error, @@ -489,7 +475,6 @@ impl<'de> Deserialize<'de> for Number { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn visit_map(self, mut visitor: V) -> Result where V: de::MapAccess<'de>, @@ -583,7 +568,6 @@ fn invalid_number() -> Error { macro_rules! deserialize_any { (@expand [$($num_string:tt)*]) => { #[cfg(not(feature = "arbitrary_precision"))] - #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de>, @@ -596,7 +580,6 @@ macro_rules! deserialize_any { } #[cfg(feature = "arbitrary_precision")] - #[inline] fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'de> { @@ -791,7 +774,6 @@ macro_rules! impl_from_unsigned { ) => { $( impl From<$ty> for Number { - #[inline] fn from(u: $ty) -> Self { let n = { #[cfg(not(feature = "arbitrary_precision"))] @@ -814,7 +796,6 @@ macro_rules! impl_from_signed { ) => { $( impl From<$ty> for Number { - #[inline] fn from(i: $ty) -> Self { let n = { #[cfg(not(feature = "arbitrary_precision"))] From f3e2c48cad22659c9993caf829abf16f5780d06b Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:29:30 -0700 Subject: [PATCH 486/508] Delete unneeded imports in doc code --- src/number.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/number.rs b/src/number.rs index 511f17ac6..cecad1b4a 100644 --- a/src/number.rs +++ b/src/number.rs @@ -268,8 +268,6 @@ impl Number { /// numbers. /// /// ``` - /// # use std::f64; - /// # /// # use serde_json::Number; /// # /// assert!(Number::from_f64(256.0).is_some()); @@ -298,8 +296,6 @@ impl Number { /// numbers. /// /// ``` - /// # use std::i128; - /// # /// # use serde_json::Number; /// # /// assert!(Number::from_i128(256).is_some()); From fb12a9db76c6233f7cf8517bbed066b37b230665 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 09:27:30 -0700 Subject: [PATCH 487/508] Update Number::from_i128 documentation regarding bounds --- src/number.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/number.rs b/src/number.rs index cecad1b4a..242c57926 100644 --- a/src/number.rs +++ b/src/number.rs @@ -292,8 +292,9 @@ impl Number { } } - /// Converts an `i128` to a `Number`. Greater than u64::MAX values are not JSON - /// numbers. + /// Converts an `i128` to a `Number`. Numbers smaller than i64::MIN or + /// larger than u64::MAX can only be represented in `Number` if serde_json's + /// "arbitrary_precision" feature is enabled. /// /// ``` /// # use serde_json::Number; From 1a944f47ed20148548a59fddb71b93646f4be1ce Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 10:08:05 -0700 Subject: [PATCH 488/508] Delete Value::as_i128 This is adequately supported by `value.as_number().and_then(Number::as_i128)` --- src/value/mod.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/src/value/mod.rs b/src/value/mod.rs index 704577e9c..6b40f9a52 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -635,24 +635,6 @@ impl Value { } } - /// If the `Value` is an integer, represent it as i128 if possible. Returns - /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": 256.0 }); - /// - /// assert_eq!(v["a"].as_i128(), Some(64)); - /// assert_eq!(v["b"].as_i128(), None); - /// ``` - pub fn as_i128(&self) -> Option { - match self { - Value::Number(n) => n.as_i128(), - _ => None, - } - } - /// If the `Value` is an integer, represent it as u64 if possible. Returns /// None otherwise. /// From 95f55f3802e4850f9b4e99abf31675c519cbd816 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 10:20:37 -0700 Subject: [PATCH 489/508] Delete Number conversion example code All of these are actually calling methods on Value, not Number. The example code on Value methods is adequate without duplicating them to Number. --- src/number.rs | 81 --------------------------------------------------- 1 file changed, 81 deletions(-) diff --git a/src/number.rs b/src/number.rs index 242c57926..3982552fd 100644 --- a/src/number.rs +++ b/src/number.rs @@ -78,21 +78,6 @@ impl Number { /// /// For any Number on which `is_i64` returns true, `as_i64` is guaranteed to /// return the integer value. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let big = i64::MAX as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); - /// - /// assert!(v["a"].is_i64()); - /// - /// // Greater than i64::MAX. - /// assert!(!v["b"].is_i64()); - /// - /// // Numbers with a decimal point are not considered integers. - /// assert!(!v["c"].is_i64()); - /// ``` pub fn is_i64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -108,20 +93,6 @@ impl Number { /// /// For any Number on which `is_u64` returns true, `as_u64` is guaranteed to /// return the integer value. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": -64, "c": 256.0 }); - /// - /// assert!(v["a"].is_u64()); - /// - /// // Negative integer. - /// assert!(!v["b"].is_u64()); - /// - /// // Numbers with a decimal point are not considered integers. - /// assert!(!v["c"].is_u64()); - /// ``` pub fn is_u64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -139,18 +110,6 @@ impl Number { /// /// Currently this function returns true if and only if both `is_i64` and /// `is_u64` return false but this is not a guarantee in the future. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 256.0, "b": 64, "c": -64 }); - /// - /// assert!(v["a"].is_f64()); - /// - /// // Integers. - /// assert!(!v["b"].is_f64()); - /// assert!(!v["c"].is_f64()); - /// ``` pub fn is_f64(&self) -> bool { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -170,17 +129,6 @@ impl Number { /// If the `Number` is an integer, represent it as i64 if possible. Returns /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let big = i64::MAX as u64 + 10; - /// let v = json!({ "a": 64, "b": big, "c": 256.0 }); - /// - /// assert_eq!(v["a"].as_i64(), Some(64)); - /// assert_eq!(v["b"].as_i64(), None); - /// assert_eq!(v["c"].as_i64(), None); - /// ``` pub fn as_i64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -200,15 +148,6 @@ impl Number { /// If the `Number` is an integer, represent it as i128 if possible. Returns /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": 256.0 }); - /// - /// assert_eq!(v["a"].as_i128(), Some(64)); - /// assert_eq!(v["b"].as_i128(), None); - /// ``` pub fn as_i128(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -222,16 +161,6 @@ impl Number { /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 64, "b": -64, "c": 256.0 }); - /// - /// assert_eq!(v["a"].as_u64(), Some(64)); - /// assert_eq!(v["b"].as_u64(), None); - /// assert_eq!(v["c"].as_u64(), None); - /// ``` pub fn as_u64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { @@ -243,16 +172,6 @@ impl Number { } /// Represents the number as f64 if possible. Returns None otherwise. - /// - /// ``` - /// # use serde_json::json; - /// # - /// let v = json!({ "a": 256.0, "b": 64, "c": -64 }); - /// - /// assert_eq!(v["a"].as_f64(), Some(256.0)); - /// assert_eq!(v["b"].as_f64(), Some(64.0)); - /// assert_eq!(v["c"].as_f64(), Some(-64.0)); - /// ``` pub fn as_f64(&self) -> Option { #[cfg(not(feature = "arbitrary_precision"))] match self.n { From be68eabf91b2332a786ee23fe338019566165380 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 10:16:03 -0700 Subject: [PATCH 490/508] Return error instead of Null if i128 deserialized to Value is out of range --- src/value/de.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/value/de.rs b/src/value/de.rs index c38253aff..3973ae794 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -44,9 +44,12 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } - #[inline] - fn visit_i128(self, value: i128) -> Result { - Ok(Number::from_i128(value).map_or(Value::Null, Value::Number)) + fn visit_i128(self, value: i128) -> Result + where + E: serde::de::Error, + { + let de = serde::de::value::I128Deserializer::new(value); + Number::deserialize(de).map(Value::Number) } #[inline] From d86703f33dc5a1569df8a164bc800b23e905a49f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 08:26:12 -0700 Subject: [PATCH 491/508] Add Number u128 conversions --- src/number.rs | 78 ++++++++++++++++++++++++++++++++++++++++--------- src/value/de.rs | 8 +++++ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/number.rs b/src/number.rs index 3982552fd..8cb0c3b7b 100644 --- a/src/number.rs +++ b/src/number.rs @@ -146,19 +146,6 @@ impl Number { self.n.parse().ok() } - /// If the `Number` is an integer, represent it as i128 if possible. Returns - /// None otherwise. - pub fn as_i128(&self) -> Option { - #[cfg(not(feature = "arbitrary_precision"))] - match self.n { - N::PosInt(n) => Some(n as i128), - N::NegInt(n) => Some(n as i128), - N::Float(_) => None, - } - #[cfg(feature = "arbitrary_precision")] - self.n.parse().ok() - } - /// If the `Number` is an integer, represent it as u64 if possible. Returns /// None otherwise. pub fn as_u64(&self) -> Option { @@ -211,6 +198,31 @@ impl Number { } } + /// If the `Number` is an integer, represent it as i128 if possible. Returns + /// None otherwise. + pub fn as_i128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as i128), + N::NegInt(n) => Some(n as i128), + N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + + /// If the `Number` is an integer, represent it as u128 if possible. Returns + /// None otherwise. + pub fn as_u128(&self) -> Option { + #[cfg(not(feature = "arbitrary_precision"))] + match self.n { + N::PosInt(n) => Some(n as u128), + N::NegInt(_) | N::Float(_) => None, + } + #[cfg(feature = "arbitrary_precision")] + self.n.parse().ok() + } + /// Converts an `i128` to a `Number`. Numbers smaller than i64::MIN or /// larger than u64::MAX can only be represented in `Number` if serde_json's /// "arbitrary_precision" feature is enabled. @@ -240,6 +252,33 @@ impl Number { Some(Number { n }) } + /// Converts a `u128` to a `Number`. Numbers greater than u64::MAX can only + /// be represented in `Number` if serde_json's "arbitrary_precision" feature + /// is enabled. + /// + /// ``` + /// # use serde_json::Number; + /// # + /// assert!(Number::from_u128(256).is_some()); + /// ``` + pub fn from_u128(i: u128) -> Option { + let n = { + #[cfg(not(feature = "arbitrary_precision"))] + { + if let Ok(u) = u64::try_from(i) { + N::PosInt(u) + } else { + return None; + } + } + #[cfg(feature = "arbitrary_precision")] + { + i.to_string() + } + }; + Some(Number { n }) + } + /// Returns the exact original JSON representation that this Number was /// parsed from. /// @@ -376,13 +415,22 @@ impl<'de> Deserialize<'de> for Number { where E: de::Error, { - Number::from_i128(value).ok_or_else(|| de::Error::custom("not a JSON number")) + Number::from_i128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) } fn visit_u64(self, value: u64) -> Result { Ok(value.into()) } + fn visit_u128(self, value: u128) -> Result + where + E: de::Error, + { + Number::from_u128(value) + .ok_or_else(|| de::Error::custom("JSON number out of range")) + } + fn visit_f64(self, value: f64) -> Result where E: de::Error, @@ -503,6 +551,8 @@ macro_rules! deserialize_any { return visitor.visit_u64(u); } else if let Some(i) = self.as_i64() { return visitor.visit_i64(i); + } else if let Some(u) = self.as_u128() { + return visitor.visit_u128(u); } else if let Some(i) = self.as_i128() { return visitor.visit_i128(i); } else if let Some(f) = self.as_f64() { diff --git a/src/value/de.rs b/src/value/de.rs index 3973ae794..45891f033 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -57,6 +57,14 @@ impl<'de> Deserialize<'de> for Value { Ok(Value::Number(value.into())) } + fn visit_u128(self, value: u128) -> Result + where + E: serde::de::Error, + { + let de = serde::de::value::U128Deserializer::new(value); + Number::deserialize(de).map(Value::Number) + } + #[inline] fn visit_f64(self, value: f64) -> Result { Ok(Number::from_f64(value).map_or(Value::Null, Value::Number)) From 2825e15cf447a251e403c4a83820959f81e60711 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 10:34:39 -0700 Subject: [PATCH 492/508] Release 1.0.130 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 05671e9e5..aca212813 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.129" +version = "1.0.130" 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 bb0749452..77a4d2442 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.129")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.130")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 0d084c50380a91b0b83ee96eb5e8ae04de7baaf5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 10:42:15 -0700 Subject: [PATCH 493/508] Touch up PR 1135 --- src/map.rs | 5 +++-- src/value/de.rs | 8 ++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/map.rs b/src/map.rs index d8e700c1c..bc9a9307b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -6,6 +6,7 @@ //! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html //! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html +use crate::error::Error; use crate::value::Value; use alloc::string::String; #[cfg(feature = "preserve_order")] @@ -589,7 +590,7 @@ macro_rules! delegate_iterator { } } -impl<'de> de::IntoDeserializer<'de, crate::Error> for Map { +impl<'de> de::IntoDeserializer<'de, Error> for Map { type Deserializer = Self; fn into_deserializer(self) -> Self::Deserializer { @@ -597,7 +598,7 @@ impl<'de> de::IntoDeserializer<'de, crate::Error> for Map { } } -impl<'de> de::IntoDeserializer<'de, crate::Error> for &'de Map { +impl<'de> de::IntoDeserializer<'de, Error> for &'de Map { type Deserializer = Self; fn into_deserializer(self) -> Self::Deserializer { diff --git a/src/value/de.rs b/src/value/de.rs index b494b981b..dd4698e80 100644 --- a/src/value/de.rs +++ b/src/value/de.rs @@ -11,8 +11,8 @@ use core::fmt; use core::slice; use core::str::FromStr; use serde::de::{ - self, Deserialize, DeserializeSeed, EnumAccess, Expected, IntoDeserializer, MapAccess, - SeqAccess, Unexpected, VariantAccess, Visitor, + self, Deserialize, DeserializeSeed, Deserializer as _, EnumAccess, Expected, IntoDeserializer, + MapAccess, SeqAccess, Unexpected, VariantAccess, Visitor, }; use serde::forward_to_deserialize_any; @@ -597,8 +597,6 @@ impl<'de> VariantAccess<'de> for VariantDeserializer { where V: Visitor<'de>, { - use serde::de::Deserializer; - match self.value { Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( @@ -1109,8 +1107,6 @@ impl<'de> VariantAccess<'de> for VariantRefDeserializer<'de> { where V: Visitor<'de>, { - use serde::de::Deserializer; - match self.value { Some(Value::Object(v)) => v.deserialize_any(visitor), Some(other) => Err(serde::de::Error::invalid_type( From a810ba985095ecd5bb3f8741f0e6d71e36975cb6 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Oct 2024 15:50:51 -0700 Subject: [PATCH 494/508] Release 1.0.131 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index aca212813..c004b536c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.130" +version = "1.0.131" 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 77a4d2442..45ef1954d 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.130")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.131")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From b71ccd2d8f6aabaa0ac6cb889f8e18989038d968 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 19 Oct 2024 08:50:31 -0700 Subject: [PATCH 495/508] Reduce duplicative instantiation of logic in SeqAccess and MapAccess --- src/de.rs | 108 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 63 insertions(+), 45 deletions(-) diff --git a/src/de.rs b/src/de.rs index 7959a6635..d6cb502da 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1926,31 +1926,41 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { where T: de::DeserializeSeed<'de>, { - let peek = match tri!(self.de.parse_whitespace()) { - Some(b']') => { - return Ok(None); - } - Some(b',') if !self.first => { - self.de.eat_char(); - tri!(self.de.parse_whitespace()) - } - Some(b) => { - if self.first { - self.first = false; - Some(b) - } else { - return Err(self.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)); + fn has_next_element<'de, 'a, R: Read<'de> + 'a>( + seq: &mut SeqAccess<'a, R>, + ) -> Result { + let peek = match tri!(seq.de.parse_whitespace()) { + Some(b']') => { + return Ok(false); } + Some(b',') if !seq.first => { + seq.de.eat_char(); + tri!(seq.de.parse_whitespace()) + } + Some(b) => { + if seq.first { + seq.first = false; + Some(b) + } else { + return Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)); + } + } + None => { + return Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)); + } + }; + + match peek { + Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Ok(true), + None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), } - None => { - return Err(self.de.peek_error(ErrorCode::EofWhileParsingList)); - } - }; + } - match peek { - Some(b']') => Err(self.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Ok(Some(tri!(seed.deserialize(&mut *self.de)))), - None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)), + if tri!(has_next_element(self)) { + Ok(Some(tri!(seed.deserialize(&mut *self.de)))) + } else { + Ok(None) } } } @@ -1973,32 +1983,40 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { where K: de::DeserializeSeed<'de>, { - let peek = match tri!(self.de.parse_whitespace()) { - Some(b'}') => { - return Ok(None); - } - Some(b',') if !self.first => { - self.de.eat_char(); - tri!(self.de.parse_whitespace()) - } - Some(b) => { - if self.first { - self.first = false; - Some(b) - } else { - return Err(self.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)); + fn has_next_key<'de, 'a, R: Read<'de> + 'a>(map: &mut MapAccess<'a, R>) -> Result { + let peek = match tri!(map.de.parse_whitespace()) { + Some(b'}') => { + return Ok(false); } + Some(b',') if !map.first => { + map.de.eat_char(); + tri!(map.de.parse_whitespace()) + } + Some(b) => { + if map.first { + map.first = false; + Some(b) + } else { + return Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)); + } + } + None => { + return Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)); + } + }; + + match peek { + Some(b'"') => Ok(true), + Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), + None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), } - None => { - return Err(self.de.peek_error(ErrorCode::EofWhileParsingObject)); - } - }; + } - match peek { - Some(b'"') => seed.deserialize(MapKey { de: &mut *self.de }).map(Some), - Some(b'}') => Err(self.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Err(self.de.peek_error(ErrorCode::KeyMustBeAString)), - None => Err(self.de.peek_error(ErrorCode::EofWhileParsingValue)), + if tri!(has_next_key(self)) { + Ok(Some(tri!(seed.deserialize(MapKey { de: &mut *self.de })))) + } else { + Ok(None) } } From 2a4cb44f7cc591a4542cbb1a73ca5bdc8a02c0d7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 19 Oct 2024 09:21:55 -0700 Subject: [PATCH 496/508] Rearrange 'match peek' --- src/de.rs | 60 ++++++++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 34 deletions(-) diff --git a/src/de.rs b/src/de.rs index d6cb502da..88ca3e66c 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1929,31 +1929,25 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { fn has_next_element<'de, 'a, R: Read<'de> + 'a>( seq: &mut SeqAccess<'a, R>, ) -> Result { - let peek = match tri!(seq.de.parse_whitespace()) { - Some(b']') => { - return Ok(false); - } + match tri!(seq.de.parse_whitespace()) { + Some(b']') => Ok(false), Some(b',') if !seq.first => { seq.de.eat_char(); - tri!(seq.de.parse_whitespace()) + match tri!(seq.de.parse_whitespace()) { + Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Ok(true), + None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), + } } - Some(b) => { + Some(_) => { if seq.first { seq.first = false; - Some(b) + Ok(true) } else { - return Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)); + Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)) } } - None => { - return Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)); - } - }; - - match peek { - Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Ok(true), - None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), + None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)), } } @@ -1984,32 +1978,30 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { K: de::DeserializeSeed<'de>, { fn has_next_key<'de, 'a, R: Read<'de> + 'a>(map: &mut MapAccess<'a, R>) -> Result { - let peek = match tri!(map.de.parse_whitespace()) { - Some(b'}') => { - return Ok(false); - } + match tri!(map.de.parse_whitespace()) { + Some(b'}') => Ok(false), Some(b',') if !map.first => { map.de.eat_char(); - tri!(map.de.parse_whitespace()) + match tri!(map.de.parse_whitespace()) { + Some(b'"') => Ok(true), + Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), + None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), + } } Some(b) => { if map.first { map.first = false; - Some(b) + if b == b'"' { + Ok(true) + } else { + Err(map.de.peek_error(ErrorCode::KeyMustBeAString)) + } } else { - return Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)); + Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)) } } - None => { - return Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)); - } - }; - - match peek { - Some(b'"') => Ok(true), - Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), - None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), + None => Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)), } } From 0f54a1a0df5045aee4a2d2f8656c365d835095e5 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 19 Oct 2024 09:22:33 -0700 Subject: [PATCH 497/508] Handle early return sooner on eof in seq or map This matches how peeking is done within deserialize_any and other Deserializer methods --- src/de.rs | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/de.rs b/src/de.rs index 88ca3e66c..a22fcf0de 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1929,9 +1929,16 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { fn has_next_element<'de, 'a, R: Read<'de> + 'a>( seq: &mut SeqAccess<'a, R>, ) -> Result { - match tri!(seq.de.parse_whitespace()) { - Some(b']') => Ok(false), - Some(b',') if !seq.first => { + let peek = match tri!(seq.de.parse_whitespace()) { + Some(b) => b, + None => { + return Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)); + } + }; + + match peek { + b']' => Ok(false), + b',' if !seq.first => { seq.de.eat_char(); match tri!(seq.de.parse_whitespace()) { Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), @@ -1939,7 +1946,7 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), } } - Some(_) => { + _ => { if seq.first { seq.first = false; Ok(true) @@ -1947,7 +1954,6 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)) } } - None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingList)), } } @@ -1978,9 +1984,16 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { K: de::DeserializeSeed<'de>, { fn has_next_key<'de, 'a, R: Read<'de> + 'a>(map: &mut MapAccess<'a, R>) -> Result { - match tri!(map.de.parse_whitespace()) { - Some(b'}') => Ok(false), - Some(b',') if !map.first => { + let peek = match tri!(map.de.parse_whitespace()) { + Some(b) => b, + None => { + return Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)); + } + }; + + match peek { + b'}' => Ok(false), + b',' if !map.first => { map.de.eat_char(); match tri!(map.de.parse_whitespace()) { Some(b'"') => Ok(true), @@ -1989,10 +2002,10 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), } } - Some(b) => { + _ => { if map.first { map.first = false; - if b == b'"' { + if peek == b'"' { Ok(true) } else { Err(map.de.peek_error(ErrorCode::KeyMustBeAString)) @@ -2001,7 +2014,6 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)) } } - None => Err(map.de.peek_error(ErrorCode::EofWhileParsingObject)), } } From f2082d2a04b3b5d72503ac89e2182a5833bb2a1e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 19 Oct 2024 09:23:00 -0700 Subject: [PATCH 498/508] Clearer order of comparisons We look at seq.first/map.first only once. --- src/de.rs | 68 ++++++++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/de.rs b/src/de.rs index a22fcf0de..5b64138d8 100644 --- a/src/de.rs +++ b/src/de.rs @@ -1936,24 +1936,20 @@ impl<'de, 'a, R: Read<'de> + 'a> de::SeqAccess<'de> for SeqAccess<'a, R> { } }; - match peek { - b']' => Ok(false), - b',' if !seq.first => { - seq.de.eat_char(); - match tri!(seq.de.parse_whitespace()) { - Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Ok(true), - None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), - } - } - _ => { - if seq.first { - seq.first = false; - Ok(true) - } else { - Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)) - } + if peek == b']' { + Ok(false) + } else if seq.first { + seq.first = false; + Ok(true) + } else if peek == b',' { + seq.de.eat_char(); + match tri!(seq.de.parse_whitespace()) { + Some(b']') => Err(seq.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Ok(true), + None => Err(seq.de.peek_error(ErrorCode::EofWhileParsingValue)), } + } else { + Err(seq.de.peek_error(ErrorCode::ExpectedListCommaOrEnd)) } } @@ -1991,29 +1987,25 @@ impl<'de, 'a, R: Read<'de> + 'a> de::MapAccess<'de> for MapAccess<'a, R> { } }; - match peek { - b'}' => Ok(false), - b',' if !map.first => { - map.de.eat_char(); - match tri!(map.de.parse_whitespace()) { - Some(b'"') => Ok(true), - Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), - Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), - None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), - } + if peek == b'}' { + Ok(false) + } else if map.first { + map.first = false; + if peek == b'"' { + Ok(true) + } else { + Err(map.de.peek_error(ErrorCode::KeyMustBeAString)) } - _ => { - if map.first { - map.first = false; - if peek == b'"' { - Ok(true) - } else { - Err(map.de.peek_error(ErrorCode::KeyMustBeAString)) - } - } else { - Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)) - } + } else if peek == b',' { + map.de.eat_char(); + match tri!(map.de.parse_whitespace()) { + Some(b'"') => Ok(true), + Some(b'}') => Err(map.de.peek_error(ErrorCode::TrailingComma)), + Some(_) => Err(map.de.peek_error(ErrorCode::KeyMustBeAString)), + None => Err(map.de.peek_error(ErrorCode::EofWhileParsingValue)), } + } else { + Err(map.de.peek_error(ErrorCode::ExpectedObjectCommaOrEnd)) } } From 86d933cfd7b5665270e66296694468286794ae44 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 19 Oct 2024 09:32:49 -0700 Subject: [PATCH 499/508] Release 1.0.132 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c004b536c..fb5da8d8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.131" +version = "1.0.132" 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 45ef1954d..f8312c73c 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.131")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.132")] // Ignored clippy lints #![allow( clippy::collapsible_else_if, From 42ab31feacef82f1cb4c11892b43fe2d89208cd7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Wed, 30 Oct 2024 19:18:57 -0700 Subject: [PATCH 500/508] Update ui test suite to nightly-2024-10-31 --- tests/ui/missing_comma.stderr | 2 +- tests/ui/parse_expr.stderr | 2 +- 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 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/ui/missing_comma.stderr b/tests/ui/missing_comma.stderr index bafa0f891..b0f0e4bd4 100644 --- a/tests/ui/missing_comma.stderr +++ b/tests/ui/missing_comma.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `"2"` +error: no rules expected `"2"` --> tests/ui/missing_comma.rs:4:21 | 4 | json!({ "1": "" "2": "" }); diff --git a/tests/ui/parse_expr.stderr b/tests/ui/parse_expr.stderr index cd3e1c94d..70cd74162 100644 --- a/tests/ui/parse_expr.stderr +++ b/tests/ui/parse_expr.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `~` +error: no rules expected `~` --> tests/ui/parse_expr.rs:4:19 | 4 | json!({ "a" : ~ }); diff --git a/tests/ui/unexpected_after_array_element.stderr b/tests/ui/unexpected_after_array_element.stderr index ef449f764..b848e4dbd 100644 --- a/tests/ui/unexpected_after_array_element.stderr +++ b/tests/ui/unexpected_after_array_element.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `=>` +error: no rules expected `=>` --> tests/ui/unexpected_after_array_element.rs:4:18 | 4 | json!([ true => ]); diff --git a/tests/ui/unexpected_after_map_entry.stderr b/tests/ui/unexpected_after_map_entry.stderr index c62d90ba0..9f77c0729 100644 --- a/tests/ui/unexpected_after_map_entry.stderr +++ b/tests/ui/unexpected_after_map_entry.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `=>` +error: no rules expected `=>` --> tests/ui/unexpected_after_map_entry.rs:4:23 | 4 | json!({ "k": true => }); diff --git a/tests/ui/unexpected_colon.stderr b/tests/ui/unexpected_colon.stderr index 7e47726bc..d47e88161 100644 --- a/tests/ui/unexpected_colon.stderr +++ b/tests/ui/unexpected_colon.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `:` +error: no rules expected `:` --> tests/ui/unexpected_colon.rs:4:13 | 4 | json!({ : true }); diff --git a/tests/ui/unexpected_comma.stderr b/tests/ui/unexpected_comma.stderr index 552f399a5..e30827453 100644 --- a/tests/ui/unexpected_comma.stderr +++ b/tests/ui/unexpected_comma.stderr @@ -1,4 +1,4 @@ -error: no rules expected the token `,` +error: no rules expected `,` --> tests/ui/unexpected_comma.rs:4:17 | 4 | json!({ "a" , "b": true }); From 7cce517f53fd60d40bd6ef87a0e51f88a306e901 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Mon, 4 Nov 2024 18:19:58 -0800 Subject: [PATCH 501/508] Raise minimum version for preserve_order feature to Rust 1.65 Required by hashbrown 0.15.1. error: package `hashbrown v0.15.1` cannot be built because it requires rustc 1.65.0 or newer, while the currently active rustc version is 1.64.0 --- .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 72f1c9197..c41b2f6dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: strategy: fail-fast: false matrix: - rust: [beta, 1.64.0, 1.56.1] + rust: [beta, 1.65.0, 1.56.1] os: [ubuntu] include: - rust: stable From be2198a54d0d9e668f8e2e59ca2e23e3ac10ae60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 8 Nov 2024 21:45:52 -0500 Subject: [PATCH 502/508] Prevent upload-artifact step from causing CI failure This step has been failing way more than reasonable across my various repos. With the provided path, there will be 1 file uploaded Artifact name is valid! Root directory input is valid! Attempt 1 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 3000 ms... Attempt 2 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 6029 ms... Attempt 3 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 8270 ms... Attempt 4 of 5 failed with error: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact. Retrying request in 12577 ms... Error: Failed to CreateArtifact: Failed to make request after 5 attempts: Request timeout: /twirp/github.actions.results.api.v1.ArtifactService/CreateArtifact --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c41b2f6dd..387322431 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,6 +36,7 @@ jobs: with: name: Cargo.lock path: Cargo.lock + continue-on-error: true build: name: Rust ${{matrix.rust}} ${{matrix.os == 'windows' && '(windows)' || ''}} From 73011c0b2bcd21639446728fc50de4131a51e5ca Mon Sep 17 00:00:00 2001 From: "Dustin J. Mitchell" Date: Thu, 14 Nov 2024 22:16:25 +0000 Subject: [PATCH 503/508] Add a safety comment to unsafe block --- src/read.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/read.rs b/src/read.rs index a3aa5d164..1ee5e6286 100644 --- a/src/read.rs +++ b/src/read.rs @@ -977,6 +977,10 @@ fn push_wtf8_codepoint(n: u32, scratch: &mut Vec) { scratch.reserve(4); + // SAFETY: After the `reserve` call, `scratch` has at least 4 bytes of allocated but + // unintialized memory after its last initialized byte, which is where `ptr` points. All + // reachable match arms write `encoded_len` bytes to that region and update the length + // accordingly, and `encoded_len` is always <= 4. unsafe { let ptr = scratch.as_mut_ptr().add(scratch.len()); From 07f280a79c07a5e713d519ed6c6a1a71b0cf871a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Thu, 14 Nov 2024 18:44:18 -0800 Subject: [PATCH 504/508] Wrap PR 1213 to 80 columns --- src/read.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/read.rs b/src/read.rs index 1ee5e6286..eaa46996b 100644 --- a/src/read.rs +++ b/src/read.rs @@ -977,10 +977,11 @@ fn push_wtf8_codepoint(n: u32, scratch: &mut Vec) { scratch.reserve(4); - // SAFETY: After the `reserve` call, `scratch` has at least 4 bytes of allocated but - // unintialized memory after its last initialized byte, which is where `ptr` points. All - // reachable match arms write `encoded_len` bytes to that region and update the length - // accordingly, and `encoded_len` is always <= 4. + // SAFETY: After the `reserve` call, `scratch` has at least 4 bytes of + // allocated but unintialized memory after its last initialized byte, which + // is where `ptr` points. All reachable match arms write `encoded_len` bytes + // to that region and update the length accordingly, and `encoded_len` is + // always <= 4. unsafe { let ptr = scratch.as_mut_ptr().add(scratch.len()); From a11f5f2bc4756035e5a04e01ad486d8a99779527 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 15 Nov 2024 18:47:02 -0800 Subject: [PATCH 505/508] Resolve unnecessary_map_or clippy lints warning: this `map_or` is redundant --> src/value/partial_eq.rs:5:5 | 5 | value.as_i64().map_or(false, |i| i == other) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(value.as_i64() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or = note: `-W clippy::unnecessary-map-or` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::unnecessary_map_or)]` warning: this `map_or` is redundant --> src/value/partial_eq.rs:9:5 | 9 | value.as_u64().map_or(false, |i| i == other) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(value.as_u64() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or warning: this `map_or` is redundant --> src/value/partial_eq.rs:14:29 | 14 | Value::Number(n) => n.as_f32().map_or(false, |i| i == other), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(n.as_f32() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or warning: this `map_or` is redundant --> src/value/partial_eq.rs:20:5 | 20 | value.as_f64().map_or(false, |i| i == other) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(value.as_f64() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or warning: this `map_or` is redundant --> src/value/partial_eq.rs:24:5 | 24 | value.as_bool().map_or(false, |i| i == other) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(value.as_bool() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or warning: this `map_or` is redundant --> src/value/partial_eq.rs:28:5 | 28 | value.as_str().map_or(false, |i| i == other) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(value.as_str() == Some(other))` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or --- src/value/partial_eq.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/value/partial_eq.rs b/src/value/partial_eq.rs index 46c1dbc33..8626eed7f 100644 --- a/src/value/partial_eq.rs +++ b/src/value/partial_eq.rs @@ -2,30 +2,30 @@ use super::Value; use alloc::string::String; fn eq_i64(value: &Value, other: i64) -> bool { - value.as_i64().map_or(false, |i| i == other) + value.as_i64() == Some(other) } fn eq_u64(value: &Value, other: u64) -> bool { - value.as_u64().map_or(false, |i| i == other) + value.as_u64() == Some(other) } fn eq_f32(value: &Value, other: f32) -> bool { match value { - Value::Number(n) => n.as_f32().map_or(false, |i| i == other), + Value::Number(n) => n.as_f32() == Some(other), _ => false, } } fn eq_f64(value: &Value, other: f64) -> bool { - value.as_f64().map_or(false, |i| i == other) + value.as_f64() == Some(other) } fn eq_bool(value: &Value, other: bool) -> bool { - value.as_bool().map_or(false, |i| i == other) + value.as_bool() == Some(other) } fn eq_str(value: &Value, other: &str) -> bool { - value.as_str().map_or(false, |i| i == other) + value.as_str() == Some(other) } impl PartialEq for Value { From 2ccb5b67ca34a5e53b42fa608d756e25e57f91a3 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 15 Nov 2024 18:48:24 -0800 Subject: [PATCH 506/508] Disable question_mark clippy lint in lexical test Serde_json uses #![deny(clippy::question_mark_used)]. warning: this `match` expression can be replaced with `?` --> tests/../src/lexical/algorithm.rs:54:21 | 54 | let value = match mantissa.checked_mul(power) { | _____________________^ 55 | | None => return None, 56 | | Some(value) => value, 57 | | }; | |_________^ help: try instead: `mantissa.checked_mul(power)?` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#question_mark = note: `-W clippy::question-mark` implied by `-W clippy::all` = help: to override `-W clippy::all` add `#[allow(clippy::question_mark)]` --- tests/lexical.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/lexical.rs b/tests/lexical.rs index 368c84478..77967956c 100644 --- a/tests/lexical.rs +++ b/tests/lexical.rs @@ -12,6 +12,7 @@ clippy::let_underscore_untyped, clippy::module_name_repetitions, clippy::needless_late_init, + clippy::question_mark, clippy::shadow_unrelated, clippy::similar_names, clippy::single_match_else, From 4e5f985958bedbc7e75ba6c1931edaf73508c4a7 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 16 Nov 2024 17:17:56 -0800 Subject: [PATCH 507/508] Implement From<[T; N]> for Value --- src/value/from.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/value/from.rs b/src/value/from.rs index 2dae925a1..df4b2038c 100644 --- a/src/value/from.rs +++ b/src/value/from.rs @@ -183,6 +183,12 @@ impl> From> for Value { } } +impl, const N: usize> From<[T; N]> for Value { + fn from(array: [T; N]) -> Self { + Value::Array(array.into_iter().map(Into::into).collect()) + } +} + impl> From<&[T]> for Value { /// Convert a slice to `Value::Array`. /// From 0903de449c52c1b4a2271e909b7afb18909dc379 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 16 Nov 2024 17:26:01 -0800 Subject: [PATCH 508/508] Release 1.0.133 --- Cargo.toml | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fb5da8d8b..4d9e82c7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "serde_json" -version = "1.0.132" +version = "1.0.133" 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 f8312c73c..b98cad696 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.132")] +#![doc(html_root_url = "https://docs.rs/serde_json/1.0.133")] // Ignored clippy lints #![allow( clippy::collapsible_else_if,