use std::{
cmp::Ordering,
collections::{HashMap, HashSet},
net::SocketAddr,
};
use time::OffsetDateTime;
use crate::core::{
id::Id,
message::{Message, Response},
routing_table::RoutingTable,
traits::ProcessData,
};
type ConnId = usize;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnState {
Connected,
Disconnected,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum StreamState {
Closed,
Uni,
Bi,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct QuicMeta {
addr: SocketAddr,
conn_id: Option<ConnId>,
conn_state: ConnState,
stream_state: StreamState,
last_seen: Option<OffsetDateTime>,
}
impl QuicMeta {
fn new(
addr: SocketAddr,
conn_id: Option<ConnId>,
conn_state: ConnState,
stream_state: StreamState,
last_seen: Option<OffsetDateTime>,
) -> Self {
Self {
addr,
conn_id,
conn_state,
stream_state,
last_seen,
}
}
}
pub struct QuicRouter {
rt: RoutingTable<ConnId, QuicMeta>,
}
impl QuicRouter {
pub fn new(local_id: Id, max_bucket_size: u8, k: u8) -> Self {
Self {
rt: RoutingTable {
local_id,
max_bucket_size,
k,
buckets: HashMap::new(),
peer_list: HashMap::new(),
id_list: HashMap::new(),
},
}
}
pub fn local_id(&self) -> Id {
self.rt.local_id
}
pub fn peer_meta(&self, id: &Id) -> Option<QuicMeta> {
self.rt.peer_list.get(id).copied()
}
pub fn insert(&mut self, id: Id, addr: SocketAddr) -> bool {
if id == self.rt.local_id {
return false;
}
self.rt
.peer_list
.entry(id)
.and_modify(|meta| {
if matches!(meta.conn_state, ConnState::Disconnected) {
meta.addr = addr;
}
})
.or_insert_with(|| {
QuicMeta::new(
addr,
None,
ConnState::Disconnected,
StreamState::Closed,
None,
)
});
true
}
pub fn can_connect(&mut self, id: Id) -> (bool, Option<u32>) {
let i = match self.rt.local_id.log2_distance(&id) {
Some(i) => i,
None => return (false, None),
};
let bucket = self.rt.buckets.entry(i).or_insert_with(HashSet::new);
match bucket.len().cmp(&self.rt.max_bucket_size.into()) {
Ordering::Less => {
(true, Some(i))
}
Ordering::Equal => {
(false, None)
}
Ordering::Greater => {
unreachable!()
}
}
}
pub fn set_connected(&mut self, id: Id, conn_id: ConnId, stream_state: StreamState) -> bool {
match self.can_connect(id) {
(true, Some(i)) => {
if let (Some(quic_meta), Some(bucket)) =
(self.rt.peer_list.get_mut(&id), self.rt.buckets.get_mut(&i))
{
let _res = bucket.insert(id);
debug_assert!(_res);
self.rt.id_list.insert(conn_id, id);
quic_meta.conn_id = Some(conn_id);
quic_meta.conn_state = ConnState::Connected;
quic_meta.last_seen = Some(OffsetDateTime::now_utc());
}
true
}
_ => false,
}
}
pub fn set_disconnected(&mut self, conn_id: ConnId) -> bool {
todo!()
}
pub fn select_broadcast_peers(&self, height: u32) -> Option<Vec<(u32, SocketAddr)>> {
todo!()
}
pub fn process_message<S: Clone, T: ProcessData<S>>(
&mut self,
state: S,
message: Message,
conn_id: ConnId,
) -> Option<Response> {
todo!()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn localhost_with_port(port: u16) -> SocketAddr {
format!("127.0.0.1:{}", port).parse().unwrap()
}
#[test]
fn insert() {
let mut router = QuicRouter::new(Id::from_u16(0), 1, 20);
assert!(router.insert(Id::from_u16(1), localhost_with_port(1)));
assert!(router.insert(Id::from_u16(2), localhost_with_port(2)));
assert!(router.insert(Id::from_u16(3), localhost_with_port(3)));
}
#[test]
fn insert_defaults() {
let mut router = QuicRouter::new(Id::from_u16(0), 1, 20);
let id = Id::from_u16(1);
let addr = localhost_with_port(1);
assert!(router.insert(id, addr));
let meta = router.peer_meta(&id).unwrap();
assert_eq!(meta.addr, addr);
assert_eq!(meta.conn_id, None);
assert_eq!(meta.conn_state, ConnState::Disconnected);
assert_eq!(meta.stream_state, StreamState::Closed);
assert_eq!(meta.last_seen, None);
}
#[test]
fn insert_self() {
let mut router = QuicRouter::new(Id::from_u16(0), 1, 20);
assert!(!router.insert(router.local_id(), localhost_with_port(0)));
}
#[test]
fn insert_duplicate() {
let mut router = QuicRouter::new(Id::from_u16(0), 1, 20);
assert!(router.insert(Id::from_u16(1), localhost_with_port(1)));
assert!(router.insert(Id::from_u16(1), localhost_with_port(1)));
}
#[test]
fn insert_duplicate_updates_addr() {
let mut router = QuicRouter::new(Id::from_u16(0), 1, 20);
let id = Id::from_u16(1);
let addr = localhost_with_port(1);
assert!(router.insert(id, addr));
assert_eq!(router.peer_meta(&id).unwrap().addr, addr);
let id = Id::from_u16(1);
let addr = localhost_with_port(2);
assert!(router.insert(id, addr));
assert_eq!(router.peer_meta(&id).unwrap().addr, addr);
}
}