use crate::core::Role;
const OPENER_BIT: u64 = 0x01;
const DIR_BIT: u64 = 0x02;
pub(crate) const INDEX_MAX: u64 = (1u64 << 60) - 1;
pub(crate) const MAX_STREAMS_CEILING: u64 = 1u64 << 60;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct StreamId(u64);
impl StreamId {
pub fn index(self) -> u64 {
self.0 >> 2
}
pub fn dir(self) -> Dir {
if self.0 & DIR_BIT == 0 {
Dir::Bi
} else {
Dir::Uni
}
}
pub fn initiated_by_connection_initiator(self) -> bool {
self.0 & OPENER_BIT == 0
}
pub fn as_u64(self) -> u64 {
self.0
}
pub(crate) fn from_u64(v: u64) -> Self {
Self(v)
}
pub(crate) fn new(index: u64, dir: Dir, opener: Opener) -> Self {
debug_assert!(
index <= INDEX_MAX,
"§9.1: the index is 60 bits and §10.4's limit is what bounds it"
);
let dir_bit = match dir {
Dir::Bi => 0,
Dir::Uni => DIR_BIT,
};
let opener_bit = match opener {
Opener::Initiator => 0,
Opener::Responder => OPENER_BIT,
};
Self((index << 2) | dir_bit | opener_bit)
}
pub(crate) fn opener(self) -> Opener {
if self.initiated_by_connection_initiator() {
Opener::Initiator
} else {
Opener::Responder
}
}
}
impl ::core::fmt::Display for StreamId {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum Dir {
Bi,
Uni,
}
impl Dir {
pub(crate) fn slot(self) -> usize {
match self {
Dir::Bi => 0,
Dir::Uni => 1,
}
}
pub(crate) const ALL: [Dir; 2] = [Dir::Bi, Dir::Uni];
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum Opener {
Initiator,
Responder,
}
impl Opener {
pub(crate) fn of_role(role: Role) -> Self {
match role {
Role::Initiator => Opener::Initiator,
Role::Responder => Opener::Responder,
}
}
pub(crate) fn peer(self) -> Self {
match self {
Opener::Initiator => Opener::Responder,
Opener::Responder => Opener::Initiator,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_two_low_bits_are_the_tag_and_the_rest_is_the_index() {
let cases = [
(Dir::Bi, Opener::Initiator, 0b00u64),
(Dir::Bi, Opener::Responder, 0b01),
(Dir::Uni, Opener::Initiator, 0b10),
(Dir::Uni, Opener::Responder, 0b11),
];
for (dir, opener, tag) in cases {
for index in [0u64, 1, 5, 31, 128, INDEX_MAX] {
let id = StreamId::new(index, dir, opener);
assert_eq!(id.as_u64(), (index << 2) | tag);
assert_eq!(id.index(), index);
assert_eq!(id.dir(), dir);
assert_eq!(id.opener(), opener);
assert_eq!(
id.initiated_by_connection_initiator(),
opener == Opener::Initiator
);
assert_eq!(StreamId::from_u64(id.as_u64()), id);
}
}
}
#[test]
fn the_largest_decodable_id_yields_the_largest_index() {
let largest = crate::varint::VarInt::MAX_VALUE;
assert_eq!(StreamId::from_u64(largest).index(), INDEX_MAX);
assert_eq!(MAX_STREAMS_CEILING, INDEX_MAX + 1);
}
#[test]
fn display_is_the_decimal_wire_value() {
assert_eq!(
StreamId::new(3, Dir::Uni, Opener::Responder).to_string(),
"15"
);
}
#[test]
fn opener_comes_from_the_installed_role() {
assert_eq!(Opener::of_role(Role::Initiator), Opener::Initiator);
assert_eq!(Opener::of_role(Role::Responder), Opener::Responder);
assert_eq!(Opener::Initiator.peer(), Opener::Responder);
assert_eq!(Opener::Responder.peer(), Opener::Initiator);
}
}