Skip to content

Commit 85366c1

Browse files
authored
Erase the const generics from the edge-nal-embassy types (#78)
* Make Udp and Tcp Copy; erase all generics by using a dyn trait for buffers alloc; documentation * Reduce the size of the socket struct * Document the Dns type; make it Copy * Update changelog * Address code review feedback
1 parent 38c680c commit 85366c1

File tree

7 files changed

+395
-200
lines changed

7 files changed

+395
-200
lines changed

edge-nal-embassy/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
* Erase the const generics from the edge-nal-embassy types #78
10+
* Make `Tcp`, `Udp` and `Dns` `Copy`
11+
* Change the TX_SZ and RX_SZ defaults for UDP buffers to 1472 bytes which is a bit smaller yet enough if the overall Ethernet MTU is 1500 bytes (which it typically is; Ethernet MTU might be lower, but is rarely higher)
812
## [0.6.0] - 2025-05-29
913
* Optional `defmt` support via two new features (one has to specify one, or the other, or neither, but not both):
1014
* `log` - uses the `log` crate for all logging

edge-nal-embassy/src/dns.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ use core::net::IpAddr;
22

33
use edge_nal::AddrType;
44

5-
use embassy_net::{
6-
dns::{DnsQueryType, Error},
7-
Stack,
8-
};
5+
use embassy_net::dns::{DnsQueryType, Error};
6+
use embassy_net::Stack;
7+
98
use embedded_io_async::ErrorKind;
109

11-
/// A struct that implements the `Dns` trait from `edge-nal`
10+
/// A type that implements the `Dns` trait from `edge-nal`.
11+
/// It uses the DNS resolver from the provided Embassy networking stack.
12+
///
13+
/// The type is `Copy` and `Clone`, so it can be easily passed around.
14+
#[derive(Copy, Clone)]
1215
pub struct Dns<'a> {
1316
stack: Stack<'a>,
1417
}
@@ -51,6 +54,7 @@ impl edge_nal::Dns for Dns<'_> {
5154
}
5255
}
5356

57+
/// DNS error type
5458
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
5559
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
5660
pub struct DnsError(Error);
@@ -61,7 +65,6 @@ impl From<Error> for DnsError {
6165
}
6266
}
6367

64-
// TODO
6568
impl embedded_io_async::Error for DnsError {
6669
fn kind(&self) -> ErrorKind {
6770
ErrorKind::Other

edge-nal-embassy/src/lib.rs

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub use tcp::*;
1818
#[cfg(feature = "udp")]
1919
pub use udp::*;
2020

21+
use crate::sealed::SealedDynPool;
22+
2123
// This mod MUST go first, so that the others see its macros.
2224
pub(crate) mod fmt;
2325

@@ -28,7 +30,49 @@ mod tcp;
2830
#[cfg(feature = "udp")]
2931
mod udp;
3032

31-
pub(crate) struct Pool<T, const N: usize> {
33+
/// A const-generics-erased trait variant of `Pool`
34+
///
35+
/// Allows for types like `Tcp`, `TcpSocket`, `Udp` and `UdpSocket` that do reference the
36+
/// pool to erase the const-generics set on the Pool object type when used for TCP and UDP buffers.
37+
///
38+
/// To erase the type of the pool itself, these types use `&dyn DynPool<B>`
39+
pub trait DynPool<B>: SealedDynPool<B> {}
40+
41+
impl<T, B> DynPool<B> for &T where T: DynPool<B> {}
42+
43+
mod sealed {
44+
use core::ptr::NonNull;
45+
46+
/// The sealed trait variant of `DynPool`.
47+
pub trait SealedDynPool<B> {
48+
/// Allocate an object from the pool.
49+
///
50+
/// Returns `None` if the pool is exhausted.
51+
fn alloc(&self) -> Option<B>;
52+
53+
/// Free an object back to the pool.
54+
///
55+
/// # Safety
56+
/// - `buffer_token` must be a pointer obtained from `alloc` that hasn't been freed yet.
57+
unsafe fn free(&self, buffer_token: NonNull<u8>);
58+
}
59+
60+
impl<T, B> SealedDynPool<B> for &T
61+
where
62+
T: SealedDynPool<B>,
63+
{
64+
fn alloc(&self) -> Option<B> {
65+
(**self).alloc()
66+
}
67+
68+
unsafe fn free(&self, buffer_token: NonNull<u8>) {
69+
(**self).free(buffer_token)
70+
}
71+
}
72+
}
73+
74+
/// A simple fixed-size pool allocator for `T`.
75+
pub struct Pool<T, const N: usize> {
3276
used: [Cell<bool>; N],
3377
data: [UnsafeCell<MaybeUninit<T>>; N],
3478
}
@@ -39,7 +83,8 @@ impl<T, const N: usize> Pool<T, N> {
3983
#[allow(clippy::declare_interior_mutable_const)]
4084
const UNINIT: UnsafeCell<MaybeUninit<T>> = UnsafeCell::new(MaybeUninit::uninit());
4185

42-
const fn new() -> Self {
86+
/// Create a new pool.
87+
pub const fn new() -> Self {
4388
Self {
4489
used: [Self::VALUE; N],
4590
data: [Self::UNINIT; N],
@@ -48,6 +93,11 @@ impl<T, const N: usize> Pool<T, N> {
4893
}
4994

5095
impl<T, const N: usize> Pool<T, N> {
96+
/// Allocate an object from the pool.
97+
///
98+
/// # Returns
99+
/// - `Some(NonNull<T>)` if an object was successfully allocated.
100+
/// - `None` if the pool is exhausted.
51101
fn alloc(&self) -> Option<NonNull<T>> {
52102
for n in 0..N {
53103
// this can't race because Pool is not Sync.
@@ -60,7 +110,12 @@ impl<T, const N: usize> Pool<T, N> {
60110
None
61111
}
62112

63-
/// safety: p must be a pointer obtained from self.alloc that hasn't been freed yet.
113+
/// Free an object back to the pool.
114+
///
115+
/// Safety: p must be a pointer obtained from `alloc` that hasn't been freed yet.
116+
///
117+
/// # Arguments
118+
/// - `p`: A pointer to the object to free.
64119
unsafe fn free(&self, p: NonNull<T>) {
65120
let origin = self.data.as_ptr() as *mut T;
66121
let n = p.as_ptr().offset_from(origin);
@@ -70,17 +125,20 @@ impl<T, const N: usize> Pool<T, N> {
70125
}
71126
}
72127

128+
/// Convert an embassy-net `IpEndpoint` to a standard library `SocketAddr`.
73129
pub(crate) fn to_net_socket(socket: IpEndpoint) -> SocketAddr {
74130
SocketAddr::new(socket.addr.into(), socket.port)
75131
}
76132

133+
/// Convert an embassy-net `IpListenEndpoint` to a standard library `SocketAddr`.
77134
pub(crate) fn to_emb_socket(socket: SocketAddr) -> Option<IpEndpoint> {
78135
Some(IpEndpoint {
79136
addr: to_emb_addr(socket.ip())?,
80137
port: socket.port(),
81138
})
82139
}
83140

141+
/// Convert a standard library `SocketAddr` to an embassy-net `IpListenEndpoint`.
84142
pub(crate) fn to_emb_bind_socket(socket: SocketAddr) -> Option<IpListenEndpoint> {
85143
let addr = if socket.ip().is_unspecified() {
86144
None
@@ -94,6 +152,7 @@ pub(crate) fn to_emb_bind_socket(socket: SocketAddr) -> Option<IpListenEndpoint>
94152
})
95153
}
96154

155+
/// Convert an embassy-net `IpAddress` to a standard library `IpAddr`.
97156
pub(crate) fn to_emb_addr(addr: IpAddr) -> Option<IpAddress> {
98157
match addr {
99158
#[cfg(feature = "proto-ipv4")]

0 commit comments

Comments
 (0)