Skip to content

Commit

Permalink
Merge pull request #1037 from dtolnay/numkey
Browse files Browse the repository at this point in the history
Insert check for whitespace surrounding numeric map key deserializing from Value
  • Loading branch information
dtolnay authored Jul 11, 2023
2 parents 51b1bd0 + 9f4c4af commit 0c4a7f8
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 95 deletions.
159 changes: 84 additions & 75 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.disable_recursion_limit = true;
}

fn peek(&mut self) -> Result<Option<u8>> {
pub(crate) fn peek(&mut self) -> Result<Option<u8>> {
self.read.peek()
}

Expand Down Expand Up @@ -309,9 +309,9 @@ impl<'de, R: Read<'de>> Deserializer<R> {
self.fix_position(err)
}

fn deserialize_number<V>(&mut self, visitor: V) -> Result<V::Value>
pub(crate) fn deserialize_number<'any, V>(&mut self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
V: de::Visitor<'any>,
{
let peek = match tri!(self.parse_whitespace()) {
Some(b) => b,
Expand All @@ -335,6 +335,79 @@ impl<'de, R: Read<'de>> Deserializer<R> {
}
}

#[cfg(feature = "float_roundtrip")]
pub(crate) fn do_deserialize_f32<'any, V>(&mut self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'any>,
{
self.single_precision = true;
let val = self.deserialize_number(visitor);
self.single_precision = false;
val
}

pub(crate) fn do_deserialize_i128<'any, V>(&mut self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'any>,
{
let mut buf = String::new();

match tri!(self.parse_whitespace()) {
Some(b'-') => {
self.eat_char();
buf.push('-');
}
Some(_) => {}
None => {
return Err(self.peek_error(ErrorCode::EofWhileParsingValue));
}
};

tri!(self.scan_integer128(&mut buf));

let value = match buf.parse() {
Ok(int) => visitor.visit_i128(int),
Err(_) => {
return Err(self.error(ErrorCode::NumberOutOfRange));
}
};

match value {
Ok(value) => Ok(value),
Err(err) => Err(self.fix_position(err)),
}
}

pub(crate) fn do_deserialize_u128<'any, V>(&mut self, visitor: V) -> Result<V::Value>
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' => {
Expand Down Expand Up @@ -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<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.deserialize_number(visitor)
self.$using(visitor)
}
};
}
Expand Down Expand Up @@ -1424,77 +1501,9 @@ impl<'de, 'a, R: Read<'de>> de::Deserializer<'de> for &'a mut Deserializer<R> {
deserialize_number!(deserialize_f64);

#[cfg(feature = "float_roundtrip")]
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>
where
V: de::Visitor<'de>,
{
self.single_precision = true;
let val = self.deserialize_number(visitor);
self.single_precision = false;
val
}

fn deserialize_i128<V>(self, visitor: V) -> Result<V::Value>
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<V>(self, visitor: V) -> Result<V::Value>
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<V>(self, visitor: V) -> Result<V::Value>
where
Expand Down
55 changes: 35 additions & 20 deletions src/value/de.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -1121,18 +1121,29 @@ 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<V>(self, visitor: V) -> Result<V::Value, Error>
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),
#[cfg(any(feature = "std", feature = "alloc"))]
(Err(_), Cow::Owned(s)) => visitor.visit_string(s),
let mut de = crate::Deserializer::from_str(&self.key);

match tri!(de.peek()) {
Some(b'0'..=b'9' | b'-') => {}
_ => return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0)),
}

let number = tri!(de.$using(visitor));

if tri!(de.peek()).is_some() {
return Err(Error::syntax(ErrorCode::ExpectedNumericKey, 0, 0));
}

Ok(number)
}
};
}
Expand All @@ -1147,18 +1158,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<V>(self, visitor: V) -> Result<V::Value, Error>
Expand Down
6 changes: 6 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1907,6 +1907,12 @@ fn test_integer_key() {
),
(r#"{"123 ":null}"#, "expected `\"` at line 1 column 6"),
]);

let err = from_value::<BTreeMap<i32, ()>>(json!({" 123":null})).unwrap_err();
assert_eq!(err.to_string(), "invalid value: expected key to be a number in quotes");

let err = from_value::<BTreeMap<i32, ()>>(json!({"123 ":null})).unwrap_err();
assert_eq!(err.to_string(), "invalid value: expected key to be a number in quotes");
}

#[test]
Expand Down

0 comments on commit 0c4a7f8

Please sign in to comment.