///| Common hash/hex helpers.

///|
fn utf8_encode(s : String) -> Bytes {
  let buf : Array[Byte] = []
  for c in s {
    let cp = c.to_int()
    if cp < 0x80 {
      buf.push(cp.to_byte())
    } else if cp < 0x800 {
      buf.push((0xc0 | (cp >> 6)).to_byte())
      buf.push((0x80 | (cp & 0x3f)).to_byte())
    } else if cp < 0x10000 {
      buf.push((0xe0 | (cp >> 12)).to_byte())
      buf.push((0x80 | ((cp >> 6) & 0x3f)).to_byte())
      buf.push((0x80 | (cp & 0x3f)).to_byte())
    } else {
      buf.push((0xf0 | (cp >> 18)).to_byte())
      buf.push((0x80 | ((cp >> 12) & 0x3f)).to_byte())
      buf.push((0x80 | ((cp >> 6) & 0x3f)).to_byte())
      buf.push((0x80 | (cp & 0x3f)).to_byte())
    }
  }
  Bytes::from_array(buf)
}

///|
pub fn short_hex(hex : String, n : Int) -> String {
  if hex.length() <= n {
    hex
  } else {
    String::unsafe_substring(hex, start=0, end=n)
  }
}

///|
pub fn is_hex_char(c : Char) -> Bool {
  (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
}

///|
pub fn is_hex_string(s : String) -> Bool {
  if s.length() == 0 {
    return false
  }
  for c in s {
    if !is_hex_char(c) {
      return false
    }
  }
  true
}

///|
pub fn lower_hex_string(s : String) -> String {
  let out = StringBuilder::new()
  for c in s {
    if c >= 'A' && c <= 'F' {
      out.write_char((c.to_int() + 32).unsafe_to_char())
    } else {
      out.write_char(c)
    }
  }
  out.to_string()
}

///|
pub fn hex_char_to_int(c : Char) -> Int raise {
  if c >= '0' && c <= '9' {
    return c.to_int() - '0'.to_int()
  }
  if c >= 'a' && c <= 'f' {
    return 10 + (c.to_int() - 'a'.to_int())
  }
  if c >= 'A' && c <= 'F' {
    return 10 + (c.to_int() - 'A'.to_int())
  }
  fail("Invalid hex char")
}

///|
pub fn int_to_hex4(n : Int) -> String {
  let digits = "0123456789abcdef"
  let mut result = ""
  let mut v = n
  for _ in 0..<4 {
    let d = v % 16
    result = String::make(1, digits[d].to_int().unsafe_to_char()) + result
    v = v / 16
  }
  result
}