///|
fn parse_extensions(
  data : BytesView,
  offset : Int,
  total_len : Int,
  hello : ClientHello,
) -> Result[ClientHello, ParseError] {
  let mut cursor = offset
  let end = offset + total_len
  let mut current = hello
  while cursor + 4 <= end {
    guard read_u16(data, cursor) is Some(ext_type) else {
      return Err(BadLength(field="extension.type", offset=cursor))
    }
    guard read_u16_int(data, cursor + 2) is Some(ext_len) else {
      return Err(BadLength(field="extension.length", offset=cursor + 2))
    }
    cursor = cursor + 4
    if ext_len < 0 ||
      cursor + ext_len > end ||
      !has_range(data, cursor, ext_len) {
      return Err(BadLength(field="extension.body", offset=cursor))
    }
    if !is_grease(ext_type) {
      current.extensions.push(ext_type)
    }
    let body = data[cursor:cursor + ext_len]
    match ext_type.to_int() {
      0 => current = { ..current, server_name: parse_sni(body) }
      10 => current = { ..current, supported_groups: parse_u16_vector(body) }
      11 => current = { ..current, ec_point_formats: parse_byte_vector(body) }
      13 =>
        current = { ..current, signature_algorithms: parse_u16_vector(body) }
      16 => current = { ..current, alpn_protocols: parse_alpn(body) }
      _ => ()
    }
    cursor = cursor + ext_len
  }
  if cursor == end {
    Ok(current)
  } else {
    Err(BadLength(field="extensions.trailing", offset=cursor))
  }
}

///|
pub fn parse_client_hello(data : BytesView) -> Result[ClientHello, ParseError] {
  if data.length() < 5 {
    return Err(IncompleteRecord(expected=5, available=data.length()))
  }
  if data[0].to_int() != 0x16 {
    return Err(NotTlsHandshake)
  }
  guard read_u16(data, 1) is Some(record_version) else {
    return Err(BadLength(field="record.version", offset=1))
  }
  if record_version.to_int() < 0x0301 || record_version.to_int() > 0x0304 {
    return Err(UnsupportedRecordVersion(record_version))
  }
  guard read_u16_int(data, 3) is Some(record_len) else {
    return Err(BadLength(field="record.length", offset=3))
  }
  if record_len < 0 {
    return Err(BadLength(field="record.length", offset=3))
  }
  let record_end = 5 + record_len
  if record_end < 5 || data.length() < record_end {
    return Err(IncompleteRecord(expected=record_end, available=data.length()))
  }
  if record_len < 4 {
    return Err(BadLength(field="handshake.header", offset=5))
  }
  if data[5].to_int() != 1 {
    return Err(BadHandshakeType(data[5].to_int()))
  }
  guard read_u24_int(data, 6) is Some(handshake_len) else {
    return Err(BadLength(field="handshake.length", offset=6))
  }
  let body_offset = 9
  let body_end = body_offset + handshake_len
  if handshake_len < 0 || body_end < body_offset || body_end > record_end {
    return Err(BadLength(field="handshake.body", offset=body_offset))
  }
  guard require_range(data, body_offset, 34, "clienthello.fixed") is Ok(_) else {
    return Err(BadLength(field="clienthello.fixed", offset=body_offset))
  }
  guard read_u16(data, body_offset) is Some(legacy_version) else {
    return Err(BadLength(field="client.version", offset=body_offset))
  }
  let mut cursor = body_offset + 34
  guard read_u8(data, cursor) is Some(session_len) else {
    return Err(BadLength(field="session_id.length", offset=cursor))
  }
  if cursor + 1 + session_len > body_end {
    return Err(BadLength(field="session_id.body", offset=cursor))
  }
  cursor = cursor + 1 + session_len
  guard read_u16_int(data, cursor) is Some(cipher_len) else {
    return Err(BadLength(field="cipher_suites.length", offset=cursor))
  }
  cursor = cursor + 2
  if cipher_len < 0 || cursor + cipher_len > body_end || cipher_len % 2 != 0 {
    return Err(BadLength(field="cipher_suites.body", offset=cursor))
  }
  let cipher_suites : Array[UInt16] = []
  let mut c = cursor
  while c + 1 < cursor + cipher_len {
    match read_u16(data, c) {
      Some(v) => if !is_grease(v) { cipher_suites.push(v) }
      None => ()
    }
    c = c + 2
  }
  cursor = cursor + cipher_len
  guard read_u8(data, cursor) is Some(compression_len) else {
    return Err(BadLength(field="compression.length", offset=cursor))
  }
  cursor = cursor + 1
  if compression_len < 0 || cursor + compression_len > body_end {
    return Err(BadLength(field="compression.body", offset=cursor))
  }
  let compression_methods : Array[Byte] = []
  for i in 0.. StreamResult {
  for b in chunk {
    self.buffer.push(b)
  }
  let data = Bytes::from_array(self.buffer)
  match parse_client_hello(data) {
    Ok(hello) => {
      self.buffer.clear()
      Parsed(hello)
    }
    Err(IncompleteRecord(_)) => NeedMore
    Err(e) => {
      self.buffer.clear()
      Rejected(e)
    }
  }
}

///|
pub fn parse_tls_record(data : BytesView) -> Result[TlsRecord, ParseError] {
  if data.length() < 5 {
    return Err(IncompleteRecord(expected=5, available=data.length()))
  }
  let ct = data[0].to_int()
  guard read_u16(data, 1) is Some(_) else {
    return Err(BadLength(field="record.version", offset=1))
  }
  guard read_u16_int(data, 3) is Some(record_len) else {
    return Err(BadLength(field="record.length", offset=3))
  }
  if record_len < 0 {
    return Err(BadLength(field="record.length", offset=3))
  }
  let record_end = 5 + record_len
  if record_end < 5 || data.length() < record_end {
    return Err(IncompleteRecord(expected=record_end, available=data.length()))
  }

  let body = data[0:record_end]
  match ct {
    0x14 =>
      Ok(
        GenericRecord(
          ChangeCipherSpec,
          Bytes::from_array(self_bytes(data, 5, record_len)),
        ),
      )
    0x15 =>
      match parse_alert(body) {
        Ok(alert) => Ok(AlertRecord(alert))
        Err(e) => Err(e)
      }
    0x16 => {
      if record_len < 1 {
        return Err(BadLength(field="handshake.type", offset=5))
      }
      let handshake_type = data[5].to_int()
      match handshake_type {
        1 =>
          match parse_client_hello(body) {
            Ok(hello) => Ok(ClientHelloRecord(hello))
            Err(e) => Err(e)
          }
        2 =>
          match parse_server_hello(body) {
            Ok(hello) => Ok(ServerHelloRecord(hello))
            Err(e) => Err(e)
          }
        11 =>
          match parse_certificate(body) {
            Ok(cert) => Ok(CertificateRecord(cert))
            Err(e) => Err(e)
          }
        12 =>
          match parse_server_key_exchange(body) {
            Ok(ske) => Ok(ServerKeyExchangeRecord(ske))
            Err(e) => Err(e)
          }
        16 =>
          match parse_client_key_exchange(body) {
            Ok(cke) => Ok(ClientKeyExchangeRecord(cke))
            Err(e) => Err(e)
          }
        4 =>
          match parse_new_session_ticket(body) {
            Ok(nst) => Ok(NewSessionTicketRecord(nst))
            Err(e) => Err(e)
          }
        8 =>
          match parse_encrypted_extensions(body) {
            Ok(ee) => Ok(EncryptedExtensionsRecord(ee))
            Err(e) => Err(e)
          }
        0 =>
          match parse_hello_request(body) {
            Ok(hr) => Ok(HelloRequestRecord(hr))
            Err(e) => Err(e)
          }
        3 =>
          match parse_hello_verify_request(body) {
            Ok(hvr) => Ok(HelloVerifyRequestRecord(hvr))
            Err(e) => Err(e)
          }
        13 =>
          match parse_certificate_request(body) {
            Ok(cr) => Ok(CertificateRequestRecord(cr))
            Err(e) => Err(e)
          }
        15 =>
          match parse_certificate_verify(body) {
            Ok(cv) => Ok(CertificateVerifyRecord(cv))
            Err(e) => Err(e)
          }
        20 =>
          match parse_finished(body) {
            Ok(fn_) => Ok(FinishedRecord(fn_))
            Err(e) => Err(e)
          }
        _ =>
          Ok(
            GenericRecord(
              Handshake,
              Bytes::from_array(self_bytes(data, 5, record_len)),
            ),
          )
      }
    }
    0x17 =>
      Ok(
        GenericRecord(
          ApplicationData,
          Bytes::from_array(self_bytes(data, 5, record_len)),
        ),
      )
    _ =>
      Ok(
        GenericRecord(
          Unknown(data[0]),
          Bytes::from_array(self_bytes(data, 5, record_len)),
        ),
      )
  }
}

///|
fn self_bytes(data : BytesView, offset : Int, len : Int) -> Array[Byte] {
  if !has_range(data, offset, len) {
    return []
  }
  let out = Array::new(capacity=len)
  for i in 0..