///|
/// Apply IDNA mapping to a domain name
///
/// The result is built only once a character is removed or replaced, so a
/// domain that needs no mapping is returned as is.
fn map_domain(
  domain : String,
  use_std3_ascii_rules~ : Bool,
) -> String raise IdnaError {
  let mut output : StringBuilder? = None
  // UTF-16 offset of `c`, to copy the unchanged prefix on the first change.
  let mut offset = 0
  for c in domain {
    let entry = @idna_data.lookup_idna_entry(c)
    let keep = match entry.status() {
      // Nontransitional processing keeps deviation characters as they are.
      Valid | Deviation => true
      Ignored | Mapped => false
      Disallowed => raise DisallowedCodePoint(cp=c.to_int())
      DisallowedSTD3Valid =>
        if use_std3_ascii_rules {
          raise DisallowedCodePoint(cp=c.to_int())
        } else {
          true
        }
      DisallowedSTD3Mapped =>
        if use_std3_ascii_rules {
          raise DisallowedCodePoint(cp=c.to_int())
        } else {
          false
        }
    }
    if output is Some(output) {
      if keep {
        output.write_char(c)
      } else {
        // An ignored character has an empty mapping.
        entry.write_mapping(output)
      }
    } else if !keep {
      // The size hint counts bytes, two per UTF-16 code unit.
      let builder = StringBuilder(size_hint=2 * domain.length())
      builder.write_view(domain[:offset])
      entry.write_mapping(builder)
      output = Some(builder)
    }
    offset += if c.to_int() > 0xFFFF { 2 } else { 1 }
  }
  if output is Some(output) {
    output.to_string()
  } else {
    domain
  }
}

///|
/// Apply IDNA mapping to a single label (for ToUnicode)
fn map_label(
  label : String,
  use_std3_ascii_rules~ : Bool,
) -> String raise IdnaError {
  map_domain(label, use_std3_ascii_rules~)
}

///|
/// Split domain into labels at separators
fn split_labels(domain : String) -> Array[String] {
  let labels : Array[String] = []
  // Separators are single UTF-16 code units, so slicing at them never
  // splits a surrogate pair.
  let mut start = 0
  for i in 0..