use std::any::Any;
use std::collections::VecDeque;
use std::io::{self, Read, Write};
use std::net::{TcpListener, TcpStream};
use crate::chip::Place;
use crate::chip::Timeline;
use crate::edge::draw;
use crate::snapshot::{self, Reader, Store, Writer};
use crate::time::{EventId, VirtualNanos};
pub trait Peer: Store + std::fmt::Debug + Send + Any {
fn receive(&mut self, word: u16, context: &mut Timeline<'_, '_>);
fn on_event(&mut self, _event: EventId, _context: &mut Timeline<'_, '_>) {}
fn restarted(&mut self, _context: &mut Timeline<'_, '_>) {}
fn transmit(&mut self, now: VirtualNanos) -> Option<u16>;
fn received(&self) -> &[u16] {
&[]
}
fn forget_after(&mut self, now: VirtualNanos) {
let _ = now;
}
}
impl Place<dyn Peer> {
pub fn lose_while_silent(&mut self, now: VirtualNanos) {
if self.answering().is_none() {
while self.chip_mut().transmit(now).is_some() {
self.begin();
self.release();
}
}
}
}
const HIGH_HALF: u32 = 32;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
#[serde(bound(deserialize = "'de: 'static"))]
pub struct Errors {
pub noise: Rate,
pub framing: Rate,
pub seed: u64,
words: u64,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub struct Rate {
pub name: &'static str,
pub per_million: u32,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Arrival {
pub noise: bool,
pub framing: bool,
pub flipped: u16,
}
impl Errors {
pub fn next(&mut self, bits: u32) -> Arrival {
let count = self.words;
self.words += 1;
let noise = self.noise.drawn(self.seed, count);
let framing = self.framing.drawn(self.seed, count);
let every_pattern = (1_u64 << bits.clamp(1, u16::BITS)) - 1;
let flipped = noise.or(framing).map_or(0, |drawn| {
1 + u16::try_from(drawn % every_pattern).expect("below a u16")
});
Arrival {
noise: noise.is_some(),
framing: framing.is_some(),
flipped,
}
}
}
impl Rate {
pub const EVERY_WORD: u32 = 1_000_000;
fn drawn(self, seed: u64, count: u64) -> Option<u64> {
if self.per_million == 0 {
return None;
}
let drawn = draw(seed, self.name, count);
let out_of_a_million = ((drawn >> HIGH_HALF) * u64::from(Self::EVERY_WORD)) >> HIGH_HALF;
(out_of_a_million < u64::from(self.per_million)).then_some(drawn)
}
}
const CHUNK: usize = 4096;
const LOOK_PERIOD: VirtualNanos = VirtualNanos(1_000_000);
#[derive(Debug)]
pub struct Host {
listener: TcpListener,
stream: Option<TcpStream>,
incoming: VecDeque<u8>,
outgoing: VecDeque<u8>,
sent: u64,
written: u64,
looked: Option<VirtualNanos>,
heard: Vec<(VirtualNanos, Heard)>,
furthest: Option<VirtualNanos>,
}
#[derive(Debug)]
enum Heard {
Connected,
Bytes(Vec<u8>),
}
impl Host {
pub fn listen(port: u16) -> io::Result<Self> {
let listener = TcpListener::bind(("127.0.0.1", port))?;
listener.set_nonblocking(true)?;
Ok(Self {
listener,
stream: None,
incoming: VecDeque::new(),
outgoing: VecDeque::new(),
sent: 0,
written: 0,
looked: None,
heard: Vec::new(),
furthest: None,
})
}
pub fn port(&self) -> io::Result<u16> {
self.listener.local_addr().map(|address| address.port())
}
fn serve(&mut self, now: VirtualNanos) {
let look = self
.looked
.is_none_or(|looked| now.0 >= looked.0.saturating_add(LOOK_PERIOD.0));
if look {
self.looked = Some(now);
if self.furthest.is_some_and(|furthest| now <= furthest) {
self.hear_again(now);
} else {
self.furthest = Some(now);
self.hear(now);
}
}
self.write();
}
fn hear(&mut self, now: VirtualNanos) {
if let Ok((stream, _)) = self.listener.accept()
&& stream.set_nonblocking(true).is_ok()
{
self.stream = Some(stream);
self.incoming.clear();
self.heard.push((now, Heard::Connected));
}
let Some(stream) = &mut self.stream else {
return;
};
let mut buffer = [0; CHUNK];
loop {
match stream.read(&mut buffer) {
Ok(0) => {
self.stream = None;
break;
}
Ok(count) => {
self.incoming.extend(&buffer[..count]);
self.heard
.push((now, Heard::Bytes(buffer[..count].to_vec())));
}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => break,
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
Err(_) => {
self.stream = None;
break;
}
}
}
}
fn hear_again(&mut self, now: VirtualNanos) {
let first = self.heard.partition_point(|&(at, _)| at < now);
for (_, heard) in self.heard[first..].iter().take_while(|&&(at, _)| at == now) {
match heard {
Heard::Connected => self.incoming.clear(),
Heard::Bytes(bytes) => self.incoming.extend(bytes),
}
}
}
fn write(&mut self) {
let mut front = self.sent - self.outgoing.len() as u64;
while front < self.written && self.outgoing.pop_front().is_some() {
front += 1;
}
match &mut self.stream {
None => self.outgoing.clear(),
Some(stream) => {
while let Some(&byte) = self.outgoing.front() {
match stream.write(&[byte]) {
Ok(_) => {
self.outgoing.pop_front();
}
Err(error) if error.kind() == io::ErrorKind::Interrupted => {}
Err(error) if error.kind() == io::ErrorKind::WouldBlock => break,
Err(_) => {
self.stream = None;
self.outgoing.clear();
break;
}
}
}
}
}
let had = self.sent - self.outgoing.len() as u64;
self.written = self.written.max(had);
}
}
impl Store for Host {
fn store(&self, to: &mut Writer) -> Result<(), snapshot::Error> {
to.put(&self.incoming)?;
to.put(&self.outgoing)?;
to.put(&self.sent)?;
to.put(&self.looked)
}
fn load(&mut self, from: &mut Reader) -> Result<(), snapshot::Error> {
self.incoming = from.take()?;
self.outgoing = from.take()?;
self.sent = from.take()?;
self.looked = from.take()?;
Ok(())
}
}
impl Peer for Host {
fn receive(&mut self, word: u16, context: &mut Timeline<'_, '_>) {
self.outgoing
.push_back(u8::try_from(word & 0xFF).expect("masked to a byte"));
self.sent += 1;
self.serve(context.now());
}
fn transmit(&mut self, now: VirtualNanos) -> Option<u16> {
self.serve(now);
self.incoming.pop_front().map(u16::from)
}
fn forget_after(&mut self, now: VirtualNanos) {
self.heard.retain(|&(at, _)| at <= now);
self.furthest = self.furthest.map(|furthest| furthest.min(now));
self.written = self.written.min(self.sent);
}
}
#[cfg(test)]
mod tests {
use super::*;
const BYTE: u32 = 8;
const EVERY: u32 = Rate::EVERY_WORD;
fn errors(noise: u32, framing: u32, seed: u64) -> Errors {
Errors {
noise: Rate {
name: "line.noise_errors",
per_million: noise,
},
framing: Rate {
name: "line.framing_errors",
per_million: framing,
},
seed,
..Errors::default()
}
}
fn arrivals(mut line: Errors, words: usize) -> Vec<Arrival> {
(0..words).map(|_| line.next(BYTE)).collect()
}
#[test]
fn no_rate_is_a_clean_line_and_a_million_errs_every_word_with_some_bit_flipped() {
assert!(
arrivals(errors(0, 0, 9), 1_000)
.iter()
.all(|arrival| *arrival == Arrival::default())
);
let every = arrivals(errors(EVERY, EVERY, 9), 1_000);
let noise_alone = arrivals(errors(EVERY, 0, 9), 1_000);
for (both, noise) in every.iter().zip(&noise_alone) {
assert!(both.noise && both.framing, "{both:?}");
assert!((1..=0xFF).contains(&both.flipped), "{both:?}");
assert_eq!(both.flipped, noise.flipped, "noise picks the bits");
}
}
#[test]
fn the_flipped_bits_stay_inside_the_data_however_wide_it_is() {
for bits in [1, 5, 7, 8, 9, 16] {
let data = (1_u32 << bits) - 1;
let mut line = errors(EVERY, 0, u64::from(bits));
for _ in 0..1_000 {
let flipped = u32::from(line.next(bits).flipped);
assert!(
flipped != 0 && flipped & !data == 0,
"{bits} bits: {flipped:#x}"
);
}
}
}
#[test]
#[cfg_attr(miri, ignore = "a hundred thousand words is beyond Miri")]
fn a_rate_errs_about_that_many_words_and_the_pattern_is_the_seeds_the_names_and_the_counts() {
let noisy = arrivals(errors(100_000, 0, 3), 100_000);
let erred = noisy.iter().filter(|arrival| arrival.noise).count();
assert!((9_500..10_500).contains(&erred), "{erred} in 100 000");
assert_eq!(noisy, arrivals(errors(100_000, 0, 3), 100_000));
assert_ne!(
noisy[..64],
arrivals(errors(100_000, 0, 4), 64),
"another seed, another pattern"
);
let both = arrivals(errors(100_000, 500_000, 3), 64);
assert!(
both.iter()
.zip(&noisy)
.all(|(both, noise)| both.noise == noise.noise),
"one kind's rate never moves the other's pattern"
);
}
}
#[cfg(test)]
mod host_tests {
use std::net::TcpStream;
use super::*;
#[test]
fn the_socket_is_looked_at_once_a_period_and_a_byte_arrives_by_the_next_look() {
let mut host = Host::listen(0).unwrap();
let mut client = TcpStream::connect(("127.0.0.1", host.port().unwrap())).unwrap();
client.write_all(b"a").unwrap();
let mut now = VirtualNanos(0);
let first = loop {
if let Some(word) = host.transmit(now) {
break word;
}
now = VirtualNanos(now.0 + LOOK_PERIOD.0);
assert!(now.0 < 1_000 * LOOK_PERIOD.0, "the byte never arrived");
};
assert_eq!(first, u16::from(b'a'));
let looked_at = now;
client.write_all(b"b").unwrap();
for _ in 0..100 {
assert_eq!(
host.transmit(looked_at),
None,
"within the period the socket is not looked at again"
);
}
let mut now = VirtualNanos(looked_at.0 + LOOK_PERIOD.0);
let second = loop {
if let Some(word) = host.transmit(now) {
break word;
}
now = VirtualNanos(now.0 + LOOK_PERIOD.0);
assert!(
now.0 < looked_at.0 + 1_000 * LOOK_PERIOD.0,
"the byte never arrived"
);
};
assert_eq!(second, u16::from(b'b'));
}
fn send(host: &mut Host, bytes: &[u8], now: VirtualNanos) {
host.outgoing.extend(bytes);
host.sent += bytes.len() as u64;
host.serve(now);
}
fn nothing_more(client: &mut TcpStream) {
client
.set_read_timeout(Some(std::time::Duration::from_millis(100)))
.unwrap();
assert!(client.read(&mut [0]).is_err(), "the host had a byte twice");
}
fn look(look: u64) -> VirtualNanos {
VirtualNanos(look * LOOK_PERIOD.0)
}
fn connected() -> (Host, TcpStream, u64) {
let mut host = Host::listen(0).unwrap();
let client = TcpStream::connect(("127.0.0.1", host.port().unwrap())).unwrap();
let mut at = 0;
while host.stream.is_none() {
host.serve(look(at));
at += 1;
assert!(at < 1_000, "the host was never taken");
}
(host, client, at)
}
fn heard(host: &mut Host, from: u64, count: usize) -> (Vec<(u64, u16)>, u64) {
let mut words = Vec::new();
let mut at = from;
while words.len() < count {
if let Some(word) = host.transmit(look(at)) {
words.push((at, word));
} else {
at += 1;
}
assert!(at < from + 10_000, "only {words:?} came");
}
(words, at + 1)
}
#[test]
fn a_host_heard_after_a_save_is_heard_the_same_after_a_restore() {
let (mut host, mut client, first) = connected();
client.write_all(b"before").unwrap();
let (_, first) = heard(&mut host, first, 1);
let saved = snapshot::stored(&host).unwrap();
let (waiting, next) = heard(&mut host, first, 5);
assert_eq!(
waiting.iter().map(|&(_, word)| word).collect::<Vec<_>>(),
b"efore".map(u16::from),
"\"efore\" was waiting"
);
client.write_all(b"after").unwrap();
send(&mut host, b"sent", look(next));
let (after, last) = heard(&mut host, next, 5);
snapshot::load(&mut host, snapshot::keep(saved)).unwrap();
client.write_all(b"never").unwrap();
assert_eq!(heard(&mut host, first, 5).0, waiting);
send(&mut host, b"sent", look(next));
assert_eq!(heard(&mut host, next, 5).0, after);
let (past, _) = heard(&mut host, last, 5);
assert_eq!(
past.iter().map(|&(_, word)| word).collect::<Vec<_>>(),
b"never".map(u16::from),
"past the last look, the socket again"
);
let mut had = [0; 4];
client.read_exact(&mut had).unwrap();
assert_eq!(&had, b"sent");
nothing_more(&mut client);
}
#[test]
#[cfg_attr(miri, ignore = "eight mebibytes through a socket is beyond Miri")]
#[expect(
clippy::disallowed_methods,
reason = "the host reads on a thread of its own while the run writes"
)]
fn a_slow_host_has_every_byte_the_port_sent() {
const BURST: usize = 8 << 20;
let (mut host, mut client, at) = connected();
let burst: Vec<u8> = (0..=u8::MAX).cycle().take(BURST).collect();
send(&mut host, &burst, look(at));
host.serve(look(at));
assert!(!host.outgoing.is_empty(), "the host has yet to make room");
let reading = std::thread::spawn(move || {
let mut had = vec![0; BURST];
client.read_exact(&mut had).map(|()| had)
});
let mut now = at;
while !host.outgoing.is_empty() {
now += 1;
host.serve(look(now));
assert!(now < at + 1_000_000, "the host never made room");
}
assert!(
reading.join().unwrap().unwrap() == burst,
"every byte, in order"
);
}
#[test]
fn a_new_way_sends_the_host_what_it_sends() {
let (mut host, mut client, first) = connected();
let saved = snapshot::stored(&host).unwrap();
client.write_all(b"old").unwrap();
send(&mut host, b"old", look(first));
let (old, next) = heard(&mut host, first, 3);
snapshot::load(&mut host, snapshot::keep(saved)).unwrap();
host.forget_after(look(first - 1));
send(&mut host, b"new", look(first));
client.write_all(b"new").unwrap();
let (new, _) = heard(&mut host, first, 3);
assert_eq!(
new.iter().map(|&(_, word)| word).collect::<Vec<_>>(),
b"new".map(u16::from),
"the old way's {old:?}, to look {next}, go"
);
let mut had = [0; 6];
client.read_exact(&mut had).unwrap();
assert_eq!(&had, b"oldnew");
nothing_more(&mut client);
}
}