///|
fn unit(s : String, i : Int) -> Int { if i < 0 || i >= s.length() { -1 } else { s.code_unit_at(i).to_int() } }
///|
fn space(c : Int) -> Bool { c == 32 || c == 9 || c == 10 || c == 13 }
///|
fn xml_char(c : Int) -> Bool {
c == 9 || c == 10 || c == 13 || (c >= 32 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xfffd) || (c >= 0x10000 && c <= 0x10ffff)
}
///|
fn codepoint(s : String, i : Int) -> (Int, Int) {
let c = unit(s, i)
if c >= 0xd800 && c <= 0xdbff {
let next = unit(s, i+1)
if next >= 0xdc00 && next <= 0xdfff { return (0x10000 + (c-0xd800)*1024 + next-0xdc00, 2) }
}
(c, 1)
}
///|
fn name_start(c : Int) -> Bool {
c == 95 || (c >= 65 && c <= 90) || (c >= 97 && c <= 122) ||
(c >= 0xc0 && c <= 0xd6) || (c >= 0xd8 && c <= 0xf6) || (c >= 0xf8 && c <= 0x2ff) ||
(c >= 0x370 && c <= 0x37d) || (c >= 0x37f && c <= 0x1fff) ||
(c >= 0x200c && c <= 0x200d) || (c >= 0x2070 && c <= 0x218f) ||
(c >= 0x2c00 && c <= 0x2fef) || (c >= 0x3001 && c <= 0xd7ff) ||
(c >= 0xf900 && c <= 0xfdcf) || (c >= 0xfdf0 && c <= 0xfffd) ||
(c >= 0x10000 && c <= 0xeffff)
}
///|
fn name_char(c : Int) -> Bool {
name_start(c) || c == 45 || c == 46 || (c >= 48 && c <= 57) || c == 0xb7 ||
(c >= 0x300 && c <= 0x36f) || (c >= 0x203f && c <= 0x2040)
}
///|
fn ncname(s : String) -> Bool {
if s.is_empty() { return false }
let mut i = 0
while i < s.length() {
let (c, width) = codepoint(s, i)
if !(if i == 0 { name_start(c) } else { name_char(c) }) { return false }
i += width
}
true
}
///|
fn qname(s : String) -> (String, String)? {
if s.find(":") is Some(at) {
let prefix = s[:at].to_owned()
let suffix = s[at+1:].to_owned()
if ncname(prefix) && ncname(suffix) { Some((prefix, suffix)) } else { None }
} else if ncname(s) { Some(("", s)) } else { None }
}
///|
fn reference(s : String) -> Int? {
match s {
"amp" => return Some(38); "lt" => return Some(60); "gt" => return Some(62)
"quot" => return Some(34); "apos" => return Some(39)
_ => ()
}
guard s.has_prefix("#") else { return None }
let hex = s.has_prefix("#x")
let begin = if hex { 2 } else { 1 }
if s.length() == begin { return None }
let radix = if hex { 16 } else { 10 }
let mut n = 0
for i in begin..= 48 && c <= 57 { c - 48 }
else if hex && c >= 65 && c <= 70 { c - 55 }
else if hex && c >= 97 && c <= 102 { c - 87 } else { return None }
if digit >= radix || n > (0x10ffff - digit) / radix { return None }
n = n * radix + digit
}
if xml_char(n) { Some(n) } else { None }
}