From de76285111063b9f704a245b6bc2b161a3540356 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 16:19:40 +0800 Subject: [PATCH 01/13] Try parse millisecond unix timestamp based on #561 --- src/format/parse.rs | 78 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index 5a3a702734..afa91eee51 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -279,6 +279,24 @@ where parse_internal(parsed, s, items) } +fn get_fixed_item_len<'a, B>(item: Option<&B>) -> Option +where + B: Borrow>, +{ + use super::Fixed::*; + + item.map(|i| { + println!("{:?}", i.borrow()); + match *i.borrow() { + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot })) => 3, + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot })) => 6, + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot })) => 9, + // Item::Literal(prefix) => prefix.len(), + _ => 0, + } + }) +} + fn parse_internal<'a, 'b, I, B>( parsed: &mut Parsed, mut s: &'b str, @@ -300,7 +318,17 @@ where }}; } - for item in items { + // convert items to a pair (current, Option(next_item)) + // so we can have information about the next item + let items: Vec = items.collect::>(); + let last_item = items.last(); + let mut items: Vec<(&B, Option<&B>)> = + items.windows(2).map(|arr| (&arr[0], Some(&arr[1]))).collect::>(); + if let Some(last_item) = last_item { + items.push((last_item, None)); + } + + for (item, next_item) in items { match *item.borrow() { Item::Literal(prefix) => { if s.len() < prefix.len() { @@ -357,25 +385,44 @@ where Second => (2, false, Parsed::set_second), Nanosecond => (9, false, Parsed::set_nanosecond), Timestamp => (usize::MAX, false, Parsed::set_timestamp), - // for the future expansion Internal(ref int) => match int._dummy {}, }; - s = s.trim_start(); - let v = if signed { - if s.starts_with('-') { - let v = try_consume!(scan::number(&s[1..], 1, usize::MAX)); - 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? - } else if s.starts_with('+') { - try_consume!(scan::number(&s[1..], 1, usize::MAX)) + + let (neg, start_idx, min_width, mut max_width) = { + if signed && s.starts_with('-') { + (true, 1, 1, usize::MAX) + } else if signed && s.starts_with('+') { + (false, 1, 1, usize::MAX) } else { - // if there is no explicit sign, we respect the original `width` - try_consume!(scan::number(s, 1, width)) + (false, 0, 1, width) } - } else { - try_consume!(scan::number(s, 1, width)) }; + let substr = &s[start_idx..]; + + if max_width == usize::MAX { + let next_size = get_fixed_item_len(next_item).unwrap_or(0); + let numeric_bytes_available = + substr.as_bytes().iter().take_while(|&&c| b'0' <= c && c <= b'9').count(); + println!( + "{numeric_bytes_available} {next_size} s: {}, spec: {:?}", + &s[start_idx..], + spec + ); + max_width = numeric_bytes_available - next_size; + } + if substr.len() < min_width { + return Err(TOO_SHORT); + } + if max_width == 0 { + return Err(INVALID); + } + // println!("min_width: {:?}, max_width: {:?}", min_width, max_width); + let mut v = try_consume!(scan::number(substr, min_width, max_width)); + if neg { + v = 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? + } set(parsed, v)?; } @@ -1537,6 +1584,11 @@ mod tests { &[num(Timestamp), Literal("."), num(Nanosecond)], parsed!(nanosecond: 56_789, timestamp: 12_345_678_901_234), ); + check( + "12345678901234567", + &[num(Timestamp), internal_fixed(Nanosecond3NoDot)], + parsed!(nanosecond: 567_000_000, timestamp: 12_345_678_901_234), + ); check( "12345678901234.56789", &[num(Timestamp), fixed(Fixed::Nanosecond)], From 6c1007bada5a3fa4acaf77c3664eb32c92e5d186 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 16:32:58 +0800 Subject: [PATCH 02/13] Move assert into later parameter check --- src/format/parse.rs | 6 ------ src/format/scan.rs | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index afa91eee51..f46c4a8502 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -412,12 +412,6 @@ where ); max_width = numeric_bytes_available - next_size; } - if substr.len() < min_width { - return Err(TOO_SHORT); - } - if max_width == 0 { - return Err(INVALID); - } // println!("min_width: {:?}, max_width: {:?}", min_width, max_width); let mut v = try_consume!(scan::number(substr, min_width, max_width)); if neg { diff --git a/src/format/scan.rs b/src/format/scan.rs index 1ab87b9dd5..abb1a15ee2 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -15,8 +15,6 @@ use crate::Weekday; /// Any number that does not fit in `i64` is an error. #[inline] pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64)> { - assert!(min <= max); - // We are only interested in ascii numbers, so we can work with the `str` as bytes. We stop on // the first non-numeric byte, which may be another ascii character or beginning of multi-byte // UTF-8 character. @@ -25,6 +23,10 @@ pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64) return Err(TOO_SHORT); } + if !(min <= max) { + return Err(INVALID); + } + let mut n = 0i64; for (i, c) in bytes.iter().take(max).cloned().enumerate() { // cloned() = copied() From a7257e5d5c63d6e56f35fad5815c916a76ee7991 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 17:00:59 +0800 Subject: [PATCH 03/13] Allow numeric literal after year --- src/format/parse.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index f46c4a8502..6046a70aa3 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -279,21 +279,20 @@ where parse_internal(parsed, s, items) } -fn get_fixed_item_len<'a, B>(item: Option<&B>) -> Option +fn get_numeric_item_len<'a, B>(item: Option<&B>) -> Option where B: Borrow>, { use super::Fixed::*; - item.map(|i| { - println!("{:?}", i.borrow()); - match *i.borrow() { - Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot })) => 3, - Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot })) => 6, - Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot })) => 9, - // Item::Literal(prefix) => prefix.len(), - _ => 0, + item.map(|i| match *i.borrow() { + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond3NoDot })) => 3, + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot })) => 6, + Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot })) => 9, + Item::Literal(prefix) => { + prefix.as_bytes().iter().take_while(|&&c| b'0' <= c && c <= b'9').count() } + _ => 0, }) } @@ -402,17 +401,11 @@ where let substr = &s[start_idx..]; if max_width == usize::MAX { - let next_size = get_fixed_item_len(next_item).unwrap_or(0); + let next_size = get_numeric_item_len(next_item).unwrap_or(0); let numeric_bytes_available = substr.as_bytes().iter().take_while(|&&c| b'0' <= c && c <= b'9').count(); - println!( - "{numeric_bytes_available} {next_size} s: {}, spec: {:?}", - &s[start_idx..], - spec - ); max_width = numeric_bytes_available - next_size; } - // println!("min_width: {:?}, max_width: {:?}", min_width, max_width); let mut v = try_consume!(scan::number(substr, min_width, max_width)); if neg { v = 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? @@ -1523,6 +1516,15 @@ mod tests { second: 5, nanosecond: 567000000 ), ); + check( + "20151230204", + &[ + num(Year), Literal("123"), num(Month), num(Day) + ], + parsed!( + year: 2015, month: 2, day: 4 + ), + ); check( "Mon, 10 Jun 2013 09:32:37 GMT", &[ @@ -1578,6 +1580,11 @@ mod tests { &[num(Timestamp), Literal("."), num(Nanosecond)], parsed!(nanosecond: 56_789, timestamp: 12_345_678_901_234), ); + check( + "12345678901234111", + &[num(Timestamp), Literal("111")], + parsed!(timestamp: 12_345_678_901_234), + ); check( "12345678901234567", &[num(Timestamp), internal_fixed(Nanosecond3NoDot)], From ead3dca899315b0233149d0d075981ef6d61000a Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 17:03:12 +0800 Subject: [PATCH 04/13] Add comment --- src/format/parse.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/format/parse.rs b/src/format/parse.rs index 6046a70aa3..6230c43123 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -400,6 +400,8 @@ where }; let substr = &s[start_idx..]; + // If the width is not fixed, we need to determine the width from the next item. + // Try to consume the number in the non-greedy way. if max_width == usize::MAX { let next_size = get_numeric_item_len(next_item).unwrap_or(0); let numeric_bytes_available = From c32e7ba0220e8bc9e2aef7c7c374edfd2c13a80f Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 17:17:58 +0800 Subject: [PATCH 05/13] fix clippy error --- src/format/parse.rs | 9 ++++++--- src/format/scan.rs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index 6230c43123..ea3732b984 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -290,7 +290,7 @@ where Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot })) => 6, Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot })) => 9, Item::Literal(prefix) => { - prefix.as_bytes().iter().take_while(|&&c| b'0' <= c && c <= b'9').count() + prefix.as_bytes().iter().take_while(|&&c| (b'0'..=b'9').contains(&c)).count() } _ => 0, }) @@ -404,8 +404,11 @@ where // Try to consume the number in the non-greedy way. if max_width == usize::MAX { let next_size = get_numeric_item_len(next_item).unwrap_or(0); - let numeric_bytes_available = - substr.as_bytes().iter().take_while(|&&c| b'0' <= c && c <= b'9').count(); + let numeric_bytes_available = substr + .as_bytes() + .iter() + .take_while(|&&c| (b'0'..=b'9').contains(&c)) + .count(); max_width = numeric_bytes_available - next_size; } let mut v = try_consume!(scan::number(substr, min_width, max_width)); diff --git a/src/format/scan.rs b/src/format/scan.rs index abb1a15ee2..3e6cfe4b0b 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -23,7 +23,7 @@ pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64) return Err(TOO_SHORT); } - if !(min <= max) { + if (min > max) { return Err(INVALID); } From 1ef93e45c1f821917c8c557ece57fa0f25d7a84f Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 17:22:16 +0800 Subject: [PATCH 06/13] Add microseond and nanosecond unix epoch test --- src/format/parse.rs | 15 +++++++++++++++ src/format/scan.rs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index ea3732b984..a749141e6a 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -1595,6 +1595,21 @@ mod tests { &[num(Timestamp), internal_fixed(Nanosecond3NoDot)], parsed!(nanosecond: 567_000_000, timestamp: 12_345_678_901_234), ); + check( + "12345678901234567", + &[num(Timestamp), internal_fixed(Nanosecond3NoDot)], + parsed!(nanosecond: 567_000_000, timestamp: 12_345_678_901_234), + ); + check( + "12345678901234567890", + &[num(Timestamp), internal_fixed(Nanosecond6NoDot)], + parsed!(nanosecond: 567_890_000, timestamp: 12_345_678_901_234), + ); + check( + "12345678901234567890123", + &[num(Timestamp), internal_fixed(Nanosecond9NoDot)], + parsed!(nanosecond: 567_890_123, timestamp: 12_345_678_901_234), + ); check( "12345678901234.56789", &[num(Timestamp), fixed(Fixed::Nanosecond)], diff --git a/src/format/scan.rs b/src/format/scan.rs index 3e6cfe4b0b..3e67808479 100644 --- a/src/format/scan.rs +++ b/src/format/scan.rs @@ -23,7 +23,7 @@ pub(super) fn number(s: &str, min: usize, max: usize) -> ParseResult<(&str, i64) return Err(TOO_SHORT); } - if (min > max) { + if min > max { return Err(INVALID); } From 516c4f8f472c166f79e48ab5ff4f75f47f09a4cb Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 17:27:42 +0800 Subject: [PATCH 07/13] Fix clippy error --- src/format/parse.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index a749141e6a..6fac97b6f3 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -290,7 +290,7 @@ where Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond6NoDot })) => 6, Item::Fixed(Internal(InternalFixed { val: InternalInternal::Nanosecond9NoDot })) => 9, Item::Literal(prefix) => { - prefix.as_bytes().iter().take_while(|&&c| (b'0'..=b'9').contains(&c)).count() + prefix.as_bytes().iter().take_while(|&&c| c.is_ascii_digit()).count() } _ => 0, }) @@ -404,11 +404,8 @@ where // Try to consume the number in the non-greedy way. if max_width == usize::MAX { let next_size = get_numeric_item_len(next_item).unwrap_or(0); - let numeric_bytes_available = substr - .as_bytes() - .iter() - .take_while(|&&c| (b'0'..=b'9').contains(&c)) - .count(); + let numeric_bytes_available = + substr.as_bytes().iter().take_while(|&&c| c.is_ascii_digit()).count(); max_width = numeric_bytes_available - next_size; } let mut v = try_consume!(scan::number(substr, min_width, max_width)); From ea723d6548ac8944a21f6f038eb5c0fd511e8507 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 18:38:49 +0800 Subject: [PATCH 08/13] adopt peak to return next optional item --- src/format/parse.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index 6fac97b6f3..be088fa75e 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -317,17 +317,10 @@ where }}; } - // convert items to a pair (current, Option(next_item)) - // so we can have information about the next item - let items: Vec = items.collect::>(); - let last_item = items.last(); - let mut items: Vec<(&B, Option<&B>)> = - items.windows(2).map(|arr| (&arr[0], Some(&arr[1]))).collect::>(); - if let Some(last_item) = last_item { - items.push((last_item, None)); - } + let mut items_iter = items.peekable(); - for (item, next_item) in items { + while let Some(item) = items_iter.next() { + let next_item = items_iter.peek(); match *item.borrow() { Item::Literal(prefix) => { if s.len() < prefix.len() { From 7a111f689171ebea9edf84cf7a928dceb9037451 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 21:23:04 +0800 Subject: [PATCH 09/13] add more testcases --- src/format/parse.rs | 4 ++++ src/naive/datetime/tests.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/format/parse.rs b/src/format/parse.rs index be088fa75e..b882b16b14 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -794,6 +794,7 @@ mod tests { &[num(Year), Space(" "), Literal("x"), Space(" "), Literal("1235")], parsed!(year: 1234), ); + check("12341235", &[num(Year), Literal("1235")], parsed!(year: 1234)); // signed numeric check("-42", &[num(Year)], parsed!(year: -42)); @@ -806,9 +807,12 @@ mod tests { check(" -42195", &[num(Year)], parsed!(year: -42195)); check(" +42195", &[num(Year)], parsed!(year: 42195)); check(" -42195", &[num(Year)], parsed!(year: -42195)); + check(" -42195123", &[num(Year), Literal("123")], parsed!(year: -42195)); check(" +42195", &[num(Year)], parsed!(year: 42195)); + check(" +42195123", &[num(Year), Literal("123")], parsed!(year: 42195)); check("-42195 ", &[num(Year)], Err(TOO_LONG)); check("+42195 ", &[num(Year)], Err(TOO_LONG)); + check("+42195123 ", &[num(Year), Literal("123")], Err(TOO_LONG)); check(" - 42", &[num(Year)], Err(INVALID)); check(" + 42", &[num(Year)], Err(INVALID)); check(" -42195", &[Space(" "), num(Year)], parsed!(year: -42195)); diff --git a/src/naive/datetime/tests.rs b/src/naive/datetime/tests.rs index 161245813d..d48bb22158 100644 --- a/src/naive/datetime/tests.rs +++ b/src/naive/datetime/tests.rs @@ -191,6 +191,10 @@ fn test_datetime_parse_from_str() { NaiveDateTime::parse_from_str("2014-5-7T12:34:56+09:30", "%Y-%m-%dT%H:%M:%S%z"), Ok(ymdhms(2014, 5, 7, 12, 34, 56)) ); // ignore offset + assert_eq!( + NaiveDateTime::parse_from_str("2014123-5-7T12:34:56+09:30", "%Y123-%m-%dT%H:%M:%S%z"), + Ok(ymdhms(2014, 5, 7, 12, 34, 56)) + ); // ignore offset assert_eq!( NaiveDateTime::parse_from_str("2015-W06-1 000000", "%G-W%V-%u%H%M%S"), Ok(ymdhms(2015, 2, 2, 0, 0, 0)) @@ -218,14 +222,26 @@ fn test_datetime_parse_from_str() { NaiveDateTime::parse_from_str("1441497364.649", "%s%.3f"), Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) ); + assert_eq!( + NaiveDateTime::parse_from_str("1441497364649", "%s%3f"), + Ok(ymdhmsn(2015, 9, 5, 23, 56, 4, 649000000)) + ); assert_eq!( NaiveDateTime::parse_from_str("1497854303.087654", "%s%.6f"), Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) ); + assert_eq!( + NaiveDateTime::parse_from_str("1497854303087654", "%s%6f"), + Ok(ymdhmsn(2017, 6, 19, 6, 38, 23, 87654000)) + ); assert_eq!( NaiveDateTime::parse_from_str("1437742189.918273645", "%s%.9f"), Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) ); + assert_eq!( + NaiveDateTime::parse_from_str("1437742189918273645", "%s%9f"), + Ok(ymdhmsn(2015, 7, 24, 12, 49, 49, 918273645)) + ); } #[test] From ced88b2a9ff537a65004f89dc050e5fceb547967 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Sat, 11 Jan 2025 21:26:51 +0800 Subject: [PATCH 10/13] Restore blank line --- src/format/parse.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/format/parse.rs b/src/format/parse.rs index b882b16b14..b02411c019 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -377,9 +377,11 @@ where Second => (2, false, Parsed::set_second), Nanosecond => (9, false, Parsed::set_nanosecond), Timestamp => (usize::MAX, false, Parsed::set_timestamp), + // for the future expansion Internal(ref int) => match int._dummy {}, }; + s = s.trim_start(); let (neg, start_idx, min_width, mut max_width) = { From bca2088235ec4ed520732ffa5eb6d5cf3a526246 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Tue, 28 Jan 2025 00:27:59 +0800 Subject: [PATCH 11/13] refactor implementation --- src/format/parse.rs | 56 ++++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index b02411c019..b16a7fda84 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -296,6 +296,15 @@ where }) } +fn recalculate_numeric_item_width<'a, B>(s: &str, next_item: Option<&B>) -> usize +where + B: Borrow>, +{ + let next_width = get_numeric_item_len(next_item).unwrap_or(0); + let numeric_bytes_available = s.as_bytes().iter().take_while(|&&c| c.is_ascii_digit()).count(); + numeric_bytes_available - next_width +} + fn parse_internal<'a, 'b, I, B>( parsed: &mut Parsed, mut s: &'b str, @@ -356,7 +365,16 @@ where use super::Numeric::*; type Setter = fn(&mut Parsed, i64) -> ParseResult<()>; - let (width, signed, set): (usize, bool, Setter) = match *spec { + s = s.trim_start(); + let mut substr = &s[..]; + let negative = s.starts_with('-'); + let positive = s.starts_with('+'); + let starts_with_sign = negative || positive; + if starts_with_sign { + substr = &s[1..]; + } + + let (mut width, signed, set): (usize, bool, Setter) = match *spec { Year => (4, true, Parsed::set_year), YearDiv100 => (2, false, Parsed::set_year_div_100), YearMod100 => (2, false, Parsed::set_year_mod_100), @@ -376,37 +394,23 @@ where Minute => (2, false, Parsed::set_minute), Second => (2, false, Parsed::set_second), Nanosecond => (9, false, Parsed::set_nanosecond), - Timestamp => (usize::MAX, false, Parsed::set_timestamp), + Timestamp => ( + recalculate_numeric_item_width(substr, next_item), + false, + Parsed::set_timestamp, + ), // for the future expansion Internal(ref int) => match int._dummy {}, }; - s = s.trim_start(); - - let (neg, start_idx, min_width, mut max_width) = { - if signed && s.starts_with('-') { - (true, 1, 1, usize::MAX) - } else if signed && s.starts_with('+') { - (false, 1, 1, usize::MAX) - } else { - (false, 0, 1, width) - } - }; - let substr = &s[start_idx..]; - - // If the width is not fixed, we need to determine the width from the next item. - // Try to consume the number in the non-greedy way. - if max_width == usize::MAX { - let next_size = get_numeric_item_len(next_item).unwrap_or(0); - let numeric_bytes_available = - substr.as_bytes().iter().take_while(|&&c| c.is_ascii_digit()).count(); - max_width = numeric_bytes_available - next_size; - } - let mut v = try_consume!(scan::number(substr, min_width, max_width)); - if neg { - v = 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? + if starts_with_sign && signed { + width = recalculate_numeric_item_width(substr, next_item); } + + let v = try_consume!(scan::number(substr, 1, width)); + let v = if negative { 0i64.checked_sub(v).ok_or(OUT_OF_RANGE)? } else { v }; + set(parsed, v)?; } From 56d0be9960fa36ec8fe3b497eb4c92f338769325 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Tue, 28 Jan 2025 00:29:20 +0800 Subject: [PATCH 12/13] fix clippy --- src/format/parse.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format/parse.rs b/src/format/parse.rs index b16a7fda84..fe6d98fcb7 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -366,7 +366,7 @@ where type Setter = fn(&mut Parsed, i64) -> ParseResult<()>; s = s.trim_start(); - let mut substr = &s[..]; + let mut substr = s; let negative = s.starts_with('-'); let positive = s.starts_with('+'); let starts_with_sign = negative || positive; From 1922a29b9f63c7746f6c0ab3b82abcae6eb8dc65 Mon Sep 17 00:00:00 2001 From: "chenyee.cy.huang" Date: Tue, 28 Jan 2025 00:54:57 +0800 Subject: [PATCH 13/13] handle unsigned number --- src/format/parse.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/format/parse.rs b/src/format/parse.rs index fe6d98fcb7..2c1089004c 100644 --- a/src/format/parse.rs +++ b/src/format/parse.rs @@ -406,6 +406,8 @@ where if starts_with_sign && signed { width = recalculate_numeric_item_width(substr, next_item); + } else if starts_with_sign { + return Err(INVALID); } let v = try_consume!(scan::number(substr, 1, width));