use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
use memchr::{memchr, memchr2, memchr3, memchr_iter};
use smallvec::SmallVec;
use std::borrow::Cow;
#[inline(always)]
fn parse_f64(s: &str) -> Result<f64, std::num::ParseFloatError> {
s.parse()
}
#[inline]
pub(crate) fn try_parse_number(trimmed: &str) -> Option<f64> {
if trimmed.is_empty() {
return None;
}
if !trimmed.bytes().any(|b| b.is_ascii_digit()) {
return None;
}
if let Ok(num) = parse_f64(trimmed) {
if num.is_finite() {
return Some(num);
}
}
if let Some(stripped) = trimmed.strip_suffix('%') {
if let Ok(num) = parse_f64(stripped) {
if num.is_finite() {
return Some(num);
}
}
}
if let Some(stripped) = trimmed.strip_suffix('\u{2030}') {
if let Ok(num) = parse_f64(stripped) {
return Some(num / 1000.0);
}
}
if let Some(stripped) = trimmed.strip_suffix('\u{2031}') {
if let Ok(num) = parse_f64(stripped) {
return Some(num / 10000.0);
}
}
let bytes = trimmed.as_bytes();
let last = bytes[bytes.len() - 1];
if matches!(last, b'p' | b's') {
if let result @ Some(_) = try_parse_basis_points(trimmed) {
return result;
}
}
if matches!(last, b'K' | b'k' | b'M' | b'm' | b'B' | b'b' | b'T' | b't') {
if let result @ Some(_) = try_parse_suffixed_number(trimmed) {
return result;
}
}
if memchr(b'/', bytes).is_some() {
if let result @ Some(_) = try_parse_fraction(trimmed) {
return result;
}
}
let check = if bytes[0] == b'-' && bytes.len() > 1 {
&bytes[1..]
} else {
bytes
};
let radix_result = if check.len() >= 2
&& check[0] == b'0'
&& matches!(check[1], b'x' | b'X' | b'b' | b'B' | b'o' | b'O')
{
try_parse_radix_number(trimmed)
} else {
None
};
if radix_result.is_some() {
return radix_result;
}
let cleaned = clean_number_string(trimmed);
parse_f64(cleaned.as_ref()).ok().filter(|n| n.is_finite())
}
#[inline]
fn try_parse_basis_points(s: &str) -> Option<f64> {
let s = s.trim();
if let Some(num_str) = s.strip_suffix("bps").or_else(|| s.strip_suffix("bp")) {
if let Ok(num) = parse_f64(num_str.trim()) {
return Some(num / 10000.0);
}
}
if let Some(num_str) = s.strip_suffix(" bps").or_else(|| s.strip_suffix(" bp")) {
if let Ok(num) = parse_f64(num_str.trim()) {
return Some(num / 10000.0);
}
}
None
}
#[inline]
fn try_parse_suffixed_number(s: &str) -> Option<f64> {
let s = s.trim();
if s.len() < 2 {
return None;
}
let last_char = s.chars().last()?;
let multiplier = match last_char {
'k' | 'K' => 1_000.0,
'm' | 'M' => 1_000_000.0,
'b' | 'B' => 1_000_000_000.0,
't' | 'T' => 1_000_000_000_000.0,
_ => return None,
};
let num_str = &s[..s.len() - 1];
if let Ok(num) = parse_f64(num_str.trim()) {
return Some(num * multiplier);
}
None
}
#[inline]
fn try_parse_fraction(s: &str) -> Option<f64> {
let s = s.trim();
if !s.contains('/') {
return None;
}
if let Some(space_pos) = s.rfind(' ') {
let whole_part = s[..space_pos].trim();
let fraction_part = s[space_pos + 1..].trim();
if let (Ok(whole), Some(frac_value)) =
(parse_f64(whole_part), parse_simple_fraction(fraction_part))
{
if whole < 0.0 {
return Some(whole - frac_value);
} else {
return Some(whole + frac_value);
}
}
}
parse_simple_fraction(s)
}
#[inline]
fn parse_simple_fraction(s: &str) -> Option<f64> {
let (num_str, den_str) = s.split_once('/')?;
let numerator: f64 = parse_f64(num_str.trim()).ok()?;
let denominator: f64 = parse_f64(den_str.trim()).ok()?;
if denominator == 0.0 {
return None;
}
Some(numerator / denominator)
}
#[inline]
fn try_parse_radix_number(s: &str) -> Option<f64> {
let s = s.trim();
let (is_negative, num_str) = if let Some(rest) = s.strip_prefix('-') {
(true, rest.trim())
} else {
(false, s)
};
let result = if let Some(hex) = num_str
.strip_prefix("0x")
.or_else(|| num_str.strip_prefix("0X"))
{
i64::from_str_radix(hex, 16).ok().map(|n| n as f64)
} else if let Some(bin) = num_str
.strip_prefix("0b")
.or_else(|| num_str.strip_prefix("0B"))
{
i64::from_str_radix(bin, 2).ok().map(|n| n as f64)
} else if let Some(oct) = num_str
.strip_prefix("0o")
.or_else(|| num_str.strip_prefix("0O"))
{
i64::from_str_radix(oct, 8).ok().map(|n| n as f64)
} else {
None
};
result.map(|n| if is_negative { -n } else { n })
}
#[inline]
fn try_parse_and_normalize_iso8601(s: &str) -> Option<String> {
let trimmed = s.trim();
let len = trimmed.len();
if len < 8 {
return None;
}
let bytes = trimmed.as_bytes();
let first_byte = bytes[0];
if !first_byte.is_ascii_digit() {
return None;
}
if len == 8 && matches!(first_byte, b'1' | b'2') && bytes.iter().all(|b| b.is_ascii_digit()) {
return try_parse_compact_date(trimmed);
}
if len >= 15 && bytes[8] == b'T' {
if let Some(result) = try_parse_compact_datetime(trimmed) {
return Some(result);
}
}
if len == 8 && bytes[4] == b'-' {
return try_parse_ordinal_date(trimmed);
}
if len >= 8 && bytes[4] == b'-' && bytes[5] == b'W' {
return try_parse_week_date(trimmed);
}
if len >= 10 {
let sep = bytes[4];
if (sep == b'-' || sep == b'/' || sep == b'.') && bytes[7] == sep {
return try_parse_standard_date(trimmed, sep);
}
}
None
}
#[inline]
fn try_parse_compact_date(s: &str) -> Option<String> {
NaiveDate::parse_from_str(s, "%Y%m%d")
.ok()
.map(|d| d.format("%Y-%m-%d").to_string())
}
#[inline]
fn try_parse_compact_datetime(s: &str) -> Option<String> {
let bytes = s.as_bytes();
let len = s.len();
if len == 16 && bytes[15] == b'Z' {
if let Ok(naive) = NaiveDateTime::parse_from_str(&s[..15], "%Y%m%dT%H%M%S") {
let utc = naive.and_utc();
return Some(utc.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
}
if len >= 19 && (bytes[15] == b'+' || bytes[15] == b'-') {
let date_part = &s[0..8];
let time_part = &s[9..15];
let offset_part = &s[15..];
let formatted_offset = if offset_part.len() == 5 {
format!("{}:{}", &offset_part[..3], &offset_part[3..])
} else {
offset_part.to_string()
};
let iso_str = format!(
"{}-{}-{}T{}:{}:{}{}",
&date_part[0..4],
&date_part[4..6],
&date_part[6..8],
&time_part[0..2],
&time_part[2..4],
&time_part[4..6],
formatted_offset
);
if let Ok(dt) = DateTime::parse_from_rfc3339(&iso_str) {
let utc: DateTime<Utc> = dt.into();
return Some(utc.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
}
if len == 15 {
if let Ok(naive) = NaiveDateTime::parse_from_str(s, "%Y%m%dT%H%M%S") {
let utc = naive.and_utc();
return Some(utc.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
}
None
}
#[inline]
fn try_parse_ordinal_date(s: &str) -> Option<String> {
NaiveDate::parse_from_str(s, "%Y-%j")
.ok()
.map(|d| d.format("%Y-%m-%d").to_string())
}
#[inline]
fn try_parse_week_date(s: &str) -> Option<String> {
let formats = ["%G-W%V-%u", "%G-W%V"];
for fmt in &formats {
if let Ok(d) = NaiveDate::parse_from_str(s, fmt) {
return Some(d.format("%Y-%m-%d").to_string());
}
}
None
}
#[inline]
fn try_parse_standard_date(s: &str, sep: u8) -> Option<String> {
let bytes = s.as_bytes();
let len = s.len();
let normalized: Cow<'_, str> = if sep != b'-' {
let sep_char = sep as char;
Cow::Owned(s.replace(sep_char, "-"))
} else {
Cow::Borrowed(s)
};
if len >= 10
&& (!bytes[0..4].iter().all(|b| b.is_ascii_digit())
|| !bytes[5..7].iter().all(|b| b.is_ascii_digit())
|| !bytes[8..10].iter().all(|b| b.is_ascii_digit()))
{
return None;
}
if len == 10 {
return NaiveDate::parse_from_str(&normalized, "%Y-%m-%d")
.ok()
.map(|_| normalized.into_owned());
}
if len < 11 {
return None;
}
let datetime_sep = bytes[10];
if datetime_sep != b'T' && datetime_sep != b' ' {
return None;
}
let normalized = if datetime_sep == b' ' {
let mut s = normalized.into_owned();
unsafe {
s.as_bytes_mut()[10] = b'T';
}
Cow::Owned(s)
} else {
normalized
};
if let Ok(dt) = DateTime::parse_from_rfc3339(&normalized) {
let utc: DateTime<Utc> = dt.into();
return Some(utc.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
if let Some(result) = try_parse_with_offset_variants(&normalized) {
return Some(result);
}
let time_part = normalized.strip_suffix('Z').unwrap_or(normalized.as_ref());
let naive_formats = [
"%Y-%m-%dT%H:%M:%S%.f", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M", "%Y-%m-%dT%H", ];
for fmt in &naive_formats {
if let Ok(naive_dt) = NaiveDateTime::parse_from_str(time_part, fmt) {
let utc_dt = naive_dt.and_utc();
return Some(utc_dt.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
}
None
}
#[inline]
fn try_parse_with_offset_variants(s: &str) -> Option<String> {
let len = s.len();
if len < 14 {
return None;
}
for pos in [16, 19, 22, 23, 26] {
if pos >= len {
continue;
}
let byte = s.as_bytes()[pos];
if byte == b'+' || byte == b'-' {
let offset_part = &s[pos..];
let time_part = &s[..pos];
if let Some(normalized_offset) = normalize_offset(offset_part) {
let full = format!("{}{}", time_part, normalized_offset);
if let Ok(dt) = DateTime::parse_from_rfc3339(&full) {
let utc: DateTime<Utc> = dt.into();
return Some(utc.format("%Y-%m-%dT%H:%M:%SZ").to_string());
}
}
}
}
None
}
#[inline]
fn normalize_offset(offset: &str) -> Option<String> {
let bytes = offset.as_bytes();
let len = bytes.len();
if len < 2 {
return None;
}
let sign = bytes[0];
if sign != b'+' && sign != b'-' {
return None;
}
let sign_char = sign as char;
let rest = &offset[1..];
match rest.len() {
2 if rest.as_bytes().iter().all(|b| b.is_ascii_digit()) => {
Some(format!("{}{}:00", sign_char, rest))
}
4 if rest.as_bytes().iter().all(|b| b.is_ascii_digit()) => {
Some(format!("{}{}:{}", sign_char, &rest[..2], &rest[2..]))
}
5 if rest.as_bytes()[2] == b':' => Some(offset.to_string()),
_ => None,
}
}
#[inline(always)]
fn could_be_date(s: &str) -> bool {
let len = s.len();
if len < 8 {
return false;
}
let bytes = s.as_bytes();
if !bytes[0..4].iter().all(|b| b.is_ascii_digit()) {
return false;
}
let fifth = bytes[4];
match fifth {
b'0'..=b'9' => len == 8 || (len >= 15 && bytes[8] == b'T'),
b'-' | b'/' | b'.' => len >= 8,
_ => false,
}
}
#[inline(always)] pub(crate) fn clean_number_string(s: &str) -> Cow<'_, str> {
let trimmed = s.trim();
if trimmed.is_empty() {
return Cow::Borrowed("");
}
let is_clean = trimmed.bytes().all(|b| matches!(b, b'0'..=b'9' | b'.' | b'-' | b'+' | b'e' | b'E'))
&& !trimmed.ends_with('-') && !trimmed.starts_with('+'); if is_clean {
return Cow::Borrowed(trimmed);
}
let is_negative = trimmed.starts_with('-')
|| trimmed.starts_with('(') && trimmed.ends_with(')') || trimmed.starts_with('[') && trimmed.ends_with(']') || trimmed.ends_with('-');
let working_str = if is_negative {
if let Some(s) = trimmed.strip_prefix('(').and_then(|s| s.strip_suffix(')')) {
s
} else if let Some(s) = trimmed.strip_prefix('[').and_then(|s| s.strip_suffix(']')) {
s
} else if let Some(s) = trimmed.strip_suffix('-') {
s
} else {
&trimmed[1..]
}
} else {
trimmed
}
.trim();
let working_str = working_str.strip_prefix('+').unwrap_or(working_str).trim();
let mut without_currency = working_str;
if without_currency.len() > 2 {
if let Some(rest) = without_currency
.strip_prefix("R$")
.or_else(|| without_currency.strip_prefix("A$"))
.or_else(|| without_currency.strip_prefix("C$"))
.or_else(|| without_currency.strip_prefix("AU$"))
.or_else(|| without_currency.strip_prefix("CA$"))
.or_else(|| without_currency.strip_prefix("US$"))
.or_else(|| without_currency.strip_prefix("Fr"))
.or_else(|| without_currency.strip_prefix("kr"))
.or_else(|| without_currency.strip_prefix("z\u{142}"))
.or_else(|| without_currency.strip_prefix("K\u{10d}"))
{
without_currency = rest.trim();
}
}
without_currency = without_currency
.trim_start_matches(
&[
'$', '\u{20ac}', '\u{00a3}', '\u{00a5}', '\u{20b9}', '\u{20bd}', '\u{20a9}',
'\u{20ba}',
][..],
)
.trim();
if without_currency.len() > 4 {
let first_three = &without_currency[..3];
if first_three.bytes().all(|b| b.is_ascii_uppercase()) {
let potential_code = &without_currency[3..];
if potential_code.starts_with(' ') {
without_currency = potential_code.trim();
}
}
}
without_currency = without_currency
.trim_end_matches(
&[
'$', '\u{20ac}', '\u{00a3}', '\u{00a5}', '\u{20b9}', '\u{20bd}', '\u{20a9}',
'\u{20ba}',
][..],
)
.trim_end_matches("CR") .trim_end_matches("DR") .trim_end_matches("cr")
.trim_end_matches("dr")
.trim();
{
let b = without_currency.as_bytes();
if memchr3(b',', b'.', b' ', b).is_none() && memchr2(b'\'', b'_', b).is_none() {
return if is_negative {
Cow::Owned(format!("-{}", without_currency))
} else {
Cow::Owned(without_currency.to_string())
};
}
}
let bytes = without_currency.as_bytes();
let (last_comma_pos, comma_count) =
memchr_iter(b',', bytes).fold((None, 0usize), |(_, c), pos| (Some(pos), c + 1));
let (last_dot_pos, dot_count) =
memchr_iter(b'.', bytes).fold((None, 0usize), |(_, c), pos| (Some(pos), c + 1));
let mut buffer: SmallVec<[u8; 64]> = SmallVec::new();
if is_negative {
buffer.push(b'-');
}
match (last_comma_pos, last_dot_pos, comma_count, dot_count) {
(Some(comma_pos), Some(dot_pos), _, _) => {
if dot_pos > comma_pos {
extend_skipping_4(&mut buffer, bytes, b',', b' ', b'\'', b'_');
} else {
extend_skipping_4(&mut buffer, bytes, b'.', b' ', b'\'', b'_');
for b in buffer.iter_mut() {
if *b == b',' {
*b = b'.';
}
}
}
}
(Some(_), None, 1, 0) => {
extend_skipping_3(&mut buffer, bytes, b' ', b'\'', b'_');
for b in buffer.iter_mut() {
if *b == b',' {
*b = b'.';
}
}
}
(Some(_), None, _, 0) => {
let segments: SmallVec<[&str; 8]> = without_currency.split(',').collect();
let is_us_thousands = segments.len() > 1
&& segments[1..]
.iter()
.all(|seg| seg.len() == 3 && seg.bytes().all(|b| b.is_ascii_digit()));
let is_indian_format = segments.len() >= 2 && {
let last_seg = segments
.last()
.expect("segments non-empty: len >= 2 checked above");
let middle_segs = &segments[1..segments.len() - 1];
let last_valid = (last_seg.len() == 3 || last_seg.len() == 2)
&& last_seg.bytes().all(|b| b.is_ascii_digit());
let middle_valid = middle_segs
.iter()
.all(|seg| seg.len() == 2 && seg.bytes().all(|b| b.is_ascii_digit()));
let first_valid = !segments[0].is_empty()
&& segments[0].len() <= 3
&& segments[0].bytes().all(|b| b.is_ascii_digit());
first_valid && middle_valid && last_valid
};
if is_us_thousands || is_indian_format {
extend_skipping_4(&mut buffer, bytes, b',', b' ', b'\'', b'_');
} else {
return Cow::Owned(without_currency.to_string());
}
}
(None, Some(_), 0, count) if count > 1 => {
let segments: SmallVec<[&str; 8]> = without_currency.split('.').collect();
let is_valid_thousands = segments.len() > 1
&& segments[1..]
.iter()
.all(|seg| seg.len() == 3 && seg.bytes().all(|b| b.is_ascii_digit()));
if is_valid_thousands {
extend_skipping_4(&mut buffer, bytes, b'.', b' ', b'\'', b'_');
} else {
return Cow::Owned(without_currency.to_string());
}
}
_ => {
extend_skipping_3(&mut buffer, bytes, b' ', b'\'', b'_');
}
}
Cow::Owned(unsafe { String::from_utf8_unchecked(buffer.into_vec()) })
}
#[inline(always)]
fn try_parse_bool(s: &str) -> Option<bool> {
match s {
"true" | "TRUE" | "True" | "yes" | "YES" | "Yes" | "y" | "Y" | "on" | "ON" | "On" => {
Some(true)
}
"false" | "FALSE" | "False" | "no" | "NO" | "No" | "n" | "N" | "off" | "OFF" | "Off" => {
Some(false)
}
_ => None,
}
}
#[inline(always)]
fn is_null_string(s: &str) -> bool {
matches!(
s,
"null"
| "NULL"
| "Null"
| "nil"
| "NIL"
| "Nil"
| "none"
| "NONE"
| "None"
| "N/A"
| "n/a"
| "NA"
| "na"
)
}
#[inline]
pub(crate) fn try_convert_string_to_json_bytes(s: &str) -> Option<Cow<'static, str>> {
if s.is_empty() {
return None;
}
let trimmed = s.trim();
if trimmed.is_empty() {
return None;
}
let first_byte = trimmed.as_bytes()[0];
match first_byte {
b'n' | b'N' => {
if is_null_string(trimmed) {
return Some(Cow::Borrowed("null"));
}
if let Some(b) = try_parse_bool(trimmed) {
return Some(Cow::Borrowed(if b { "true" } else { "false" }));
}
None
}
b't' | b'T' | b'f' | b'F' | b'y' | b'Y' | b'o' | b'O' => {
try_parse_bool(trimmed).map(|b| Cow::Borrowed(if b { "true" } else { "false" }))
}
b'0'..=b'9' | b'-' | b'+' | b'.' | b'$' | b'(' | b'[' => {
if first_byte == b'0' || first_byte == b'1' {
if let Some(b) = try_parse_bool(trimmed) {
return Some(Cow::Borrowed(if b { "true" } else { "false" }));
}
}
if could_be_date(trimmed) {
if let Some(normalized_date) = try_parse_and_normalize_iso8601(trimmed) {
if normalized_date != trimmed {
return Some(Cow::Owned(format!("\"{}\"", normalized_date)));
}
return None; }
}
if let Some(num) = try_parse_number(trimmed) {
return f64_to_json_bytes(num);
}
None
}
b'A'..=b'Z' | b'\xc2'..=b'\xf4' => {
if let Some(num) = try_parse_number(trimmed) {
return f64_to_json_bytes(num);
}
None
}
_ => None,
}
}
#[inline]
fn f64_to_json_bytes(num: f64) -> Option<Cow<'static, str>> {
if num.is_finite() && num.fract() == 0.0 {
if num >= i64::MIN as f64 && num <= i64::MAX as f64 {
return Some(Cow::Owned((num as i64).to_string()));
}
if num >= 0.0 && num <= u64::MAX as f64 {
return Some(Cow::Owned((num as u64).to_string()));
}
}
serde_json::Number::from_f64(num).map(|n| Cow::Owned(n.to_string()))
}
#[inline]
pub(crate) fn extend_skipping_4(
dst: &mut SmallVec<[u8; 64]>,
src: &[u8],
s1: u8,
s2: u8,
s3: u8,
s4: u8,
) {
let mut start = 0usize;
while start < src.len() {
let rest = &src[start..];
let next = {
let a = memchr3(s1, s2, s3, rest);
let b = memchr(s4, rest);
match (a, b) {
(Some(x), Some(y)) => Some(x.min(y) + start),
(Some(x), None) | (None, Some(x)) => Some(x + start),
(None, None) => None,
}
};
match next {
Some(sep_pos) => {
dst.extend_from_slice(&src[start..sep_pos]);
start = sep_pos + 1;
}
None => {
dst.extend_from_slice(&src[start..]);
break;
}
}
}
}
#[inline]
pub(crate) fn extend_skipping_3(dst: &mut SmallVec<[u8; 64]>, src: &[u8], s1: u8, s2: u8, s3: u8) {
let mut start = 0usize;
while start < src.len() {
let rest = &src[start..];
match memchr3(s1, s2, s3, rest) {
Some(pos) => {
dst.extend_from_slice(&src[start..start + pos]);
start += pos + 1;
}
None => {
dst.extend_from_slice(&src[start..]);
break;
}
}
}
}