///| Converts a Punycode string of ASCII-only symbols to a string of Unicode symbols.
pub fn decode(encoded : String) -> String? {
let decoded = []
let encoded_length = encoded.length().reinterpret_as_uint()
let delimiter_idx = encoded.rev_find(delimiter.to_string())
let basic = match delimiter_idx {
None => 0U
Some(idx) => idx.reinterpret_as_uint()
}
// Basic ASCII code point extraction
let encoded = encoded.to_array()
for j = 0U; j < basic; j = j + 1U {
let c = encoded[j.reinterpret_as_int()]
let code = c.to_int()
if code >= 0x80 {
// Not basic ASCII code point
return None
}
decoded.push(c)
}
// Initialize the state
let mut n = initial_n
let mut i = 0U
let mut bias = initial_bias
// Main decoding loop
let mut idx = if basic > 0U { basic + 1U } else { 0U }
while idx < encoded_length {
let oldi = i
let mut w = 1U
for k = base; ; k = k + base {
let digit = decode_digit(encoded[idx.reinterpret_as_int()])
idx += 1
if digit > (@uint.max_value - i) / w {
return None // Overflow
}
i += digit * w
let t = if k <= bias {
tmin
} else if k >= bias + tmax {
tmax
} else {
k - bias
}
if digit < t {
break
}
if w > @uint.max_value / (base - t) {
return None // Overflow
}
w *= base - t
}
let out = decoded.length().reinterpret_as_uint() + 1U
bias = adapt(i - oldi, out, oldi == 0U)
if i / out > @uint.max_value - n {
return None // Overflow
}
n += i / out
i %= out
decoded.insert(
i.reinterpret_as_int(),
Int::unsafe_to_char(n.reinterpret_as_int()),
)
i += 1U
}
// FIXME(chawyehsu): I was expecting to convert the array to a string
// using `.to_string()` here but it seems it's not the right way to
// do it in MoonBit, `.join("")` doesn't work as expected either.
let mut decoded_str = ""
let mut i = 0
while i < decoded.length() {
decoded_str = decoded_str + decoded[i].to_string()
i += 1
}
Some(decoded_str)
}
///|
fn decode_digit(c : Char) -> UInt {
let code = c.to_int()
if code >= 48 && code < 58 {
code.reinterpret_as_uint() - 48U + 26U // 0-9
} else if code >= 65 && code < 91 {
code.reinterpret_as_uint() - 65U // A-Z
} else if code >= 97 && code < 123 {
code.reinterpret_as_uint() - 97U // a-z
} else {
abort("Invalid punycode")
}
}