///|
/// 链路参数。时间单位是「帧」(逻辑 tick),60fps 下 1 帧约 16.7ms。
pub(all) struct Link {
delay : Int
jitter : Int
loss_pct : Int
reorder_pct : Int
seed : UInt64
}
///|
pub fn Link::new(
delay : Int,
jitter : Int,
loss_pct : Int,
reorder_pct : Int,
seed : UInt64,
) -> Link {
{
delay: if delay < 0 {
0
} else {
delay
},
jitter: if jitter < 0 {
0
} else {
jitter
},
loss_pct: if loss_pct < 0 {
0
} else if loss_pct > 100 {
100
} else {
loss_pct
},
reorder_pct: if reorder_pct < 0 {
0
} else if reorder_pct > 100 {
100
} else {
reorder_pct
},
seed,
}
}
///|
struct Packet {
arrive_at : Int
payload : Bytes
}
///|
/// 确定性链路:同 seed、同发送序列 -> 同到达序列。
pub struct NetSim {
link : Link
mut seed : UInt64
mut queue : Array[Packet]
mut delivered : Int
mut dropped : Int
}
///|
pub fn NetSim::new(link : Link) -> NetSim {
{ link, seed: link.seed, queue: [], delivered: 0, dropped: 0 }
}
///|
fn NetSim::rand(self : NetSim) -> Int {
self.seed = self.seed * 6364136223846793005UL + 1442695040888963407UL
((self.seed >> 33) & 0x7FFFFFFFUL).to_int()
}
///|
/// 在 `now` 时刻发出一个包。可能被丢弃,否则按 delay + jitter 到达。
pub fn NetSim::send(self : NetSim, now : Int, payload : Bytes) -> Unit {
if self.link.loss_pct > 0 && self.rand() % 100 < self.link.loss_pct {
self.dropped = self.dropped + 1
return
}
let mut d = self.link.delay
if self.link.jitter > 0 {
d = d + self.rand() % (self.link.jitter + 1)
}
if self.link.reorder_pct > 0 && self.rand() % 100 < self.link.reorder_pct {
d = d + 1
}
self.queue.push({ arrive_at: now + d, payload })
}
///|
/// 取出 `now` 时刻已到达的包,按到达时间升序返回(模拟乱序)。
pub fn NetSim::recv(self : NetSim, now : Int) -> Array[Bytes] {
let ready : Array[Packet] = []
let later : Array[Packet] = []
for p in self.queue {
if p.arrive_at <= now {
ready.push(p)
} else {
later.push(p)
}
}
self.queue = later
// 插入排序:同时到达或轻微乱序的包按真实到达顺序交付
let mut i = 1
while i < ready.length() {
let cur = ready[i]
let mut j = i - 1
while j >= 0 && ready[j].arrive_at > cur.arrive_at {
ready[j + 1] = ready[j]
j = j - 1
}
ready[j + 1] = cur
i = i + 1
}
let out : Array[Bytes] = []
for p in ready {
out.push(p.payload)
self.delivered = self.delivered + 1
}
out
}
///|
pub fn NetSim::delivered(self : NetSim) -> Int {
self.delivered
}
///|
pub fn NetSim::dropped(self : NetSim) -> Int {
self.dropped
}
///|
pub fn NetSim::in_flight(self : NetSim) -> Int {
self.queue.length()
}