///|
pub fn bytes_hex(data : Bytes) -> String {
  let digits = "0123456789abcdef"
  let out = StringBuilder()
  for b in data {
    out.write_char(digits[b.to_int() >> 4].unsafe_to_char())
    out.write_char(digits[b.to_int() & 15].unsafe_to_char())
  }
  out.to_string()
}

///|
fn hex_digit(n : Int32) -> Int32 raise InputError {
  if n >= 48 && n <= 57 {
    n - 48
  } else if n >= 65 && n <= 70 {
    n - 55
  } else if n >= 97 && n <= 102 {
    n - 87
  } else {
    raise InvalidInput("invalid hexadecimal digit")
  }
}

///|
pub fn bytes_from_hex(text : String) -> Bytes raise InputError {
  guard text.length() % 2 == 0 && text.length() <= 131070 else {
    raise InvalidInput(
      "hex input must have even length and decode to at most 65535 bytes",
    )
  }
  Bytes::makei(text.length() / 2, i => {
    ((hex_digit(text[2 * i].to_int()) << 4) |
    hex_digit(text[2 * i + 1].to_int())).to_byte()
  })
}

///|
pub fn address_text(bytes : Bytes) -> String raise InputError {
  if bytes.length() == 4 {
    "\{bytes[0].to_int()}.\{bytes[1].to_int()}.\{bytes[2].to_int()}.\{bytes[3].to_int()}"
  } else if bytes.length() == 16 {
    let groups : Array[String] = []
    for i in 0..<8 {
      groups.push(
        ((bytes[2 * i].to_int() << 8) | bytes[2 * i + 1].to_int()).to_string(
          radix=16,
        ),
      )
    }
    groups.join(":")
  } else {
    raise InvalidInput("address must contain 4 or 16 octets")
  }
}

///|
/// Parse reversible DNS presentation, including escaped octets and UTF-8.
/// Use from_utf8_labels when an instance label already has known boundaries.
pub fn DnsName::parse(text : String) -> DnsName raise InputError {
  if text == "." {
    return DnsName::from_labels([])
  }
  let data = @utf8.encode(text)
  guard data.length() > 0 && data.length() <= 1024 else {
    raise InvalidInput("name presentation length outside 1..1024")
  }
  let labels : Array[Bytes] = []
  let label : Array[Byte] = []
  let mut at = 0
  while at < data.length() {
    let b = data[at]
    if b == b'.' {
      guard !label.is_empty() else { raise InvalidInput("empty DNS label") }
      labels.push(Bytes::makei(label.length(), i => label[i]))
      label.clear()
      at += 1
    } else if b == b'\\' {
      guard at + 1 < data.length() else {
        raise InvalidInput("incomplete name escape")
      }
      let n = data[at + 1].to_int()
      if n >= 48 && n <= 57 {
        guard at + 3 < data.length() else {
          raise InvalidInput("decimal name escape requires three digits")
        }
        let a = data[at + 2].to_int() - 48
        let c = data[at + 3].to_int() - 48
        guard a >= 0 && a <= 9 && c >= 0 && c <= 9 else {
          raise InvalidInput("invalid decimal name escape")
        }
        let value = (n - 48) * 100 + a * 10 + c
        guard value <= 255 else {
          raise InvalidInput("name escape exceeds one octet")
        }
        label.push(value.to_byte())
        at += 4
      } else {
        label.push(data[at + 1])
        at += 2
      }
    } else {
      label.push(b)
      at += 1
    }
  }
  if !label.is_empty() {
    labels.push(Bytes::makei(label.length(), i => label[i]))
  }
  DnsName::from_labels(labels)
}