///|
/// Exact decimal conversion used for JSON hyper integers; never passes through Double.
pub fn parse_unsigned_decimal(text : String) -> UInt64 raise XdrError {
if text.is_empty() {
raise Invalid(0, "empty decimal integer")
}
let mut value = 0UL
for i, c in text.to_array() {
if c < '0' || c > '9' {
raise Invalid(i, "expected decimal digit")
}
let digit = (c.to_int() - 48).to_uint64()
if value > (0xffffffffffffffffUL - digit) / 10UL {
raise Invalid(i, "unsigned hyper overflow")
}
value = value * 10UL + digit
}
value
}
///|
pub fn parse_signed_decimal(text : String) -> Int64 raise XdrError {
let cs = text.to_array()
let negative = cs.length() > 0 && cs[0] == '-'
let digits = if negative {
String::from_array(cs.exact_view(start=1))
} else {
text
}
let value = parse_unsigned_decimal(digits)
let bound = if negative { 0x8000000000000000UL } else { 0x7fffffffffffffffUL }
if value > bound {
raise Invalid(0, "signed hyper overflow")
}
(if negative { 0UL - value } else { value }).reinterpret_as_int64()
}
///|
pub fn to_hex(bytes : Array[Byte]) -> String {
let out = StringBuilder()
let digits = "0123456789abcdef".to_array()
for b in bytes {
out.write_char(digits[b.to_int() >> 4])
out.write_char(digits[b.to_int() & 15])
}
out.to_string()
}
///|
pub fn from_hex(
text : String,
limits? : Limits = Limits::default(),
) -> Array[Byte] raise XdrError {
limits.validate()
if text.length() % 2 != 0 || text.length() / 2 > limits.max_bytes {
raise Invalid(0, "invalid hex length")
}
let cs = text.to_array()
let out : Array[Byte] = []
let mut high = 0
for i, c in cs {
let n = if c >= '0' && c <= '9' {
c.to_int() - 48
} else if c >= 'a' && c <= 'f' {
c.to_int() - 87
} else if c >= 'A' && c <= 'F' {
c.to_int() - 55
} else {
raise Invalid(i, "invalid hex digit")
}
if i % 2 == 0 {
high = n
} else {
out.push((high * 16 + n).to_byte())
}
}
out
}