///|
#cfg(target="native")
priv suberror QuicCodecError {
QuicVarIntTooLarge
QuicVarIntTruncated
QuicPacketTooShort
} derive(Debug, ToJson)
///|
#cfg(target="native")
fn quic_write_varint(out : @buffer.Buffer, value : Int) -> Unit raise {
guard value >= 0 else { raise QuicVarIntTooLarge }
if value < 64 {
out.write_byte(value.to_byte())
} else if value < 16384 {
out.write_byte(((value >> 8) | 0x40).to_byte())
out.write_byte((value & 0xff).to_byte())
} else if value < 1073741824 {
out.write_byte(((value >> 24) | 0x80).to_byte())
out.write_byte(((value >> 16) & 0xff).to_byte())
out.write_byte(((value >> 8) & 0xff).to_byte())
out.write_byte((value & 0xff).to_byte())
} else {
raise QuicVarIntTooLarge
}
}
///|
#cfg(target="native")
fn quic_read_varint(data : Bytes, offset : Int) -> (Int, Int) raise {
guard offset < data.length() else { raise QuicVarIntTruncated }
let first = data[offset].to_int()
match first >> 6 {
0 => (first & 0x3f, offset + 1)
1 => {
guard offset + 2 <= data.length() else { raise QuicVarIntTruncated }
let value = ((first & 0x3f) << 8) | data[offset + 1].to_int()
(value, offset + 2)
}
2 => {
guard offset + 4 <= data.length() else { raise QuicVarIntTruncated }
let value = ((first & 0x3f) << 24) |
(data[offset + 1].to_int() << 16) |
(data[offset + 2].to_int() << 8) |
data[offset + 3].to_int()
(value, offset + 4)
}
_ => raise QuicVarIntTooLarge
}
}
///|
#cfg(target="native")
#warnings("-unused_field")
priv struct QuicLongHeader {
packet_type : Int
version : Int
destination_connection_id : Bytes
source_connection_id : Bytes
payload_offset : Int
}
///|
#cfg(target="native")
#warnings("-unused_value")
fn quic_parse_long_header(packet : Bytes) -> QuicLongHeader raise {
guard packet.length() >= 7 else { raise QuicPacketTooShort }
let first = packet[0].to_int()
guard (first & 0x80) != 0 else { raise QuicPacketTooShort }
let version = (packet[1].to_int() << 24) |
(packet[2].to_int() << 16) |
(packet[3].to_int() << 8) |
packet[4].to_int()
let dcid_len = packet[5].to_int()
guard 6 + dcid_len < packet.length() else { raise QuicPacketTooShort }
let dcid = @buffer.new()
dcid.write_bytes(packet[6:6 + dcid_len])
let scid_len_pos = 6 + dcid_len
let scid_len = packet[scid_len_pos].to_int()
let scid_start = scid_len_pos + 1
guard scid_start + scid_len <= packet.length() else {
raise QuicPacketTooShort
}
let scid = @buffer.new()
scid.write_bytes(packet[scid_start:scid_start + scid_len])
{
packet_type: (first >> 4) & 0x3,
version,
destination_connection_id: dcid.contents(),
source_connection_id: scid.contents(),
payload_offset: scid_start + scid_len,
}
}
///|
#cfg(target="native")
fn quic_long_packet_end(datagram : Bytes, offset : Int) -> Int raise {
let packet = quic_copy_slice(datagram, offset, datagram.length())
let long_header = quic_parse_long_header(packet)
if long_header.packet_type == 3 {
return datagram.length()
}
let (payload_length, packet_number_offset) = if long_header.packet_type == 0 {
let (token_length, token_start) = quic_read_varint(
packet,
long_header.payload_offset,
)
quic_read_varint(packet, token_start + token_length)
} else {
quic_read_varint(packet, long_header.payload_offset)
}
let packet_end = offset + packet_number_offset + payload_length
guard packet_end <= datagram.length() else { raise QuicPacketTooShort }
packet_end
}
///|
#cfg(target="native")
#warnings("-unused_value")
fn quic_split_datagram(datagram : Bytes) -> Array[Bytes] raise {
let packets = []
for offset = 0; offset < datagram.length(); {
let first = datagram[offset].to_int()
let packet_end = if (first & 0x80) != 0 {
quic_long_packet_end(datagram, offset)
} else {
datagram.length()
}
packets.push(quic_copy_slice(datagram, offset, packet_end))
continue packet_end
}
packets
}