Skip to content

Commit

Permalink
mesh_protobuf: support building with no_std (#391)
Browse files Browse the repository at this point in the history
Support encoding/decoding protobuf messages from `no_std` environments.

We don't have an immediate use case for this, but there is some talk of
using this within the boot loader (which would need a global allocator).
  • Loading branch information
jstarks authored Nov 26, 2024
1 parent 57b6244 commit 5c813a6
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 99 deletions.
2 changes: 1 addition & 1 deletion support/mesh/mesh_channel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ rust-version.workspace = true

[dependencies]
mesh_node.workspace = true
mesh_protobuf.workspace = true
mesh_protobuf = { workspace = true, features = ["std"] }

futures-core.workspace = true
futures-io.workspace = true
Expand Down
1 change: 1 addition & 0 deletions support/mesh/mesh_protobuf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ rust-version.workspace = true
default = []
prost = ["dep:prost", "dep:prost-types", "dep:prost-build"]
socket2 = ["dep:socket2"]
std = []

[dependencies]
mesh_derive.workspace = true
Expand Down
7 changes: 5 additions & 2 deletions support/mesh/mesh_protobuf/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
//! This is different from `bytes::BufMut` in that the buffer is required to be
//! contiguous, which allows for more efficient use with type erasure.
use std::mem::MaybeUninit;
use alloc::vec::Vec;
use core::mem::MaybeUninit;

/// Models a partially written, contiguous byte buffer.
pub trait Buffer {
Expand Down Expand Up @@ -66,11 +67,12 @@ impl Buffer for Buf<'_> {
}
}

#[cfg(feature = "std")]
impl Buffer for std::io::Cursor<&mut [u8]> {
unsafe fn unwritten(&mut self) -> &mut [MaybeUninit<u8>] {
let slice = self.get_mut();
// SAFETY: the caller promises not to uninitialize any initialized data.
unsafe { std::slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) }
unsafe { core::slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) }
}

unsafe fn extend_written(&mut self, len: usize) {
Expand Down Expand Up @@ -187,6 +189,7 @@ where
#[cfg(test)]
mod tests {
use super::write_with;
use alloc::vec;

#[test]
#[should_panic]
Expand Down
6 changes: 3 additions & 3 deletions support/mesh/mesh_protobuf/src/encode_with.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ use super::MessageEncode;
use super::Result;
use crate::inplace;
use crate::Downcast;
use std::ops::Deref;
use std::ops::DerefMut;
use core::ops::Deref;
use core::ops::DerefMut;

/// Wrapper type to easily support custom mesh encoding.
///
Expand Down Expand Up @@ -72,7 +72,7 @@ impl<T, U: From<T>> EncodeAs<T, U> {
}

fn encode(&mut self) -> &mut U {
match std::mem::replace(&mut self.0, Inner::Invalid) {
match core::mem::replace(&mut self.0, Inner::Invalid) {
Inner::Unencoded(t) => {
self.0 = Inner::Encoded(t.into());
}
Expand Down
53 changes: 28 additions & 25 deletions support/mesh/mesh_protobuf/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,25 @@ use crate::protofile::DescribeMessage;
use crate::protofile::FieldType;
use crate::protofile::MessageDescription;
use crate::Error;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::convert::Infallible;
use std::marker::PhantomData;
use std::num::NonZeroI16;
use std::num::NonZeroI32;
use std::num::NonZeroI64;
use std::num::NonZeroI8;
use std::num::NonZeroIsize;
use std::num::NonZeroU16;
use std::num::NonZeroU32;
use std::num::NonZeroU64;
use std::num::NonZeroU8;
use std::num::NonZeroUsize;
use std::sync::Arc;
use std::time::Duration;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::convert::Infallible;
use core::marker::PhantomData;
use core::num::NonZeroI16;
use core::num::NonZeroI32;
use core::num::NonZeroI64;
use core::num::NonZeroI8;
use core::num::NonZeroIsize;
use core::num::NonZeroU16;
use core::num::NonZeroU32;
use core::num::NonZeroU64;
use core::num::NonZeroU8;
use core::num::NonZeroUsize;
use core::time::Duration;
use thiserror::Error;

/// An encoding derived by `mesh_derive` for `T`.
Expand Down Expand Up @@ -254,7 +256,7 @@ impl FromNumber for char {
fn from_u64(v: u64) -> Result<Self> {
v.try_into()
.ok()
.and_then(std::char::from_u32)
.and_then(core::char::from_u32)
.ok_or_else(|| DecodeError::InvalidUtf32.into())
}

Expand Down Expand Up @@ -825,7 +827,7 @@ impl<T: AsRef<str>, R> FieldEncode<T, R> for StringField {
impl<'a, T: From<&'a str> + Default, R> FieldDecode<'a, T, R> for StringField {
fn read_field(item: &mut InplaceOption<'_, T>, reader: FieldReader<'a, '_, R>) -> Result<()> {
item.set(
std::str::from_utf8(reader.bytes()?)
core::str::from_utf8(reader.bytes()?)
.map_err(DecodeError::InvalidUtf8)?
.into(),
);
Expand Down Expand Up @@ -862,7 +864,7 @@ impl<'a, R> FieldDecode<'a, Cow<'a, str>, R> for BorrowedCowField {
reader: FieldReader<'a, '_, R>,
) -> Result<()> {
item.set(Cow::Borrowed(
std::str::from_utf8(reader.bytes()?).map_err(DecodeError::InvalidUtf8)?,
core::str::from_utf8(reader.bytes()?).map_err(DecodeError::InvalidUtf8)?,
));
Ok(())
}
Expand Down Expand Up @@ -930,7 +932,7 @@ impl<'a, 'b, R> FieldDecode<'a, Cow<'b, str>, R> for OwningCowField {
reader: FieldReader<'a, '_, R>,
) -> Result<()> {
item.set(Cow::Owned(
std::str::from_utf8(reader.bytes()?)
core::str::from_utf8(reader.bytes()?)
.map_err(DecodeError::InvalidUtf8)?
.into(),
));
Expand Down Expand Up @@ -1674,7 +1676,8 @@ impl<T: DefaultEncoding> DefaultEncoding for Vec<T> {

impl<T, U> Downcast<Vec<U>> for Vec<T> where T: Downcast<U> {}

impl<K: DefaultEncoding, V: DefaultEncoding> DefaultEncoding for HashMap<K, V> {
#[cfg(feature = "std")]
impl<K: DefaultEncoding, V: DefaultEncoding> DefaultEncoding for std::collections::HashMap<K, V> {
type Encoding = MapField<K, V, K::Encoding, V::Encoding>;
}

Expand Down Expand Up @@ -1796,7 +1799,7 @@ impl<T, U, R> FieldDecode<'_, T, R> for ResourceField<U>
where
T: From<U>,
U: TryFrom<R>,
U::Error: 'static + std::error::Error + Send + Sync,
U::Error: 'static + core::error::Error + Send + Sync,
{
fn read_field(item: &mut InplaceOption<'_, T>, reader: FieldReader<'_, '_, R>) -> Result<()> {
let resource = T::from(reader.resource()?.try_into().map_err(Error::new)?);
Expand All @@ -1822,7 +1825,7 @@ macro_rules! os_resource {
};
}

#[cfg(windows)]
#[cfg(all(feature = "std", windows))]
mod windows {
use crate::os_resource;
use std::os::windows::prelude::*;
Expand All @@ -1839,7 +1842,7 @@ mod windows {
os_resource!(socket2::Socket, OwnedSocket);
}

#[cfg(unix)]
#[cfg(all(feature = "std", unix))]
mod unix {
use crate::os_resource;
use std::os::unix::prelude::*;
Expand Down
22 changes: 13 additions & 9 deletions support/mesh/mesh_protobuf/src/inplace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@

//! Provides an `Option`-like type for constructing values in place.
use std::mem::MaybeUninit;
use std::sync::Arc;
use alloc::boxed::Box;
use alloc::sync::Arc;
use core::mem::MaybeUninit;

/// A type with methods like `Option` but that operates on a mutable reference
/// to possibly-initialized data.
Expand Down Expand Up @@ -51,7 +52,7 @@ impl<'a, T> InplaceOption<'a, T> {
self.init = false;
// SAFETY: val is initialized
unsafe {
let val = std::ptr::read(&*self.val);
let val = core::ptr::read(&*self.val);
Some(val.assume_init())
}
} else {
Expand Down Expand Up @@ -227,12 +228,12 @@ macro_rules! inplace {
let mut $v;
let mut $v = match opt {
Some(v) => {
$v = std::mem::MaybeUninit::new(v);
$v = core::mem::MaybeUninit::new(v);
// SAFETY: We just initialized the value.
unsafe { $crate::inplace::InplaceOption::new_init_unchecked(&mut $v) }
}
None => {
$v = std::mem::MaybeUninit::uninit();
$v = core::mem::MaybeUninit::uninit();
$crate::inplace::InplaceOption::uninit(&mut $v)
}
};
Expand All @@ -243,7 +244,7 @@ macro_rules! inplace {
#[macro_export]
macro_rules! inplace_some {
($v:ident) => {
let mut $v = std::mem::MaybeUninit::new($v);
let mut $v = core::mem::MaybeUninit::new($v);
#[allow(unused_mut)]
// SAFETY: We just initialized the value.
let mut $v = unsafe { $crate::inplace::InplaceOption::new_init_unchecked(&mut $v) };
Expand All @@ -254,20 +255,23 @@ macro_rules! inplace_some {
#[macro_export]
macro_rules! inplace_none {
($v:ident) => {
let mut $v = std::mem::MaybeUninit::uninit();
let mut $v = core::mem::MaybeUninit::uninit();
#[allow(unused_mut)]
let mut $v = $crate::inplace::InplaceOption::uninit(&mut $v);
};
($v:ident : $t:ty) => {
let mut $v = std::mem::MaybeUninit::<$t>::uninit();
let mut $v = core::mem::MaybeUninit::<$t>::uninit();
#[allow(unused_mut)]
let mut $v = $crate::inplace::InplaceOption::uninit(&mut $v);
};
}

#[cfg(test)]
mod tests {
use std::sync::Arc;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::string::ToString;
use alloc::sync::Arc;

#[test]
fn test_inplace_some() {
Expand Down
Loading

0 comments on commit 5c813a6

Please sign in to comment.