Skip to content

Commit

Permalink
GAT-based Device trait.
Browse files Browse the repository at this point in the history
The current `'a` lifetime in the `Device` trait is essentially a workaround for lack of GATs.
I'm just experimenting how this would look like, it'll have to wait until GATs are stable to go in.

The main benefit is structs implementing `Device` can now borrow stuff. This wasn't possible
before because the `for<'d> T: Device<'d>` bounds would essentially imply `T: 'static`.
  • Loading branch information
Dirbaio committed Nov 24, 2021
1 parent 0dfe66b commit cf45e77
Show file tree
Hide file tree
Showing 14 changed files with 105 additions and 85 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
on:
push:
branches: [ staging, trying ]
branches: [staging, trying]
pull_request:

name: Test
Expand All @@ -20,8 +20,8 @@ jobs:
# Test on stable, MSRV 1.46, and nightly.
# Failure is permitted on nightly.
rust:
- stable
- 1.56.0
#- stable
#- 1.56.0
- nightly

features:
Expand Down Expand Up @@ -67,8 +67,8 @@ jobs:
# Test on stable, MSRV 1.46, and nightly.
# Failure is permitted on nightly.
rust:
- stable
- 1.56.0
#- stable
#- 1.56.0
- nightly

features:
Expand Down
5 changes: 1 addition & 4 deletions examples/dhcp_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,7 @@ fn main() {
}
}

fn set_ipv4_addr<DeviceT>(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr)
where
DeviceT: for<'d> Device<'d>,
{
fn set_ipv4_addr<DeviceT: Device>(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr) {
iface.update_ip_addrs(|addrs| {
let dest = addrs.iter_mut().next().unwrap();
*dest = IpCidr::Ipv4(cidr);
Expand Down
2 changes: 1 addition & 1 deletion examples/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ pub fn parse_middleware_options<D>(
loopback: bool,
) -> FaultInjector<Tracer<PcapWriter<D, Box<dyn io::Write>>>>
where
D: for<'a> Device<'a>,
D: Device,
{
let drop_chance = matches
.opt_str("drop-chance")
Expand Down
2 changes: 1 addition & 1 deletion fuzz/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub fn parse_middleware_options<D>(
loopback: bool,
) -> FaultInjector<Tracer<PcapWriter<D, Box<dyn Write>>>>
where
D: for<'a> Device<'a>,
D: Device,
{
let drop_chance = matches
.opt_str("drop-chance")
Expand Down
14 changes: 4 additions & 10 deletions src/iface/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{Error, Result};
/// The network interface logically owns a number of other data structures; to avoid
/// a dependency on heap allocation, it instead owns a `BorrowMut<[T]>`, which can be
/// a `&mut [T]`, or `Vec<T>` if a heap is available.
pub struct Interface<'a, DeviceT: for<'d> Device<'d>> {
pub struct Interface<'a, DeviceT: Device> {
device: DeviceT,
sockets: SocketSet<'a>,
inner: InterfaceInner<'a>,
Expand Down Expand Up @@ -53,7 +53,7 @@ struct InterfaceInner<'a> {
}

/// A builder structure used for creating a network interface.
pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> {
pub struct InterfaceBuilder<'a, DeviceT: Device> {
device: DeviceT,
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
hardware_addr: Option<HardwareAddress>,
Expand All @@ -73,10 +73,7 @@ pub struct InterfaceBuilder<'a, DeviceT: for<'d> Device<'d>> {
ipv4_multicast_groups: ManagedMap<'a, Ipv4Address, ()>,
}

impl<'a, DeviceT> InterfaceBuilder<'a, DeviceT>
where
DeviceT: for<'d> Device<'d>,
{
impl<'a, DeviceT: Device> InterfaceBuilder<'a, DeviceT> {
/// Create a builder used for creating a network interface using the
/// given device and address.
#[cfg_attr(
Expand Down Expand Up @@ -465,10 +462,7 @@ enum IgmpReportState {
},
}

impl<'a, DeviceT> Interface<'a, DeviceT>
where
DeviceT: for<'d> Device<'d>,
{
impl<'a, DeviceT: Device> Interface<'a, DeviceT> {
/// Add a socket to the interface, and return its handle.
///
/// # Panics
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
),
deny(unused)
)]
#![feature(generic_associated_types)]

//! The _smoltcp_ library is built in a layered structure, with the layers corresponding
//! to the levels of API abstraction. Only the highest layers would be used by a typical
Expand Down
23 changes: 13 additions & 10 deletions src/phy/fault_injector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,13 @@ impl State {
/// adverse network conditions (such as random packet loss or corruption), or software
/// or hardware limitations (such as a limited number or size of usable network buffers).
#[derive(Debug)]
pub struct FaultInjector<D: for<'a> Device<'a>> {
pub struct FaultInjector<D: Device> {
inner: D,
state: RefCell<State>,
config: Config,
}

impl<D: for<'a> Device<'a>> FaultInjector<D> {
impl<D: Device> FaultInjector<D> {
/// Create a fault injector device, using the given random number generator seed.
pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
let state = State {
Expand Down Expand Up @@ -195,12 +195,15 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
}
}

impl<'a, D> Device<'a> for FaultInjector<D>
where
D: for<'b> Device<'b>,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>;
impl<D: Device> Device for FaultInjector<D> {
type RxToken<'a>
where
Self: 'a,
= RxToken<'a, D::RxToken<'a>>;
type TxToken<'a>
where
Self: 'a,
= TxToken<'a, D::TxToken<'a>>;

fn capabilities(&self) -> DeviceCapabilities {
let mut caps = self.inner.capabilities();
Expand All @@ -210,7 +213,7 @@ where
caps
}

fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let &mut Self {
ref mut inner,
ref state,
Expand All @@ -233,7 +236,7 @@ where
})
}

fn transmit(&'a mut self) -> Option<Self::TxToken> {
fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
let &mut Self {
ref mut inner,
ref state,
Expand Down
25 changes: 15 additions & 10 deletions src/phy/fuzz_injector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ pub trait Fuzzer {
#[allow(unused)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
pub struct FuzzInjector<D: Device, FTx: Fuzzer, FRx: Fuzzer> {
inner: D,
fuzz_tx: FTx,
fuzz_rx: FRx,
}

#[allow(unused)]
impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx> {
impl<D: Device, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx> {
/// Create a fuzz injector device.
pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector<D, FTx, FRx> {
FuzzInjector {
Expand All @@ -42,14 +42,19 @@ impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx>
}
}

impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
impl<D: Device, FTx, FRx> Device for FuzzInjector<D, FTx, FRx>
where
D: for<'b> Device<'b>,
FTx: Fuzzer + 'a,
FRx: Fuzzer + 'a,
FTx: Fuzzer,
FRx: Fuzzer,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, FRx>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, FTx>;
type RxToken<'a>
where
Self: 'a,
= RxToken<'a, D::RxToken<'a>, FRx>;
type TxToken<'a>
where
Self: 'a,
= TxToken<'a, D::TxToken<'a>, FTx>;

fn capabilities(&self) -> DeviceCapabilities {
let mut caps = self.inner.capabilities();
Expand All @@ -59,7 +64,7 @@ where
caps
}

fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let &mut Self {
ref mut inner,
ref fuzz_rx,
Expand All @@ -78,7 +83,7 @@ where
})
}

fn transmit(&'a mut self) -> Option<Self::TxToken> {
fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
let &mut Self {
ref mut inner,
fuzz_rx: _,
Expand Down
10 changes: 5 additions & 5 deletions src/phy/loopback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ impl Loopback {
}
}

impl<'a> Device<'a> for Loopback {
type RxToken = RxToken;
type TxToken = TxToken<'a>;
impl Device for Loopback {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken<'a>;

fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities {
Expand All @@ -41,7 +41,7 @@ impl<'a> Device<'a> for Loopback {
}
}

fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
self.queue.pop_front().map(move |buffer| {
let rx = RxToken { buffer };
let tx = TxToken {
Expand All @@ -51,7 +51,7 @@ impl<'a> Device<'a> for Loopback {
})
}

fn transmit(&'a mut self) -> Option<Self::TxToken> {
fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
Some(TxToken {
queue: &mut self.queue,
})
Expand Down
26 changes: 16 additions & 10 deletions src/phy/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ An implementation of the [Device](trait.Device.html) trait for a simple hardware
Ethernet controller could look as follows:
```rust
# #![feature(generic_associated_types)]
use smoltcp::Result;
use smoltcp::phy::{self, DeviceCapabilities, Device, Medium};
use smoltcp::time::Instant;
Expand All @@ -38,16 +40,16 @@ impl<'a> StmPhy {
}
}
impl<'a> phy::Device<'a> for StmPhy {
type RxToken = StmPhyRxToken<'a>;
type TxToken = StmPhyTxToken<'a>;
impl phy::Device for StmPhy {
type RxToken<'a> where Self: 'a = StmPhyRxToken<'a>;
type TxToken<'a> where Self: 'a = StmPhyTxToken<'a>;
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
Some((StmPhyRxToken(&mut self.rx_buffer[..]),
StmPhyTxToken(&mut self.tx_buffer[..])))
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
Some(StmPhyTxToken(&mut self.tx_buffer[..]))
}
Expand Down Expand Up @@ -312,20 +314,24 @@ impl Default for Medium {
/// The interface is based on _tokens_, which are types that allow to receive/transmit a
/// single packet. The `receive` and `transmit` functions only construct such tokens, the
/// real sending/receiving operation are performed when the tokens are consumed.
pub trait Device<'a> {
type RxToken: RxToken + 'a;
type TxToken: TxToken + 'a;
pub trait Device {
type RxToken<'a>: RxToken
where
Self: 'a;
type TxToken<'a>: TxToken
where
Self: 'a;

/// Construct a token pair consisting of one receive token and one transmit token.
///
/// The additional transmit token makes it possible to generate a reply packet based
/// on the contents of the received packet. For example, this makes it possible to
/// handle arbitrarily large ICMP echo ("ping") requests, where the all received bytes
/// need to be sent back, without heap allocation.
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)>;
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;

/// Construct a transmit token.
fn transmit(&'a mut self) -> Option<Self::TxToken>;
fn transmit(&mut self) -> Option<Self::TxToken<'_>>;

/// Get a description of device capabilities.
fn capabilities(&self) -> DeviceCapabilities;
Expand Down
23 changes: 14 additions & 9 deletions src/phy/pcap_writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,15 @@ impl<T: Write> PcapSink for T {
#[derive(Debug)]
pub struct PcapWriter<D, S>
where
D: for<'a> Device<'a>,
D: Device,
S: PcapSink,
{
lower: D,
sink: RefCell<S>,
mode: PcapMode,
}

impl<D: for<'a> Device<'a>, S: PcapSink> PcapWriter<D, S> {
impl<D: Device, S: PcapSink> PcapWriter<D, S> {
/// Creates a packet capture writer.
pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter<D, S> {
let medium = lower.capabilities().medium;
Expand Down Expand Up @@ -162,19 +162,24 @@ impl<D: for<'a> Device<'a>, S: PcapSink> PcapWriter<D, S> {
}
}

impl<'a, D, S> Device<'a> for PcapWriter<D, S>
impl<D: Device, S> Device for PcapWriter<D, S>
where
D: for<'b> Device<'b>,
S: PcapSink + 'a,
S: PcapSink,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, S>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, S>;
type RxToken<'a>
where
Self: 'a,
= RxToken<'a, D::RxToken<'a>, S>;
type TxToken<'a>
where
Self: 'a,
= TxToken<'a, D::TxToken<'a>, S>;

fn capabilities(&self) -> DeviceCapabilities {
self.lower.capabilities()
}

fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
fn receive(&mut self) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> {
let sink = &self.sink;
let mode = self.mode;
self.lower.receive().map(move |(rx_token, tx_token)| {
Expand All @@ -192,7 +197,7 @@ where
})
}

fn transmit(&'a mut self) -> Option<Self::TxToken> {
fn transmit(&mut self) -> Option<Self::TxToken<'_>> {
let sink = &self.sink;
let mode = self.mode;
self.lower
Expand Down

0 comments on commit cf45e77

Please sign in to comment.