///|
pub fn Symbols::from_bytes(bytes : Bytes) -> Symbols raise CodeError {
  if bytes.length() > 262144 {
    raise Limit("byte adapter exceeds maximum vector size")
  }
  let values : Array[Int32] = []
  for byte in bytes.to_array() {
    values.push(byte.to_int())
  }
  { values, }
}

///|
pub fn Symbols::to_bytes(self : Symbols) -> Bytes {
  let bytes = self.values.map(value => value.to_byte())
  Bytes::from_array(bytes[:])
}

///|
pub fn Symbols::to_hex(self : Symbols) -> String {
  let digits : Array[Char] = [
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
  ]
  let text : Array[Char] = []
  for value in self.values {
    text.push(digits[value >> 4])
    text.push(digits[value & 15])
  }
  String::from_array(text[:])
}

///|
fn hex_digit(char : Char) -> Int32 {
  match char {
    '0'..<='9' => char.to_int() - 48
    'a'..<='f' => char.to_int() - 87
    'A'..<='F' => char.to_int() - 55
    _ => -1
  }
}

///|
// Only ASCII whitespace is ignored, including between a byte's two nibbles.
pub fn symbols_from_hex(
  text : String,
  max_bytes~ : Int32 = 262144,
) -> Symbols raise CodeError {
  if max_bytes < 0 || max_bytes > 262144 {
    raise Limit("hex byte budget outside 0..262144")
  }
  if text.length() > 1048576 {
    raise Limit("hex text exceeds 1048576 code units")
  }
  let values : Array[Int32] = []
  let mut high = -1
  for char in text.iter() {
    if char == ' ' || char == '\t' || char == '\r' || char == '\n' {
      continue
    }
    let digit = hex_digit(char)
    if digit < 0 {
      raise Invalid("hex input contains a non-ASCII hexadecimal digit")
    }
    if high < 0 {
      high = digit
    } else {
      if values.length() == max_bytes {
        raise Limit("hex output byte budget exhausted")
      }
      values.push((high << 4) | digit)
      high = -1
    }
  }
  if high >= 0 {
    raise Invalid("hex input has an odd number of digits")
  }
  { values, }
}

///|
pub fn packet_from_hex(text : String) -> Packet raise CodeError {
  packet_from_wire(symbols_from_hex(text, max_bytes=4188))
}

///|
pub fn feedback_from_hex(text : String) -> Feedback raise CodeError {
  feedback_from_wire(symbols_from_hex(text, max_bytes=88))
}