///|
/// Read Double.parseDouble syntax after Smithy's '.', 'e' or 'E' dispatch.
/// Java trims only U+0000..U+0020 and accepts one floating type suffix.
fn java_double(text : String) -> Double? {
let mut start = 0
let mut end = text.length()
while start < end && text.code_unit_at(start) <= 32 {
start += 1
}
while end > start && text.code_unit_at(end - 1) <= 32 {
end -= 1
}
guard start < end else { return None }
let negative = text.code_unit_at(start) == '-'
if negative || text.code_unit_at(start) == '+' {
start += 1
}
guard start < end else { return None }
let last = text.code_unit_at(end - 1)
if last == 'd' || last == 'D' || last == 'f' || last == 'F' {
end -= 1
}
guard start < end else { return None }
let body = text[start:end].to_owned()
if body.has_prefix("0x") || body.has_prefix("0X") {
return java_hex_double(body, negative~)
}
let mut i = 0
let mut digits = 0
while i < body.length() &&
body.code_unit_at(i) >= '0' &&
body.code_unit_at(i) <= '9' {
i += 1
digits += 1
}
if i < body.length() && body.code_unit_at(i) == '.' {
i += 1
while i < body.length() &&
body.code_unit_at(i) >= '0' &&
body.code_unit_at(i) <= '9' {
i += 1
digits += 1
}
}
guard digits > 0 else { return None }
if i < body.length() &&
(body.code_unit_at(i) == 'e' || body.code_unit_at(i) == 'E') {
i += 1
if i < body.length() &&
(body.code_unit_at(i) == '+' || body.code_unit_at(i) == '-') {
i += 1
}
let exponent_start = i
while i < body.length() &&
body.code_unit_at(i) >= '0' &&
body.code_unit_at(i) <= '9' {
i += 1
}
guard i > exponent_start else { return None }
}
guard i == body.length() else { return None }
// The grammar is valid. Core raises on overflow; Java returns infinity.
let value = @string.parse_double(body) catch {
_ => 0x7ff0000000000000L.reinterpret_as_double()
}
Some(if negative { -value } else { value })
}
///|
fn hex_digit(c : UInt16) -> Int32? {
if c >= '0' && c <= '9' {
Some(c.to_int() - 48)
} else if c >= 'a' && c <= 'f' {
Some(c.to_int() - 87)
} else if c >= 'A' && c <= 'F' {
Some(c.to_int() - 55)
} else {
None
}
}
///|
/// Keep 54 leading bits and a sticky bit, then round once to binary64,
/// ties to even. This also handles subnormals without double rounding.
fn java_hex_double(body : String, negative~ : Bool) -> Double? {
let mut i = 2
let mut point = false
let mut digits = 0
let mut fraction = 0L
let mut bits = 0L
let mut leading = 0L
let mut sticky = false
while i < body.length() {
let c = body.code_unit_at(i)
if c == '.' && !point {
point = true
i += 1
continue
}
guard hex_digit(c) is Some(digit) else { break }
digits += 1
if point {
fraction += 4L
}
for shift in [3, 2, 1, 0] {
let bit = (digit >> shift) & 1
if bits == 0L && bit == 0 {
continue
}
if bits < 54L {
leading = (leading << 1) | bit.to_int64()
} else if bit != 0 {
sticky = true
}
bits += 1L
}
i += 1
}
guard digits > 0 &&
i < body.length() &&
(body.code_unit_at(i) == 'p' || body.code_unit_at(i) == 'P') else {
return None
}
i += 1
let exponent_negative = i < body.length() && body.code_unit_at(i) == '-'
if i < body.length() && (body.code_unit_at(i) == '+' || exponent_negative) {
i += 1
}
let exponent_start = i
let mut exponent = 0L
while i < body.length() &&
body.code_unit_at(i) >= '0' &&
body.code_unit_at(i) <= '9' {
// More than any addressable significand can offset, without integer overflow.
exponent = (exponent * 10L + body.code_unit_at(i).to_int().to_int64() - 48L).min(
1000000000000L,
)
i += 1
}
guard i > exponent_start && i == body.length() else { return None }
if exponent_negative {
exponent = -exponent
}
let highest = exponent - fraction + bits - 1L
let sign = if negative { 0x8000000000000000L } else { 0L }
if bits == 0L || highest < -1075L {
return Some(sign.reinterpret_as_double())
}
if highest > 1023L {
return Some((sign | 0x7ff0000000000000L).reinterpret_as_double())
}
let keep = if highest < -1022L { (highest + 1075L).to_int() } else { 53 }
let cut = 54 - keep
let padded = leading << (54 - bits.min(54L).to_int())
let mut mantissa = padded >> cut
let guard_bit = 1L << (cut - 1)
if (padded & guard_bit) != 0L &&
(sticky || (padded & (guard_bit - 1L)) != 0L || (mantissa & 1L) != 0L) {
mantissa += 1L
}
let result = if highest < -1022L {
mantissa
} else {
((highest + 1023L) << 52) + mantissa - (1L << 52)
}
Some((sign | result).reinterpret_as_double())
}