///|
/// Low-level percent-encoding primitives.
///|
let hex_digits : String = "0123456789ABCDEF"
///|
/// Returns the value of the hexadecimal digit `x`, or `-1` if `x` is not one.
fn decode_hexdig(x : Int) -> Int {
if x >= '0'.to_int() && x <= '9'.to_int() {
x - '0'.to_int()
} else if x >= 'A'.to_int() && x <= 'F'.to_int() {
x - 'A'.to_int() + 10
} else if x >= 'a'.to_int() && x <= 'f'.to_int() {
x - 'a'.to_int() + 10
} else {
-1
}
}
///|
/// Checks whether `x` is a hexadecimal digit.
fn is_hexdig(x : Int) -> Bool {
decode_hexdig(x) >= 0
}
///|
/// Checks whether `hi` and `lo` are both hexadecimal digits.
fn is_hexdig_pair(hi : Int, lo : Int) -> Bool {
is_hexdig(hi) && is_hexdig(lo)
}
///|
/// Decodes the octet represented by the hexadecimal digits `hi` and `lo`.
fn decode_octet(hi : Int, lo : Int) -> Int {
(decode_hexdig(hi) << 4) | decode_hexdig(lo)
}
///|
/// Percent-encodes a single octet, e.g. `0x2f` to `"%2F"`.
fn encode_byte(x : Int) -> String {
let sb = StringBuilder::new(size_hint=3)
sb.write_char('%')
sb.write_char(hex_digits.get_char((x >> 4) & 0xf).unwrap())
sb.write_char(hex_digits.get_char(x & 0xf).unwrap())
sb.to_string()
}
///|
/// Appends the percent-encoding of a single octet to `sb`.
fn push_encoded_byte(sb : StringBuilder, x : Int) -> Unit {
sb.write_char('%')
sb.write_char(hex_digits.get_char((x >> 4) & 0xf).unwrap())
sb.write_char(hex_digits.get_char(x & 0xf).unwrap())
}
///|
/// Validates `s` against `table`, i.e., checks that it is formed only of
/// characters the table allows and, if the table allows them, percent-encoded
/// octets.
fn table_validate(table : @enc.Table, s : String) -> Bool {
let pct = table.allows_pct_encoded()
let non_ascii = table.allows_non_ascii()
let len = s.length()
let mut i = 0
while i < len {
let x = code_at(s, i)
if pct && x == '%' {
if i + 2 >= len {
return false
}
if !is_hexdig_pair(code_at(s, i + 1), code_at(s, i + 2)) {
return false
}
i += 3
} else if non_ascii {
let (cp, w) = next_code_point(s, i)
if !table.allows_code_point(cp) {
return false
}
i += w
} else {
if !table.allows_ascii(x) {
return false
}
i += 1
}
}
true
}