aboutsummaryrefslogtreecommitdiff
path: root/embassy-net/src
diff options
context:
space:
mode:
authorCirrus <[email protected]>2024-08-25 13:14:36 -0700
committerCirrus <[email protected]>2024-08-25 13:14:36 -0700
commit9b142bd80fad43beeb1114ec289f570310aae2de (patch)
tree76ddf812740df683db7b0c5006c17953d281bc30 /embassy-net/src
parent1ff1f00d5a1125936d5fd3ba81c0b5c398f4cf33 (diff)
feat(embassy-net): add zero-copy UDP send/recv functions
Added recv_from_with and send_to_with. These are conceptually similar to TCP's read_with and write_with functions. An application can parse received datagrams directly out of the receive buffer or assemble a datagram of known-length directly into the send buffer.
Diffstat (limited to 'embassy-net/src')
-rw-r--r--embassy-net/src/udp.rs63
1 files changed, 63 insertions, 0 deletions
diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs
index 6e50c4e01..1d5360187 100644
--- a/embassy-net/src/udp.rs
+++ b/embassy-net/src/udp.rs
@@ -138,6 +138,35 @@ impl<'a> UdpSocket<'a> {
138 }) 138 })
139 } 139 }
140 140
141 /// Receive a datagram with a zero-copy function.
142 ///
143 /// When no datagram is available, this method will return `Poll::Pending` and
144 /// register the current task to be notified when a datagram is received.
145 ///
146 /// When a datagram is received, this method will call the provided function
147 /// with the number of bytes received and the remote endpoint and return
148 /// `Poll::Ready` with the function's returned value.
149 pub async fn recv_from_with<F, R>(&mut self, f: F) -> R
150 where
151 F: FnOnce(&[u8], UdpMetadata) -> R,
152 {
153 let mut f = Some(f);
154 poll_fn(move |cx| {
155 self.with_mut(|s, _| {
156 match s.recv() {
157 Ok((buffer, endpoint)) => Poll::Ready(unwrap!(f.take())(buffer, endpoint)),
158 Err(udp::RecvError::Truncated) => unreachable!(),
159 Err(udp::RecvError::Exhausted) => {
160 // socket buffer is empty wait until at least one byte has arrived
161 s.register_recv_waker(cx.waker());
162 Poll::Pending
163 }
164 }
165 })
166 })
167 .await
168 }
169
141 /// Send a datagram to the specified remote endpoint. 170 /// Send a datagram to the specified remote endpoint.
142 /// 171 ///
143 /// This method will wait until the datagram has been sent. 172 /// This method will wait until the datagram has been sent.
@@ -181,6 +210,40 @@ impl<'a> UdpSocket<'a> {
181 }) 210 })
182 } 211 }
183 212
213 /// Send a datagram to the specified remote endpoint with a zero-copy function.
214 ///
215 /// This method will wait until the buffer can fit the requested size before
216 /// calling the function to fill its contents.
217 ///
218 /// When the remote endpoint is not reachable, this method will return `Err(SendError::NoRoute)`
219 pub async fn send_to_with<T, F, R>(&mut self, size: usize, remote_endpoint: T, f: F) -> Result<R, SendError>
220 where
221 T: Into<UdpMetadata> + Copy,
222 F: FnOnce(&mut [u8]) -> R,
223 {
224 let mut f = Some(f);
225 poll_fn(move |cx| {
226 self.with_mut(|s, _| {
227 match s.send(size, remote_endpoint) {
228 Ok(buffer) => Poll::Ready(Ok(unwrap!(f.take())(buffer))),
229 Err(udp::SendError::BufferFull) => {
230 s.register_send_waker(cx.waker());
231 Poll::Pending
232 }
233 Err(udp::SendError::Unaddressable) => {
234 // If no sender/outgoing port is specified, there is not really "no route"
235 if s.endpoint().port == 0 {
236 Poll::Ready(Err(SendError::SocketNotBound))
237 } else {
238 Poll::Ready(Err(SendError::NoRoute))
239 }
240 }
241 }
242 })
243 })
244 .await
245 }
246
184 /// Returns the local endpoint of the socket. 247 /// Returns the local endpoint of the socket.
185 pub fn endpoint(&self) -> IpListenEndpoint { 248 pub fn endpoint(&self) -> IpListenEndpoint {
186 self.with(|s, _| s.endpoint()) 249 self.with(|s, _| s.endpoint())