///|
fn is_restricted_xml_char(cp : Int) -> Bool {
(cp >= 1 && cp <= 8) ||
cp == 11 ||
cp == 12 ||
(cp >= 14 && cp <= 31) ||
(cp >= 127 && cp <= 132) ||
(cp >= 134 && cp <= 159)
}
///|
fn write_xml_charref(out : StringBuilder, cp : Int) -> Unit {
out.write_string("")
out.write_string(cp.to_string())
out.write_char(';')
}
///|
/// Escape a BINXML string for an XML text node, matching Views.py:
/// `xml.sax.saxutils.escape` then `xmlcharrefreplace` then drop restricted chars.
pub fn escape_xml_value(text : String) -> String {
let out = StringBuilder()
for i = 0; i < text.length(); i = i + 1 {
let cp = text[i].to_int()
if cp == 38 {
out.write_string("&")
} else if cp == 60 {
out.write_string("<")
} else if cp == 62 {
out.write_string(">")
} else if is_restricted_xml_char(cp) {
()
} else if cp > 127 {
write_xml_charref(out, cp)
} else {
out.write_char(cp.unsafe_to_char())
}
}
out.to_string()
}
///|
fn xml_name_start(cp : Int) -> Bool {
(cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || cp == 95
}
///|
fn xml_name_part(cp : Int) -> Bool {
xml_name_start(cp) || cp == 45
}
///|
/// Stricter XML Name check used by python-evtx Views.validate_name.
pub fn validate_xml_name(name : String) -> Result[String, EvtxError] {
if name.length() == 0 || !xml_name_start(name[0].to_int()) {
return Err(InvalidXmlName(name))
}
for i = 1; i < name.length(); i = i + 1 {
if !xml_name_part(name[i].to_int()) {
return Err(InvalidXmlName(name))
}
}
Ok(name)
}
///|
fn name_hash16(text : String) -> Int {
let mut hash = 0
for i = 0; i < text.length(); i = i + 1 {
hash = (hash * 65599 + text[i].to_int()) & 0xFFFF
}
hash
}