use std::ffi::OsStr;
use std::io::{self, Read, Write};
use std::path::{Path, PathBuf};
use std::process::{Child, ChildStdin, ChildStdout, Command, ExitStatus, Stdio};
use crate::edge::Pulse;
use crate::emulator::{Stop, StopReason};
use crate::time::VirtualNanos;
const LARGEST_FRAME: usize = 1 << 20;
const KIND_ADVANCE: u8 = 1;
const KIND_REACHED: u8 = 2;
const KIND_SHUTDOWN: u8 = 3;
const KIND_REPORT: u8 = 4;
pub trait Wire: Sized {
fn encode(&self, out: &mut Vec<u8>);
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum WireError {
#[error("the frame ends after {have} of the {need} bytes a value needs")]
Short {
have: usize,
need: usize,
},
#[error("{value} is no {what}")]
Invalid {
what: &'static str,
value: u64,
},
#[error("{0} bytes follow the last value of the frame")]
Trailing(usize),
}
fn take<'a>(bytes: &mut &'a [u8], need: usize) -> Result<&'a [u8], WireError> {
if bytes.len() < need {
return Err(WireError::Short {
have: bytes.len(),
need,
});
}
let (taken, rest) = bytes.split_at(need);
*bytes = rest;
Ok(taken)
}
macro_rules! integer_wire {
($($int:ty),*) => {$(
impl Wire for $int {
fn encode(&self, out: &mut Vec<u8>) {
out.extend_from_slice(&self.to_le_bytes());
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
let taken = take(bytes, size_of::<$int>())?;
Ok(<$int>::from_le_bytes(taken.try_into().expect("taken that many")))
}
}
)*};
}
integer_wire!(u8, u16, u32, u64, i32, i64);
impl Wire for bool {
fn encode(&self, out: &mut Vec<u8>) {
u8::from(*self).encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
match u8::decode(bytes)? {
0 => Ok(false),
1 => Ok(true),
value => Err(WireError::Invalid {
what: "bool",
value: u64::from(value),
}),
}
}
}
impl Wire for VirtualNanos {
fn encode(&self, out: &mut Vec<u8>) {
self.0.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
u64::decode(bytes).map(Self)
}
}
impl<T: Wire, const N: usize> Wire for [T; N] {
fn encode(&self, out: &mut Vec<u8>) {
for value in self {
value.encode(out);
}
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
let mut values = Vec::with_capacity(N);
for _ in 0..N {
values.push(T::decode(bytes)?);
}
Ok(values
.try_into()
.unwrap_or_else(|_| unreachable!("decoded N values")))
}
}
impl<T: Wire> Wire for Option<T> {
fn encode(&self, out: &mut Vec<u8>) {
match self {
None => false.encode(out),
Some(value) => {
true.encode(out);
value.encode(out);
}
}
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
if bool::decode(bytes)? {
T::decode(bytes).map(Some)
} else {
Ok(None)
}
}
}
impl<T: Wire> Wire for Vec<T> {
fn encode(&self, out: &mut Vec<u8>) {
u32::try_from(self.len())
.expect("fewer values than a word counts")
.encode(out);
for value in self {
value.encode(out);
}
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
let count = u32::decode(bytes)?;
let mut values = Vec::new();
for _ in 0..count {
values.push(T::decode(bytes)?);
}
Ok(values)
}
}
impl Wire for Pulse {
fn encode(&self, out: &mut Vec<u8>) {
self.width.encode(out);
self.period.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
Ok(Self {
width: Wire::decode(bytes)?,
period: Wire::decode(bytes)?,
})
}
}
impl Wire for StopReason {
fn encode(&self, out: &mut Vec<u8>) {
let code: u8 = match self {
Self::Deadline => 0,
Self::Idle => 1,
Self::Reset => 2,
};
code.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
let code = u8::decode(bytes)?;
Self::from_code(u32::from(code)).ok_or(WireError::Invalid {
what: "stop reason",
value: u64::from(code),
})
}
}
impl Wire for Stop {
fn encode(&self, out: &mut Vec<u8>) {
self.at.encode(out);
self.instruction_count.encode(out);
self.reason.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
Ok(Self {
at: Wire::decode(bytes)?,
instruction_count: Wire::decode(bytes)?,
reason: Wire::decode(bytes)?,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Output {
pub pulse: Option<Pulse>,
pub command: Option<u32>,
}
impl Wire for Output {
fn encode(&self, out: &mut Vec<u8>) {
self.pulse.encode(out);
self.command.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
Ok(Self {
pulse: Wire::decode(bytes)?,
command: Wire::decode(bytes)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Report {
pub resets: Vec<VirtualNanos>,
pub unmodeled: u64,
pub refused: u64,
pub unbacked: u64,
pub unrouted: u64,
}
impl Wire for Report {
fn encode(&self, out: &mut Vec<u8>) {
self.resets.encode(out);
self.unmodeled.encode(out);
self.refused.encode(out);
self.unbacked.encode(out);
self.unrouted.encode(out);
}
fn decode(bytes: &mut &[u8]) -> Result<Self, WireError> {
Ok(Self {
resets: Wire::decode(bytes)?,
unmodeled: Wire::decode(bytes)?,
refused: Wire::decode(bytes)?,
unbacked: Wire::decode(bytes)?,
unrouted: Wire::decode(bytes)?,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Frame {
Advance {
until: VirtualNanos,
signals: Option<Vec<u8>>,
},
Reached {
stop: Stop,
outputs: Vec<Output>,
},
Shutdown,
Report(Report),
}
impl Frame {
fn kind(&self) -> &'static str {
match self {
Self::Advance { .. } => "Advance",
Self::Reached { .. } => "Reached",
Self::Shutdown => "Shutdown",
Self::Report(_) => "Report",
}
}
pub fn write_to(&self, output: &mut impl Write) -> io::Result<()> {
let mut body = Vec::new();
match self {
Self::Advance { until, signals } => {
KIND_ADVANCE.encode(&mut body);
until.encode(&mut body);
signals.encode(&mut body);
}
Self::Reached { stop, outputs } => {
KIND_REACHED.encode(&mut body);
stop.encode(&mut body);
outputs.encode(&mut body);
}
Self::Shutdown => KIND_SHUTDOWN.encode(&mut body),
Self::Report(report) => {
KIND_REPORT.encode(&mut body);
report.encode(&mut body);
}
}
let length = u32::try_from(body.len()).expect("a frame is shorter than a word counts");
output.write_all(&length.to_le_bytes())?;
output.write_all(&body)?;
output.flush()
}
pub fn read_from(input: &mut impl Read) -> Result<Self, LinkError> {
let mut length = [0; 4];
match input.read_exact(&mut length) {
Ok(()) => {}
Err(error) if error.kind() == io::ErrorKind::UnexpectedEof => {
return Err(LinkError::Closed);
}
Err(error) => return Err(LinkError::Io(error)),
}
let length = usize::try_from(u32::from_le_bytes(length)).expect("a word fits");
if length > LARGEST_FRAME {
return Err(LinkError::Wire(WireError::Invalid {
what: "frame length",
value: length as u64,
}));
}
let mut body = vec![0; length];
input.read_exact(&mut body)?;
let mut bytes = &body[..];
let frame = match u8::decode(&mut bytes)? {
KIND_ADVANCE => Self::Advance {
until: Wire::decode(&mut bytes)?,
signals: Wire::decode(&mut bytes)?,
},
KIND_REACHED => Self::Reached {
stop: Wire::decode(&mut bytes)?,
outputs: Wire::decode(&mut bytes)?,
},
KIND_SHUTDOWN => Self::Shutdown,
KIND_REPORT => Self::Report(Wire::decode(&mut bytes)?),
kind => {
return Err(LinkError::Wire(WireError::Invalid {
what: "frame kind",
value: u64::from(kind),
}));
}
};
if !bytes.is_empty() {
return Err(LinkError::Wire(WireError::Trailing(bytes.len())));
}
Ok(frame)
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum LinkError {
#[error("the other side of the link closed it")]
Closed,
#[error(transparent)]
Io(#[from] io::Error),
#[error(transparent)]
Wire(#[from] WireError),
#[error("the link carried {got} where {wanted} was expected")]
Unexpected {
got: &'static str,
wanted: &'static str,
},
#[error("starting {binary}: {source}", binary = binary.display())]
Spawn {
binary: PathBuf,
#[source]
source: io::Error,
},
#[error("the target exited with {0}")]
Exited(ExitStatus),
}
#[derive(Debug)]
pub struct Target {
child: Child,
input: ChildStdout,
output: ChildStdin,
}
impl Target {
pub fn spawn(
binary: &Path,
machine: &str,
firmware: &Path,
arguments: &[impl AsRef<OsStr>],
) -> Result<Self, LinkError> {
let mut child = Command::new(binary)
.args(["run", "--link", "--machine", machine])
.args(arguments)
.arg(firmware)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.spawn()
.map_err(|source| LinkError::Spawn {
binary: binary.to_path_buf(),
source,
})?;
let output = child.stdin.take().expect("piped");
let input = child.stdout.take().expect("piped");
Ok(Self {
child,
input,
output,
})
}
pub fn advance(
&mut self,
until: VirtualNanos,
signals: Option<&impl Wire>,
) -> Result<(Stop, Vec<Output>), LinkError> {
let signals = signals.map(|signals| {
let mut bytes = Vec::new();
signals.encode(&mut bytes);
bytes
});
Frame::Advance { until, signals }.write_to(&mut self.output)?;
match Frame::read_from(&mut self.input)? {
Frame::Reached { stop, outputs } => Ok((stop, outputs)),
other => Err(LinkError::Unexpected {
got: other.kind(),
wanted: "Reached",
}),
}
}
pub fn shutdown(mut self) -> Result<Report, LinkError> {
Frame::Shutdown.write_to(&mut self.output)?;
let report = match Frame::read_from(&mut self.input)? {
Frame::Report(report) => report,
other => {
return Err(LinkError::Unexpected {
got: other.kind(),
wanted: "Report",
});
}
};
let status = self.child.wait()?;
if !status.success() {
return Err(LinkError::Exited(status));
}
Ok(report)
}
}
impl Drop for Target {
fn drop(&mut self) {
if matches!(self.child.try_wait(), Ok(None)) {
self.child.kill().ok();
self.child.wait().ok();
}
}
}
#[derive(Debug)]
pub struct Served<R: Read, W: Write> {
input: R,
output: W,
}
impl Served<io::Stdin, io::Stdout> {
#[must_use]
pub fn stdio() -> Self {
Self {
input: io::stdin(),
output: io::stdout(),
}
}
}
impl<R: Read, W: Write> Served<R, W> {
pub fn over(input: R, output: W) -> Self {
Self { input, output }
}
pub fn receive(&mut self) -> Result<Frame, LinkError> {
match Frame::read_from(&mut self.input)? {
frame @ (Frame::Advance { .. } | Frame::Shutdown) => Ok(frame),
other => Err(LinkError::Unexpected {
got: other.kind(),
wanted: "Advance or Shutdown",
}),
}
}
pub fn reached(&mut self, stop: Stop, outputs: Vec<Output>) -> Result<(), LinkError> {
Ok(Frame::Reached { stop, outputs }.write_to(&mut self.output)?)
}
pub fn report(&mut self, report: Report) -> Result<(), LinkError> {
Ok(Frame::Report(report).write_to(&mut self.output)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn round_trip(frame: &Frame) -> Frame {
let mut bytes = Vec::new();
frame.write_to(&mut bytes).unwrap();
Frame::read_from(&mut &bytes[..]).unwrap()
}
#[test]
fn every_frame_reads_back_as_it_was_written() {
let stop = Stop {
at: VirtualNanos(5_000_000),
instruction_count: 1234,
reason: StopReason::Idle,
};
let frames = [
Frame::Advance {
until: VirtualNanos(5_000_000),
signals: None,
},
Frame::Advance {
until: VirtualNanos(10_000_000),
signals: Some(vec![1, 2, 3]),
},
Frame::Reached {
stop,
outputs: vec![
Output {
pulse: Some(Pulse {
width: VirtualNanos(1_500_000),
period: VirtualNanos(2_500_000),
}),
command: Some(500),
},
Output {
pulse: None,
command: None,
},
],
},
Frame::Shutdown,
Frame::Report(Report {
resets: vec![VirtualNanos(7)],
unmodeled: 1,
refused: 2,
unbacked: 3,
unrouted: 4,
}),
];
for frame in &frames {
assert_eq!(&round_trip(frame), frame);
}
}
#[test]
fn a_short_frame_an_unknown_kind_and_a_trailing_byte_are_refused_and_a_closed_link_is_named() {
assert!(matches!(
Frame::read_from(&mut &[][..]),
Err(LinkError::Closed)
));
let short = [3u8, 0, 0, 0, KIND_ADVANCE, 1, 2];
assert!(matches!(
Frame::read_from(&mut &short[..]),
Err(LinkError::Wire(WireError::Short { .. }))
));
let unknown = [1u8, 0, 0, 0, 9];
assert!(matches!(
Frame::read_from(&mut &unknown[..]),
Err(LinkError::Wire(WireError::Invalid {
what: "frame kind",
value: 9
}))
));
let trailing = [2u8, 0, 0, 0, KIND_SHUTDOWN, 0];
assert!(matches!(
Frame::read_from(&mut &trailing[..]),
Err(LinkError::Wire(WireError::Trailing(1)))
));
}
#[test]
fn the_served_side_takes_only_what_the_engine_may_send() {
let mut bytes = Vec::new();
Frame::Report(Report::default())
.write_to(&mut bytes)
.unwrap();
let mut served = Served::over(&bytes[..], Vec::new());
assert!(matches!(
served.receive(),
Err(LinkError::Unexpected { got: "Report", .. })
));
}
}