#[cfg(any(target_arch = "wasm32", test))]
pub fn parse_c_double(b: &[u8]) -> (f64, usize) {
let mut i = 0usize;
while i < b.len() && matches!(b[i], b' ' | b'\t' | b'\n' | 0x0b | 0x0c | b'\r') {
i += 1;
}
let num_start = i;
if i < b.len() && (b[i] == b'+' || b[i] == b'-') {
i += 1;
}
let mut saw_digit = false;
while i < b.len() && b[i].is_ascii_digit() {
i += 1;
saw_digit = true;
}
if i < b.len() && b[i] == b'.' {
i += 1;
while i < b.len() && b[i].is_ascii_digit() {
i += 1;
saw_digit = true;
}
}
if !saw_digit {
return (0.0, 0); }
if i < b.len() && (b[i] == b'e' || b[i] == b'E') {
let mut j = i + 1;
if j < b.len() && (b[j] == b'+' || b[j] == b'-') {
j += 1;
}
if j < b.len() && b[j].is_ascii_digit() {
i = j;
while i < b.len() && b[i].is_ascii_digit() {
i += 1;
}
}
}
let val = core::str::from_utf8(&b[num_start..i])
.ok()
.and_then(|s| s.parse::<f64>().ok())
.unwrap_or(0.0);
(val, i)
}
#[cfg(target_arch = "wasm32")]
#[no_mangle]
pub unsafe extern "C" fn strtod(
nptr: *const core::ffi::c_char,
endptr: *mut *mut core::ffi::c_char,
) -> f64 {
if nptr.is_null() {
if !endptr.is_null() {
*endptr = nptr as *mut core::ffi::c_char;
}
return 0.0;
}
let mut len = 0usize;
while *nptr.add(len) != 0 {
len += 1;
}
let bytes = core::slice::from_raw_parts(nptr as *const u8, len);
let (val, consumed) = parse_c_double(bytes);
if !endptr.is_null() {
*endptr = (nptr as *mut core::ffi::c_char).add(consumed);
}
val
}
#[cfg(test)]
mod tests {
use super::parse_c_double;
fn parse(s: &str) -> (f64, usize) {
parse_c_double(s.as_bytes())
}
#[test]
fn decimal_forms() {
assert_eq!(parse("1.5"), (1.5, 3));
assert_eq!(parse("0"), (0.0, 1));
assert_eq!(parse("42"), (42.0, 2));
assert_eq!(parse(".5"), (0.5, 2));
assert_eq!(parse("12."), (12.0, 3));
assert_eq!(parse("-3.14"), (-3.14, 5));
assert_eq!(parse("+7"), (7.0, 2));
}
#[test]
fn exponents() {
assert_eq!(parse("1e10"), (1e10, 4));
assert_eq!(parse("2.5E-3"), (2.5e-3, 6));
assert_eq!(parse("1.5e"), (1.5, 3));
assert_eq!(parse("1e+"), (1.0, 1));
}
#[test]
fn whitespace_and_trailing() {
assert_eq!(parse(" 42"), (42.0, 4)); assert_eq!(parse("3.14abc"), (3.14, 4)); }
#[test]
fn hex_prefix_stops_at_x() {
assert_eq!(parse("0x1f"), (0.0, 1));
}
#[test]
fn no_conversion() {
assert_eq!(parse(""), (0.0, 0));
assert_eq!(parse("abc"), (0.0, 0));
assert_eq!(parse(" "), (0.0, 0));
assert_eq!(parse("+"), (0.0, 0));
assert_eq!(parse(".e5"), (0.0, 0));
}
}