First commit
This commit is contained in:
14
embassy/Cargo.toml
Normal file
14
embassy/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "embassy"
|
||||
version = "0.1.0"
|
||||
authors = ["Dario Nieuwenhuis <dirbaio@dirbaio.net>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
std = []
|
||||
|
||||
[dependencies]
|
||||
defmt = "0.1.0"
|
||||
cortex-m = "0.6.3"
|
||||
futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] }
|
||||
pin-project = { version = "0.4.23", default-features = false }
|
51
embassy/src/flash.rs
Normal file
51
embassy/src/flash.rs
Normal file
@ -0,0 +1,51 @@
|
||||
|
||||
use core::future::Future;
|
||||
|
||||
#[derive(defmt::Format, Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum Error {
|
||||
Failed,
|
||||
AddressMisaligned,
|
||||
BufferMisaligned,
|
||||
|
||||
_NonExhaustive,
|
||||
}
|
||||
|
||||
pub trait Flash {
|
||||
type ReadFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
type WriteFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
type ErasePageFuture<'a>: Future<Output = Result<(), Error>>;
|
||||
|
||||
/// Reads data from the flash device.
|
||||
///
|
||||
/// address must be a multiple of self.read_size().
|
||||
/// buf.len() must be a multiple of self.read_size().
|
||||
fn read<'a>(&'a mut self, address: usize, buf: &'a mut [u8]) -> Self::ReadFuture<'a>;
|
||||
|
||||
/// Writes data to the flash device.
|
||||
///
|
||||
/// address must be a multiple of self.write_size().
|
||||
/// buf.len() must be a multiple of self.write_size().
|
||||
fn write<'a>(&'a mut self, address: usize, buf: &'a [u8]) -> Self::WriteFuture<'a>;
|
||||
|
||||
/// Erases a single page from the flash device.
|
||||
///
|
||||
/// address must be a multiple of self.erase_size().
|
||||
fn erase<'a>(&'a mut self, address: usize) -> Self::ErasePageFuture<'a>;
|
||||
|
||||
/// Returns the total size, in bytes.
|
||||
/// This is not guaranteed to be a power of 2.
|
||||
fn size(&self) -> usize;
|
||||
|
||||
/// Returns the read size in bytes.
|
||||
/// This is guaranteed to be a power of 2.
|
||||
fn read_size(&self) -> usize;
|
||||
|
||||
/// Returns the write size in bytes.
|
||||
/// This is guaranteed to be a power of 2.
|
||||
fn write_size(&self) -> usize;
|
||||
|
||||
/// Returns the erase size in bytes.
|
||||
/// This is guaranteed to be a power of 2.
|
||||
fn erase_size(&self) -> usize;
|
||||
}
|
||||
|
133
embassy/src/io/error.rs
Normal file
133
embassy/src/io/error.rs
Normal file
@ -0,0 +1,133 @@
|
||||
#[cfg(feature = "std")]
|
||||
use core::convert::From;
|
||||
#[cfg(feature = "std")]
|
||||
use futures::io;
|
||||
|
||||
/// Categories of errors that can occur.
|
||||
///
|
||||
/// This list is intended to grow over time and it is not recommended to
|
||||
/// exhaustively match against it.
|
||||
#[derive(defmt::Format, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Error {
|
||||
/// An entity was not found, often a file.
|
||||
NotFound,
|
||||
/// The operation lacked the necessary privileges to complete.
|
||||
PermissionDenied,
|
||||
/// The connection was refused by the remote server.
|
||||
ConnectionRefused,
|
||||
/// The connection was reset by the remote server.
|
||||
ConnectionReset,
|
||||
/// The connection was aborted (terminated) by the remote server.
|
||||
ConnectionAborted,
|
||||
/// The network operation failed because it was not connected yet.
|
||||
NotConnected,
|
||||
/// A socket address could not be bound because the address is already in
|
||||
/// use elsewhere.
|
||||
AddrInUse,
|
||||
/// A nonexistent interface was requested or the requested address was not
|
||||
/// local.
|
||||
AddrNotAvailable,
|
||||
/// The operation failed because a pipe was closed.
|
||||
BrokenPipe,
|
||||
/// An entity already exists, often a file.
|
||||
AlreadyExists,
|
||||
/// The operation needs to block to complete, but the blocking operation was
|
||||
/// requested to not occur.
|
||||
WouldBlock,
|
||||
/// A parameter was incorrect.
|
||||
InvalidInput,
|
||||
/// Data not valid for the operation were encountered.
|
||||
///
|
||||
/// Unlike [`InvalidInput`], this typically means that the operation
|
||||
/// parameters were valid, however the error was caused by malformed
|
||||
/// input data.
|
||||
///
|
||||
/// For example, a function that reads a file into a string will error with
|
||||
/// `InvalidData` if the file's contents are not valid UTF-8.
|
||||
///
|
||||
/// [`InvalidInput`]: #variant.InvalidInput
|
||||
InvalidData,
|
||||
/// The I/O operation's timeout expired, causing it to be canceled.
|
||||
TimedOut,
|
||||
/// An error returned when an operation could not be completed because a
|
||||
/// call to [`write`] returned [`Ok(0)`].
|
||||
///
|
||||
/// This typically means that an operation could only succeed if it wrote a
|
||||
/// particular number of bytes but only a smaller number of bytes could be
|
||||
/// written.
|
||||
///
|
||||
/// [`write`]: ../../std/io/trait.Write.html#tymethod.write
|
||||
/// [`Ok(0)`]: ../../std/io/type.Result.html
|
||||
WriteZero,
|
||||
/// This operation was interrupted.
|
||||
///
|
||||
/// Interrupted operations can typically be retried.
|
||||
Interrupted,
|
||||
|
||||
/// An error returned when an operation could not be completed because an
|
||||
/// "end of file" was reached prematurely.
|
||||
///
|
||||
/// This typically means that an operation could only succeed if it read a
|
||||
/// particular number of bytes but only a smaller number of bytes could be
|
||||
/// read.
|
||||
UnexpectedEof,
|
||||
|
||||
/// An operation would have read more data if the given buffer was large.
|
||||
///
|
||||
/// This typically means that the buffer has been filled with the first N bytes
|
||||
/// of the read data.
|
||||
Truncated,
|
||||
|
||||
/// Any I/O error not part of this list.
|
||||
Other,
|
||||
}
|
||||
|
||||
pub type Result<T> = core::result::Result<T, Error>;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Error {
|
||||
match err.kind() {
|
||||
io::ErrorKind::NotFound => Error::NotFound,
|
||||
io::ErrorKind::PermissionDenied => Error::PermissionDenied,
|
||||
io::ErrorKind::ConnectionRefused => Error::ConnectionRefused,
|
||||
io::ErrorKind::ConnectionReset => Error::ConnectionReset,
|
||||
io::ErrorKind::ConnectionAborted => Error::ConnectionAborted,
|
||||
io::ErrorKind::NotConnected => Error::NotConnected,
|
||||
io::ErrorKind::AddrInUse => Error::AddrInUse,
|
||||
io::ErrorKind::AddrNotAvailable => Error::AddrNotAvailable,
|
||||
io::ErrorKind::BrokenPipe => Error::BrokenPipe,
|
||||
io::ErrorKind::AlreadyExists => Error::AlreadyExists,
|
||||
io::ErrorKind::WouldBlock => Error::WouldBlock,
|
||||
io::ErrorKind::InvalidInput => Error::InvalidInput,
|
||||
io::ErrorKind::InvalidData => Error::InvalidData,
|
||||
io::ErrorKind::TimedOut => Error::TimedOut,
|
||||
io::ErrorKind::WriteZero => Error::WriteZero,
|
||||
io::ErrorKind::Interrupted => Error::Interrupted,
|
||||
io::ErrorKind::UnexpectedEof => Error::UnexpectedEof,
|
||||
_ => Error::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/*
|
||||
impl From<smoltcp::Error> for Error {
|
||||
fn from(err: smoltcp::Error) -> Error {
|
||||
match err {
|
||||
smoltcp::Error::Exhausted => Error::Exhausted,
|
||||
smoltcp::Error::Illegal => Error::Illegal,
|
||||
smoltcp::Error::Unaddressable => Error::Unaddressable,
|
||||
smoltcp::Error::Truncated => Error::Truncated,
|
||||
smoltcp::Error::Checksum => Error::Checksum,
|
||||
smoltcp::Error::Unrecognized => Error::Unrecognized,
|
||||
smoltcp::Error::Fragmented => Error::Fragmented,
|
||||
smoltcp::Error::Malformed => Error::Malformed,
|
||||
smoltcp::Error::Dropped => Error::Dropped,
|
||||
_ => Error::Other,
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
7
embassy/src/io/mod.rs
Normal file
7
embassy/src/io/mod.rs
Normal file
@ -0,0 +1,7 @@
|
||||
mod error;
|
||||
mod traits;
|
||||
mod util;
|
||||
|
||||
pub use self::error::*;
|
||||
pub use self::traits::*;
|
||||
pub use self::util::*;
|
197
embassy/src/io/traits.rs
Normal file
197
embassy/src/io/traits.rs
Normal file
@ -0,0 +1,197 @@
|
||||
|
||||
use core::ops::DerefMut;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
use alloc::boxed::Box;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use futures::io as std_io;
|
||||
|
||||
use super::error::Result;
|
||||
|
||||
/// Read bytes asynchronously.
|
||||
///
|
||||
/// This trait is analogous to the `std::io::BufRead` trait, but integrates
|
||||
/// with the asynchronous task system. In particular, the `poll_fill_buf`
|
||||
/// method, unlike `BufRead::fill_buf`, will automatically queue the current task
|
||||
/// for wakeup and return if data is not yet available, rather than blocking
|
||||
/// the calling thread.
|
||||
pub trait AsyncBufRead {
|
||||
/// Attempt to return the contents of the internal buffer, filling it with more data
|
||||
/// from the inner reader if it is empty.
|
||||
///
|
||||
/// On success, returns `Poll::Ready(Ok(buf))`.
|
||||
///
|
||||
/// If no data is available for reading, the method returns
|
||||
/// `Poll::Pending` and arranges for the current task (via
|
||||
/// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
|
||||
/// readable or is closed.
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`consume`] method to function properly. When calling this
|
||||
/// method, none of the contents will be "read" in the sense that later
|
||||
/// calling [`poll_read`] may return the same contents. As such, [`consume`] must
|
||||
/// be called with the number of bytes that are consumed from this buffer to
|
||||
/// ensure that the bytes are never returned twice.
|
||||
///
|
||||
/// [`poll_read`]: AsyncBufRead::poll_read
|
||||
/// [`consume`]: AsyncBufRead::consume
|
||||
///
|
||||
/// An empty buffer returned indicates that the stream has reached EOF.
|
||||
///
|
||||
/// # Implementation
|
||||
///
|
||||
/// This function may not return errors of kind `WouldBlock` or
|
||||
/// `Interrupted`. Implementations must convert `WouldBlock` into
|
||||
/// `Poll::Pending` and either internally retry or convert
|
||||
/// `Interrupted` into another error kind.
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>>;
|
||||
|
||||
/// Tells this buffer that `amt` bytes have been consumed from the buffer,
|
||||
/// so they should no longer be returned in calls to [`poll_read`].
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`poll_fill_buf`] method to function properly. This function does
|
||||
/// not perform any I/O, it simply informs this object that some amount of
|
||||
/// its buffer, returned from [`poll_fill_buf`], has been consumed and should
|
||||
/// no longer be returned. As such, this function may do odd things if
|
||||
/// [`poll_fill_buf`] isn't called before calling it.
|
||||
///
|
||||
/// The `amt` must be `<=` the number of bytes in the buffer returned by
|
||||
/// [`poll_fill_buf`].
|
||||
///
|
||||
/// [`poll_read`]: AsyncBufRead::poll_read
|
||||
/// [`poll_fill_buf`]: AsyncBufRead::poll_fill_buf
|
||||
fn consume(self: Pin<&mut Self>, amt: usize);
|
||||
}
|
||||
|
||||
/// Write bytes asynchronously.
|
||||
///
|
||||
/// This trait is analogous to the `core::io::Write` trait, but integrates
|
||||
/// with the asynchronous task system. In particular, the `poll_write`
|
||||
/// method, unlike `Write::write`, will automatically queue the current task
|
||||
/// for wakeup and return if the writer cannot take more data, rather than blocking
|
||||
/// the calling thread.
|
||||
pub trait AsyncWrite {
|
||||
/// Attempt to write bytes from `buf` into the object.
|
||||
///
|
||||
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
|
||||
///
|
||||
/// If the object is not ready for writing, the method returns
|
||||
/// `Poll::Pending` and arranges for the current task (via
|
||||
/// `cx.waker().wake_by_ref()`) to receive a notification when the object becomes
|
||||
/// writable or is closed.
|
||||
///
|
||||
/// # Implementation
|
||||
///
|
||||
/// This function may not return errors of kind `WouldBlock` or
|
||||
/// `Interrupted`. Implementations must convert `WouldBlock` into
|
||||
/// `Poll::Pending` and either internally retry or convert
|
||||
/// `Interrupted` into another error kind.
|
||||
///
|
||||
/// `poll_write` must try to make progress by flushing the underlying object if
|
||||
/// that is the only way the underlying object can become writable again.
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>>;
|
||||
}
|
||||
|
||||
macro_rules! defer_async_read {
|
||||
() => {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
||||
Pin::new(&mut **self.get_mut()).poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
||||
Pin::new(&mut **self).consume(amt)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for Box<T> {
|
||||
defer_async_read!();
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncBufRead + Unpin> AsyncBufRead for &mut T {
|
||||
defer_async_read!();
|
||||
}
|
||||
|
||||
impl<P> AsyncBufRead for Pin<P>
|
||||
where
|
||||
P: DerefMut + Unpin,
|
||||
P::Target: AsyncBufRead,
|
||||
{
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
||||
self.get_mut().as_mut().poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
self.get_mut().as_mut().consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! deref_async_write {
|
||||
() => {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize>> {
|
||||
Pin::new(&mut **self).poll_write(cx, buf)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for Box<T> {
|
||||
deref_async_write!();
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncWrite + Unpin> AsyncWrite for &mut T {
|
||||
deref_async_write!();
|
||||
}
|
||||
|
||||
impl<P> AsyncWrite for Pin<P>
|
||||
where
|
||||
P: DerefMut + Unpin,
|
||||
P::Target: AsyncWrite,
|
||||
{
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
||||
self.get_mut().as_mut().poll_write(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub struct FromStdIo<T>(T);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T> FromStdIo<T> {
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||
unsafe { Pin::new_unchecked(inner) }
|
||||
.poll_fill_buf(cx)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||
unsafe { Pin::new_unchecked(inner) }.consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||
unsafe { Pin::new_unchecked(inner) }
|
||||
.poll_write(cx, buf)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
80
embassy/src/io/util/copy_buf.rs
Normal file
80
embassy/src/io/util/copy_buf.rs
Normal file
@ -0,0 +1,80 @@
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use futures::ready;
|
||||
use pin_project::pin_project;
|
||||
|
||||
use crate::io::{AsyncBufRead, AsyncWrite, Error, Result};
|
||||
|
||||
/// Creates a future which copies all the bytes from one object to another.
|
||||
///
|
||||
/// The returned future will copy all the bytes read from this `AsyncBufRead` into the
|
||||
/// `writer` specified. This future will only complete once the `reader` has hit
|
||||
/// EOF and all bytes have been written to and flushed from the `writer`
|
||||
/// provided.
|
||||
///
|
||||
/// On success the number of bytes is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # futures::executor::block_on(async {
|
||||
/// use futures::io::{self, AsyncWriteExt, Cursor};
|
||||
///
|
||||
/// let reader = Cursor::new([1, 2, 3, 4]);
|
||||
/// let mut writer = Cursor::new(vec![0u8; 5]);
|
||||
///
|
||||
/// let bytes = io::copy_buf(reader, &mut writer).await?;
|
||||
/// writer.close().await?;
|
||||
///
|
||||
/// assert_eq!(bytes, 4);
|
||||
/// assert_eq!(writer.into_inner(), [1, 2, 3, 4, 0]);
|
||||
/// # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
|
||||
/// ```
|
||||
pub fn copy_buf<R, W>(reader: R, writer: &mut W) -> CopyBuf<'_, R, W>
|
||||
where
|
||||
R: AsyncBufRead,
|
||||
W: AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
CopyBuf {
|
||||
reader,
|
||||
writer,
|
||||
amt: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Future for the [`copy_buf()`] function.
|
||||
#[pin_project]
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct CopyBuf<'a, R, W: ?Sized> {
|
||||
#[pin]
|
||||
reader: R,
|
||||
writer: &'a mut W,
|
||||
amt: usize,
|
||||
}
|
||||
|
||||
impl<R, W> Future for CopyBuf<'_, R, W>
|
||||
where
|
||||
R: AsyncBufRead,
|
||||
W: AsyncWrite + Unpin + ?Sized,
|
||||
{
|
||||
type Output = Result<usize>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut this = self.project();
|
||||
loop {
|
||||
let buffer = ready!(this.reader.as_mut().poll_fill_buf(cx))?;
|
||||
if buffer.is_empty() {
|
||||
return Poll::Ready(Ok(*this.amt));
|
||||
}
|
||||
|
||||
let i = ready!(Pin::new(&mut this.writer).poll_write(cx, buffer))?;
|
||||
if i == 0 {
|
||||
return Poll::Ready(Err(Error::WriteZero.into()));
|
||||
}
|
||||
*this.amt += i;
|
||||
this.reader.as_mut().consume(i);
|
||||
}
|
||||
}
|
||||
}
|
145
embassy/src/io/util/mod.rs
Normal file
145
embassy/src/io/util/mod.rs
Normal file
@ -0,0 +1,145 @@
|
||||
use core::cmp::min;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use futures::ready;
|
||||
|
||||
mod read;
|
||||
pub use self::read::Read;
|
||||
|
||||
mod read_buf;
|
||||
pub use self::read_buf::ReadBuf;
|
||||
|
||||
mod read_byte;
|
||||
pub use self::read_byte::ReadByte;
|
||||
|
||||
mod read_exact;
|
||||
pub use self::read_exact::ReadExact;
|
||||
|
||||
mod read_while;
|
||||
pub use self::read_while::ReadWhile;
|
||||
|
||||
mod read_to_end;
|
||||
pub use self::read_to_end::ReadToEnd;
|
||||
|
||||
mod skip_while;
|
||||
pub use self::skip_while::SkipWhile;
|
||||
|
||||
mod write;
|
||||
pub use self::write::Write;
|
||||
|
||||
mod write_all;
|
||||
pub use self::write_all::WriteAll;
|
||||
|
||||
mod write_byte;
|
||||
pub use self::write_byte::WriteByte;
|
||||
|
||||
#[cfg(feature = "alloc")]
|
||||
mod split;
|
||||
#[cfg(feature = "alloc")]
|
||||
pub use self::split::{split, ReadHalf, WriteHalf};
|
||||
|
||||
mod copy_buf;
|
||||
pub use self::copy_buf::{copy_buf, CopyBuf};
|
||||
|
||||
use super::error::Result;
|
||||
use super::traits::{AsyncBufRead, AsyncWrite};
|
||||
|
||||
pub trait AsyncBufReadExt: AsyncBufRead {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut [u8],
|
||||
) -> Poll<Result<usize>>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
let mut this = &mut *self;
|
||||
let rbuf = ready!(Pin::new(&mut this).poll_fill_buf(cx))?;
|
||||
let n = min(buf.len(), rbuf.len());
|
||||
buf[..n].copy_from_slice(&rbuf[..n]);
|
||||
Pin::new(&mut this).consume(n);
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
|
||||
fn read_while<'a, F: Fn(u8) -> bool>(
|
||||
&'a mut self,
|
||||
buf: &'a mut [u8],
|
||||
f: F,
|
||||
) -> ReadWhile<'a, Self, F>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
ReadWhile::new(self, f, buf)
|
||||
}
|
||||
|
||||
fn skip_while<'a, F: Fn(u8) -> bool>(&'a mut self, f: F) -> SkipWhile<'a, Self, F>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
SkipWhile::new(self, f)
|
||||
}
|
||||
|
||||
fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> Read<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
Read::new(self, buf)
|
||||
}
|
||||
|
||||
fn read_buf<'a>(&'a mut self) -> ReadBuf<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
ReadBuf::new(self)
|
||||
}
|
||||
|
||||
fn read_byte<'a>(&'a mut self) -> ReadByte<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
ReadByte::new(self)
|
||||
}
|
||||
|
||||
fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExact<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
ReadExact::new(self, buf)
|
||||
}
|
||||
|
||||
fn read_to_end<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadToEnd<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
ReadToEnd::new(self, buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + ?Sized> AsyncBufReadExt for R {}
|
||||
|
||||
pub async fn read_line<R: AsyncBufRead + Unpin>(r: &mut R, buf: &mut [u8]) -> Result<usize> {
|
||||
r.skip_while(|b| b == b'\r' || b == b'\n').await?;
|
||||
let n = r.read_while(buf, |b| b != b'\r' && b != b'\n').await?;
|
||||
r.skip_while(|b| b == b'\r').await?;
|
||||
//assert_eq!(b'\n', r.read_byte().await?);
|
||||
r.read_byte().await?;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
pub trait AsyncWriteExt: AsyncWrite {
|
||||
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAll<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
WriteAll::new(self, buf)
|
||||
}
|
||||
|
||||
fn write_byte<'a>(&'a mut self, byte: u8) -> WriteByte<'a, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
WriteByte::new(self, byte)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncWrite + ?Sized> AsyncWriteExt for R {}
|
39
embassy/src/io/util/read.rs
Normal file
39
embassy/src/io/util/read.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use super::super::error::{Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
use core::cmp::min;
|
||||
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Read<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin> Unpin for Read<'_, R> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Read<'a, R> {
|
||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
||||
Read { reader, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + ?Sized + Unpin> Future for Read<'_, R> {
|
||||
type Output = Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = &mut *self;
|
||||
let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
|
||||
|
||||
let n = min(this.buf.len(), buf.len());
|
||||
this.buf[..n].copy_from_slice(&buf[..n]);
|
||||
Pin::new(&mut this.reader).consume(n);
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
}
|
34
embassy/src/io/util/read_buf.rs
Normal file
34
embassy/src/io/util/read_buf.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use super::super::error::{Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
pub struct ReadBuf<'a, R: ?Sized> {
|
||||
reader: Option<&'a mut R>,
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin> Unpin for ReadBuf<'_, R> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadBuf<'a, R> {
|
||||
pub(super) fn new(reader: &'a mut R) -> Self {
|
||||
ReadBuf {
|
||||
reader: Some(reader),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadBuf<'a, R> {
|
||||
type Output = Result<&'a [u8]>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = &mut *self;
|
||||
|
||||
let buf = ready!(Pin::new(this.reader.as_mut().unwrap()).poll_fill_buf(cx))?;
|
||||
let buf: &'a [u8] = unsafe { core::mem::transmute(buf) };
|
||||
this.reader = None;
|
||||
Poll::Ready(Ok(buf))
|
||||
}
|
||||
}
|
36
embassy/src/io/util/read_byte.rs
Normal file
36
embassy/src/io/util/read_byte.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::{Error, Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
pub struct ReadByte<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin> Unpin for ReadByte<'_, R> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadByte<'a, R> {
|
||||
pub(super) fn new(reader: &'a mut R) -> Self {
|
||||
Self { reader }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadByte<'a, R> {
|
||||
type Output = Result<u8>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self { reader } = &mut *self;
|
||||
let mut reader = Pin::new(reader);
|
||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if rbuf.len() == 0 {
|
||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
let r = rbuf[0];
|
||||
reader.as_mut().consume(1);
|
||||
Poll::Ready(Ok(r))
|
||||
}
|
||||
}
|
48
embassy/src/io/util/read_exact.rs
Normal file
48
embassy/src/io/util/read_exact.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use super::super::error::{Error, Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
use core::cmp::min;
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
/// Future for the [`read_exact`](super::AsyncBufReadExt::read_exact) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct ReadExact<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
buf: &'a mut [u8],
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin> Unpin for ReadExact<'_, R> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadExact<'a, R> {
|
||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
||||
ReadExact { reader, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadExact<'_, R> {
|
||||
type Output = Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let this = &mut *self;
|
||||
while !this.buf.is_empty() {
|
||||
let buf = ready!(Pin::new(&mut this.reader).poll_fill_buf(cx))?;
|
||||
if buf.len() == 0 {
|
||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
let n = min(this.buf.len(), buf.len());
|
||||
this.buf[..n].copy_from_slice(&buf[..n]);
|
||||
Pin::new(&mut this.reader).consume(n);
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &mut []).split_at_mut(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
48
embassy/src/io/util/read_to_end.rs
Normal file
48
embassy/src/io/util/read_to_end.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use core::cmp::min;
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::{Error, Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
pub struct ReadToEnd<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
buf: &'a mut [u8],
|
||||
n: usize,
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin> Unpin for ReadToEnd<'_, R> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> ReadToEnd<'a, R> {
|
||||
pub(super) fn new(reader: &'a mut R, buf: &'a mut [u8]) -> Self {
|
||||
Self { reader, buf, n: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for ReadToEnd<'a, R> {
|
||||
type Output = Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self { reader, buf, n } = &mut *self;
|
||||
let mut reader = Pin::new(reader);
|
||||
loop {
|
||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if rbuf.len() == 0 {
|
||||
return Poll::Ready(Ok(*n));
|
||||
}
|
||||
|
||||
if *n == buf.len() {
|
||||
return Poll::Ready(Err(Error::Truncated));
|
||||
}
|
||||
|
||||
// truncate data if it doesn't fit in buf
|
||||
let p = min(rbuf.len(), buf.len() - *n);
|
||||
buf[*n..*n + p].copy_from_slice(&rbuf[..p]);
|
||||
*n += p;
|
||||
|
||||
reader.as_mut().consume(p);
|
||||
}
|
||||
}
|
||||
}
|
61
embassy/src/io/util/read_while.rs
Normal file
61
embassy/src/io/util/read_while.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use core::cmp::min;
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::{Error, Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
pub struct ReadWhile<'a, R: ?Sized, F> {
|
||||
reader: &'a mut R,
|
||||
buf: &'a mut [u8],
|
||||
n: usize,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin, F> Unpin for ReadWhile<'_, R, F> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> ReadWhile<'a, R, F> {
|
||||
pub(super) fn new(reader: &'a mut R, f: F, buf: &'a mut [u8]) -> Self {
|
||||
Self {
|
||||
reader,
|
||||
f,
|
||||
buf,
|
||||
n: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for ReadWhile<'a, R, F> {
|
||||
type Output = Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self { reader, f, buf, n } = &mut *self;
|
||||
let mut reader = Pin::new(reader);
|
||||
loop {
|
||||
let rbuf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if rbuf.len() == 0 {
|
||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
let (p, done) = match rbuf.iter().position(|&b| !f(b)) {
|
||||
Some(p) => (p, true),
|
||||
None => (rbuf.len(), false),
|
||||
};
|
||||
|
||||
// truncate data if it doesn't fit in buf
|
||||
let p2 = min(p, buf.len() - *n);
|
||||
buf[*n..*n + p2].copy_from_slice(&rbuf[..p2]);
|
||||
*n += p2;
|
||||
|
||||
// consume it all, even if it doesn't fit.
|
||||
// Otherwise we can deadlock because we never read to the ending char
|
||||
reader.as_mut().consume(p);
|
||||
|
||||
if done {
|
||||
return Poll::Ready(Ok(*n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
45
embassy/src/io/util/skip_while.rs
Normal file
45
embassy/src/io/util/skip_while.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use core::iter::Iterator;
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::{Error, Result};
|
||||
use super::super::traits::AsyncBufRead;
|
||||
|
||||
pub struct SkipWhile<'a, R: ?Sized, F> {
|
||||
reader: &'a mut R,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<R: ?Sized + Unpin, F> Unpin for SkipWhile<'_, R, F> {}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> SkipWhile<'a, R, F> {
|
||||
pub(super) fn new(reader: &'a mut R, f: F) -> Self {
|
||||
Self { reader, f }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin, F: Fn(u8) -> bool> Future for SkipWhile<'a, R, F> {
|
||||
type Output = Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let Self { reader, f } = &mut *self;
|
||||
let mut reader = Pin::new(reader);
|
||||
loop {
|
||||
let buf = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if buf.len() == 0 {
|
||||
return Poll::Ready(Err(Error::UnexpectedEof));
|
||||
}
|
||||
|
||||
let (p, done) = match buf.iter().position(|b| !f(*b)) {
|
||||
Some(p) => (p, true),
|
||||
None => (buf.len(), false),
|
||||
};
|
||||
reader.as_mut().consume(p);
|
||||
if done {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
40
embassy/src/io/util/split.rs
Normal file
40
embassy/src/io/util/split.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use alloc::rc::Rc;
|
||||
use core::cell::UnsafeCell;
|
||||
use core::pin::Pin;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::Result;
|
||||
use super::super::traits::{AsyncBufRead, AsyncWrite};
|
||||
|
||||
/// The readable half of an object returned from `AsyncBufRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadHalf<T> {
|
||||
handle: Rc<UnsafeCell<T>>,
|
||||
}
|
||||
|
||||
/// The writable half of an object returned from `AsyncBufRead::split`.
|
||||
#[derive(Debug)]
|
||||
pub struct WriteHalf<T> {
|
||||
handle: Rc<UnsafeCell<T>>,
|
||||
}
|
||||
|
||||
impl<T: AsyncBufRead + Unpin> AsyncBufRead for ReadHalf<T> {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
||||
Pin::new(unsafe { &mut *self.handle.get() }).poll_fill_buf(cx)
|
||||
}
|
||||
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
Pin::new(unsafe { &mut *self.handle.get() }).consume(amt)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite + Unpin> AsyncWrite for WriteHalf<T> {
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
||||
Pin::new(unsafe { &mut *self.handle.get() }).poll_write(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn split<T: AsyncBufRead + AsyncWrite>(t: T) -> (ReadHalf<T>, WriteHalf<T>) {
|
||||
let c = Rc::new(UnsafeCell::new(t));
|
||||
(ReadHalf { handle: c.clone() }, WriteHalf { handle: c })
|
||||
}
|
33
embassy/src/io/util/write.rs
Normal file
33
embassy/src/io/util/write.rs
Normal file
@ -0,0 +1,33 @@
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::Result;
|
||||
use super::super::traits::AsyncWrite;
|
||||
|
||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Write<'a, W: ?Sized> {
|
||||
writer: &'a mut W,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Unpin> Unpin for Write<'_, W> {}
|
||||
|
||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> Write<'a, W> {
|
||||
pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
|
||||
Write { writer, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for Write<'_, W> {
|
||||
type Output = Result<usize>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<usize>> {
|
||||
let this = &mut *self;
|
||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
|
||||
Poll::Ready(Ok(n))
|
||||
}
|
||||
}
|
44
embassy/src/io/util/write_all.rs
Normal file
44
embassy/src/io/util/write_all.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::Result;
|
||||
use super::super::traits::AsyncWrite;
|
||||
|
||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct WriteAll<'a, W: ?Sized> {
|
||||
writer: &'a mut W,
|
||||
buf: &'a [u8],
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Unpin> Unpin for WriteAll<'_, W> {}
|
||||
|
||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteAll<'a, W> {
|
||||
pub(super) fn new(writer: &'a mut W, buf: &'a [u8]) -> Self {
|
||||
WriteAll { writer, buf }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteAll<'_, W> {
|
||||
type Output = Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
let this = &mut *self;
|
||||
while !this.buf.is_empty() {
|
||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, this.buf))?;
|
||||
{
|
||||
let (_, rest) = mem::replace(&mut this.buf, &[]).split_at(n);
|
||||
this.buf = rest;
|
||||
}
|
||||
if n == 0 {
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
39
embassy/src/io/util/write_byte.rs
Normal file
39
embassy/src/io/util/write_byte.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use core::pin::Pin;
|
||||
use futures::future::Future;
|
||||
use futures::ready;
|
||||
use futures::task::{Context, Poll};
|
||||
|
||||
use super::super::error::Result;
|
||||
use super::super::traits::AsyncWrite;
|
||||
|
||||
/// Future for the [`write_all`](super::AsyncWriteExt::write_all) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct WriteByte<'a, W: ?Sized> {
|
||||
writer: &'a mut W,
|
||||
byte: u8,
|
||||
}
|
||||
|
||||
impl<W: ?Sized + Unpin> Unpin for WriteByte<'_, W> {}
|
||||
|
||||
impl<'a, W: AsyncWrite + ?Sized + Unpin> WriteByte<'a, W> {
|
||||
pub(super) fn new(writer: &'a mut W, byte: u8) -> Self {
|
||||
WriteByte { writer, byte }
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWrite + ?Sized + Unpin> Future for WriteByte<'_, W> {
|
||||
type Output = Result<()>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
let this = &mut *self;
|
||||
let buf = [this.byte; 1];
|
||||
let n = ready!(Pin::new(&mut this.writer).poll_write(cx, &buf))?;
|
||||
if n == 0 {
|
||||
panic!();
|
||||
}
|
||||
assert!(n == 1);
|
||||
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
8
embassy/src/lib.rs
Normal file
8
embassy/src/lib.rs
Normal file
@ -0,0 +1,8 @@
|
||||
#![no_std]
|
||||
#![feature(slice_fill)]
|
||||
#![feature(generic_associated_types)]
|
||||
#![feature(const_fn)]
|
||||
|
||||
pub mod flash;
|
||||
pub mod util;
|
||||
pub mod io;
|
21
embassy/src/util/drop_bomb.rs
Normal file
21
embassy/src/util/drop_bomb.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use core::mem;
|
||||
|
||||
pub struct DropBomb {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
impl DropBomb {
|
||||
pub fn new() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
|
||||
pub fn defuse(self) {
|
||||
mem::forget(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropBomb {
|
||||
fn drop(&mut self) {
|
||||
depanic!("boom")
|
||||
}
|
||||
}
|
32
embassy/src/util/macros.rs
Normal file
32
embassy/src/util/macros.rs
Normal file
@ -0,0 +1,32 @@
|
||||
#![macro_use]
|
||||
|
||||
macro_rules! depanic {
|
||||
($( $i:expr ),*) => {
|
||||
{
|
||||
defmt::error!($( $i ),*);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! deassert {
|
||||
($cond:expr) => {
|
||||
deassert!($cond, "assertion failed");
|
||||
};
|
||||
($cond:expr, $msg:literal) => {
|
||||
{
|
||||
if !$cond {
|
||||
defmt::error!($msg);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
};
|
||||
($cond:expr, $msg:literal, $( $i:expr ),*) => {
|
||||
{
|
||||
if !$cond {
|
||||
defmt::error!($msg, $( $i ),*);
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
70
embassy/src/util/mod.rs
Normal file
70
embassy/src/util/mod.rs
Normal file
@ -0,0 +1,70 @@
|
||||
#![macro_use]
|
||||
|
||||
mod macros;
|
||||
|
||||
mod signal;
|
||||
pub use signal::*;
|
||||
mod portal;
|
||||
pub use portal::*;
|
||||
mod waker_store;
|
||||
pub use waker_store::*;
|
||||
mod drop_bomb;
|
||||
pub use drop_bomb::*;
|
||||
|
||||
use defmt::{warn, error};
|
||||
|
||||
pub trait Dewrap<T> {
|
||||
/// dewrap = defmt unwrap
|
||||
fn dewrap(self) -> T;
|
||||
|
||||
/// dexpect = defmt expect
|
||||
fn dexpect<M: defmt::Format>(self, msg: M) -> T;
|
||||
|
||||
fn dewarn<M: defmt::Format>(self, msg: M) -> Self;
|
||||
}
|
||||
|
||||
impl<T> Dewrap<T> for Option<T> {
|
||||
fn dewrap(self) -> T {
|
||||
match self {
|
||||
Some(t) => t,
|
||||
None => depanic!("unwrap failed: enum is none"),
|
||||
}
|
||||
}
|
||||
|
||||
fn dexpect<M: defmt::Format>(self, msg: M) -> T {
|
||||
match self {
|
||||
Some(t) => t,
|
||||
None => depanic!("unexpected None: {:?}", msg),
|
||||
}
|
||||
}
|
||||
|
||||
fn dewarn<M: defmt::Format>(self, msg: M) -> Self {
|
||||
if self.is_none() {
|
||||
warn!("{:?} is none", msg);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> {
|
||||
fn dewrap(self) -> T {
|
||||
match self {
|
||||
Ok(t) => t,
|
||||
Err(e) => depanic!("unwrap failed: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
fn dexpect<M: defmt::Format>(self, msg: M) -> T {
|
||||
match self {
|
||||
Ok(t) => t,
|
||||
Err(e) => depanic!("unexpected error: {:?}: {:?}", msg, e),
|
||||
}
|
||||
}
|
||||
|
||||
fn dewarn<M: defmt::Format>(self, msg: M) -> Self {
|
||||
if let Err(e) = &self {
|
||||
warn!("{:?} err: {:?}", msg, e);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
125
embassy/src/util/portal.rs
Normal file
125
embassy/src/util/portal.rs
Normal file
@ -0,0 +1,125 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::util::*;
|
||||
|
||||
/// Utility to call a closure across tasks.
|
||||
pub struct Portal<T> {
|
||||
state: UnsafeCell<State<T>>,
|
||||
}
|
||||
|
||||
enum State<T> {
|
||||
None,
|
||||
Running,
|
||||
Waiting(*mut dyn FnMut(T)),
|
||||
}
|
||||
|
||||
impl<T> Portal<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: UnsafeCell::new(State::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call(&self, val: T) {
|
||||
unsafe {
|
||||
match *self.state.get() {
|
||||
State::None => {}
|
||||
State::Running => depanic!("Portall::call() called reentrantly"),
|
||||
State::Waiting(func) => (*func)(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_once<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a
|
||||
where
|
||||
F: FnMut(T) -> R + 'a,
|
||||
{
|
||||
async move {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
let signal = Signal::new();
|
||||
let mut result: MaybeUninit<R> = MaybeUninit::uninit();
|
||||
let mut call_func = |val: T| {
|
||||
unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
*state = State::None;
|
||||
result.as_mut_ptr().write(func(val))
|
||||
};
|
||||
signal.signal(());
|
||||
};
|
||||
|
||||
let func_ptr: *mut dyn FnMut(T) = &mut call_func as _;
|
||||
let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) };
|
||||
|
||||
unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
match state {
|
||||
State::None => {}
|
||||
_ => depanic!("Multiple tasks waiting on same portal"),
|
||||
}
|
||||
*state = State::Waiting(func_ptr);
|
||||
}
|
||||
|
||||
signal.wait().await;
|
||||
|
||||
bomb.defuse();
|
||||
|
||||
unsafe { result.assume_init() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait_many<'a, R, F>(&'a self, mut func: F) -> impl Future<Output = R> + 'a
|
||||
where
|
||||
F: FnMut(T) -> Option<R> + 'a,
|
||||
{
|
||||
async move {
|
||||
let bomb = DropBomb::new();
|
||||
|
||||
let signal = Signal::new();
|
||||
let mut result: MaybeUninit<R> = MaybeUninit::uninit();
|
||||
let mut call_func = |val: T| {
|
||||
unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
|
||||
let func_ptr = match *state {
|
||||
State::Waiting(p) => p,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Set state to Running while running the function to avoid reentrancy.
|
||||
*state = State::Running;
|
||||
|
||||
*state = match func(val) {
|
||||
None => State::Waiting(func_ptr),
|
||||
Some(res) => {
|
||||
result.as_mut_ptr().write(res);
|
||||
signal.signal(());
|
||||
State::None
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
let func_ptr: *mut dyn FnMut(T) = &mut call_func as _;
|
||||
let func_ptr: *mut dyn FnMut(T) = unsafe { mem::transmute(func_ptr) };
|
||||
|
||||
unsafe {
|
||||
let state = &mut *self.state.get();
|
||||
match *state {
|
||||
State::None => {}
|
||||
_ => depanic!("Multiple tasks waiting on same portal"),
|
||||
}
|
||||
*state = State::Waiting(func_ptr);
|
||||
}
|
||||
|
||||
signal.wait().await;
|
||||
|
||||
bomb.defuse();
|
||||
|
||||
unsafe { result.assume_init() }
|
||||
}
|
||||
}
|
||||
}
|
70
embassy/src/util/signal.rs
Normal file
70
embassy/src/util/signal.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use core::cell::UnsafeCell;
|
||||
use core::future::Future;
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
pub struct Signal<T> {
|
||||
state: UnsafeCell<State<T>>,
|
||||
}
|
||||
|
||||
enum State<T> {
|
||||
None,
|
||||
Waiting(Waker),
|
||||
Signaled(T),
|
||||
}
|
||||
|
||||
unsafe impl<T: Send> Send for Signal<T> {}
|
||||
unsafe impl<T: Send> Sync for Signal<T> {}
|
||||
|
||||
impl<T: Send> Signal<T> {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
state: UnsafeCell::new(State::None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn signal(&self, val: T) {
|
||||
unsafe {
|
||||
cortex_m::interrupt::free(|_| {
|
||||
let state = &mut *self.state.get();
|
||||
match mem::replace(state, State::Signaled(val)) {
|
||||
State::Waiting(waker) => waker.wake(),
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait<'a>(&'a self) -> impl Future<Output = T> + 'a {
|
||||
WaitFuture { signal: self }
|
||||
}
|
||||
}
|
||||
|
||||
struct WaitFuture<'a, T> {
|
||||
signal: &'a Signal<T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Send> Future for WaitFuture<'a, T> {
|
||||
type Output = T;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
unsafe {
|
||||
cortex_m::interrupt::free(|_| {
|
||||
let state = &mut *self.signal.state.get();
|
||||
match state {
|
||||
State::None => {
|
||||
*state = State::Waiting(cx.waker().clone());
|
||||
Poll::Pending
|
||||
}
|
||||
State::Waiting(w) if w.will_wake(cx.waker()) => Poll::Pending,
|
||||
State::Waiting(_) => depanic!("waker overflow"),
|
||||
State::Signaled(_) => match mem::replace(state, State::None) {
|
||||
State::Signaled(res) => Poll::Ready(res),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
23
embassy/src/util/waker_store.rs
Normal file
23
embassy/src/util/waker_store.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use core::task::Waker;
|
||||
|
||||
pub struct WakerStore {
|
||||
waker: Option<Waker>,
|
||||
}
|
||||
|
||||
impl WakerStore {
|
||||
pub const fn new() -> Self {
|
||||
Self { waker: None }
|
||||
}
|
||||
|
||||
pub fn store(&mut self, w: &Waker) {
|
||||
match self.waker {
|
||||
Some(ref w2) if (w2.will_wake(w)) => {}
|
||||
Some(_) => panic!("Waker overflow"),
|
||||
None => self.waker = Some(w.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wake(&mut self) {
|
||||
self.waker.take().map(|w| w.wake());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user