// QUIC packet numbers (RFC 9000 §17.1). A packet number is a 62-bit integer sent
// truncated to 1–4 bytes: the sender emits the fewest bytes that let the peer
// recover the full number given the largest it has seen. Signed Int64 arithmetic
// mirrors the RFC's unbounded integers (the decode window arithmetic goes negative
// for small numbers, which UInt64 would wrap).
///|
/// The number of significant bits in `n` (`n > 0`).
fn quic_bit_length(n : Int64) -> Int {
let mut c = 0
let mut x = n
while x > 0L {
c = c + 1
x = x >> 1
}
c
}
///|
/// The number of bytes (1–4) needed to encode `full_pn` given the largest packet
/// number the peer has acknowledged (RFC 9000 §17.1 / A.2): enough bytes to cover
/// twice the number of unacknowledged packets, so wraparound is unambiguous. With
/// no acknowledgement yet, the count is `full_pn + 1`.
pub fn quic_pn_len(full_pn : Int64, largest_acked : Int64?) -> Int {
let num_unacked = match largest_acked {
Some(la) => full_pn - la
None => full_pn + 1L
}
let min_bits = if num_unacked <= 0L {
1
} else {
quic_bit_length(num_unacked)
}
let nb = (min_bits + 7) / 8
if nb < 1 {
1
} else if nb > 4 {
4
} else {
nb
}
}
///|
/// Encode `full_pn` as its truncated big-endian packet number, using the shortest
/// length that is unambiguous given `largest_acked` (RFC 9000 A.2).
pub fn quic_pn_encode(full_pn : Int64, largest_acked : Int64?) -> Bytes {
let nb = quic_pn_len(full_pn, largest_acked)
let buf = Buffer()
for i = nb - 1; i >= 0; i = i - 1 {
buf.write_byte((full_pn >> (i * 8)).to_byte())
}
buf.to_bytes()
}
///|
/// Recover the full packet number from a `truncated_pn` of `pn_nbits` bits, given
/// the largest full packet number already received (RFC 9000 A.3): pick the value
/// congruent to `truncated_pn` that is closest to the next expected number,
/// resolving wraparound with the half-window rule.
pub fn quic_pn_decode(
largest_pn : Int64,
truncated_pn : Int64,
pn_nbits : Int,
) -> Int64 {
let expected = largest_pn + 1L
let pn_win = 1L << pn_nbits
let pn_hwin = pn_win >> 1
let pn_mask = pn_win - 1L
let candidate = (expected & pn_mask.lnot()) | truncated_pn
if candidate <= expected - pn_hwin && candidate < (1L << 62) - pn_win {
candidate + pn_win
} else if candidate > expected + pn_hwin && candidate >= pn_win {
candidate - pn_win
} else {
candidate
}
}