///|
fn uri_subdelimiter(ch : Char) -> Bool {
  ch is ('!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=')
}

///|
fn valid_ipv4(text : StringView) -> Bool {
  let parts = text.split(".").to_array()
  if parts.length() != 4 {
    return false
  }
  for part in parts {
    if part.is_empty() ||
      part.length() > 3 ||
      (part.length() > 1 && part[0] == '0') {
      return false
    }
    let mut value = 0
    for ch in part {
      if !(ch is ('0'..<='9')) {
        return false
      }
      value = value * 10 + ch.to_int() - '0'.to_int()
    }
    if value > 255 {
      return false
    }
  }
  true
}

///|
fn ipv6_units(text : StringView, ipv4~ : Bool) -> Int32? {
  if text.is_empty() {
    return Some(0)
  }
  let groups = text.split(":").to_array()
  let mut units = 0
  for index, group in groups {
    if group.contains(".") {
      if !ipv4 || index != groups.length() - 1 || !valid_ipv4(group) {
        return None
      }
      units += 2
    } else {
      if group.is_empty() || group.length() > 4 {
        return None
      }
      for ch in group {
        if !(ch is ('0'..<='9' | 'a'..<='f' | 'A'..<='F')) {
          return None
        }
      }
      units += 1
    }
  }
  Some(units)
}

///|
fn valid_ip_literal(host : String) -> Bool {
  if host.length() <= 2 || !host.has_suffix("]") {
    return false
  }
  let inner = host[1:host.length() - 1]
  if inner.has_prefix("v") || inner.has_prefix("V") {
    let parts = inner[1:].split(".").to_array()
    if parts.length() < 2 || parts[0].is_empty() {
      return false
    }
    for ch in parts[0] {
      if !(ch is ('0'..<='9' | 'a'..<='f' | 'A'..<='F')) {
        return false
      }
    }
    let offset = parts[0].length() + 2
    if offset >= inner.length() {
      return false
    }
    for ch in inner[offset:] {
      if !(is_unreserved_byte(ch.to_int()) || uri_subdelimiter(ch) || ch == ':') {
        return false
      }
    }
    return true
  }
  let halves = inner.split("::").to_array()
  match halves[:] {
    [whole] => ipv6_units(whole, ipv4=true) == Some(8)
    [left, right] =>
      match (ipv6_units(left, ipv4=false), ipv6_units(right, ipv4=true)) {
        (Some(a), Some(b)) => a + b < 8
        _ => false
      }
    _ => false
  }
}