///|
pub fn bytes_concat(left : Bytes, right : Bytes) -> Bytes {
  let buffer = @buffer.Buffer(size_hint=left.length() + right.length())
  buffer.write_bytes(left)
  buffer.write_bytes(right)
  buffer.to_bytes()
}

///|
pub fn bytes_slice(
  input : Bytes,
  start : Int,
  end : Int,
) -> Result[Bytes, ProxyError] {
  if start < 0 || end < start || end > input.length() {
    Err(proxy_error(InvalidLength, start, "invalid byte slice"))
  } else {
    Ok(Bytes::from_array(input.to_array()[start:end]))
  }
}

///|
pub fn bytes_equal_constant_time(left : Bytes, right : Bytes) -> Bool {
  let mut different = left.length() ^ right.length()
  let limit = if left.length() < right.length() {
    left.length()
  } else {
    right.length()
  }
  for i = 0; i < limit; i = i + 1 {
    different = different | (left[i].to_int() ^ right[i].to_int())
  }
  different == 0
}

///|
fn hex_nibble(value : Int) -> Char {
  if value < 10 {
    ('0'.to_int() + value).to_char().unwrap()
  } else {
    ('a'.to_int() + value - 10).to_char().unwrap()
  }
}

///|
pub fn hex_encode(input : Bytes) -> String {
  let chars = Array::make(input.length() * 2, '0')
  for i = 0; i < input.length(); i = i + 1 {
    chars[i * 2] = hex_nibble(input[i].to_int() / 16)
    chars[i * 2 + 1] = hex_nibble(input[i].to_int() % 16)
  }
  String::from_array(chars)
}

///|
fn decode_hex_char(ch : Char) -> Int {
  let n = ch.to_int()
  if n >= '0'.to_int() && n <= '9'.to_int() {
    n - '0'.to_int()
  } else if n >= 'a'.to_int() && n <= 'f'.to_int() {
    n - 'a'.to_int() + 10
  } else if n >= 'A'.to_int() && n <= 'F'.to_int() {
    n - 'A'.to_int() + 10
  } else {
    -1
  }
}

///|
pub fn hex_decode(input : String) -> Result[Bytes, ProxyError] {
  let chars = input.to_array()
  if chars.length() % 2 != 0 {
    return Err(
      proxy_error(
        InvalidLength,
        chars.length(),
        "hex needs an even number of characters",
      ),
    )
  }
  let buffer = @buffer.Buffer(size_hint=chars.length() / 2)
  for i = 0; i < chars.length(); i = i + 2 {
    let hi = decode_hex_char(chars[i])
    let lo = decode_hex_char(chars[i + 1])
    if hi < 0 || lo < 0 {
      return Err(proxy_error(InvalidLength, i, "invalid hexadecimal digit"))
    }
    buffer.write_byte((hi * 16 + lo).to_byte())
  }
  Ok(buffer.to_bytes())
}