Heap-allocated packets not necessary; no_std and no-alloc build supported end-to-end
This commit is contained in:
parent
8b3bb9527c
commit
7ef7e93eb4
12 changed files with 14 additions and 333 deletions
|
@ -1,4 +1,4 @@
|
|||
[workspace]
|
||||
members = ["matter", "matter_macro_derive", "boxslab", "tools/tlv_tool"]
|
||||
members = ["matter", "matter_macro_derive", "tools/tlv_tool"]
|
||||
|
||||
exclude = ["examples/*"]
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
[package]
|
||||
name = "boxslab"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
bitmaps={version="3.2.0", features=[]}
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2020-2022 Project CHIP Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use std::{
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
// TODO: why is max bitmap size 64 a correct max size? Could we match
|
||||
// boxslabs instead or store used/not used inside the box slabs themselves?
|
||||
const MAX_BITMAP_SIZE: usize = 64;
|
||||
pub struct Bitmap {
|
||||
inner: bitmaps::Bitmap<MAX_BITMAP_SIZE>,
|
||||
max_size: usize,
|
||||
}
|
||||
|
||||
impl Bitmap {
|
||||
pub fn new(max_size: usize) -> Self {
|
||||
assert!(max_size <= MAX_BITMAP_SIZE);
|
||||
Bitmap {
|
||||
inner: bitmaps::Bitmap::new(),
|
||||
max_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, index: usize) -> bool {
|
||||
assert!(index < self.max_size);
|
||||
self.inner.set(index, true)
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, index: usize) -> bool {
|
||||
assert!(index < self.max_size);
|
||||
self.inner.set(index, false)
|
||||
}
|
||||
|
||||
pub fn first_false_index(&self) -> Option<usize> {
|
||||
match self.inner.first_false_index() {
|
||||
Some(idx) if idx < self.max_size => Some(idx),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.inner.is_empty()
|
||||
}
|
||||
|
||||
pub fn is_full(&self) -> bool {
|
||||
self.first_false_index().is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! box_slab {
|
||||
($name:ident,$t:ty,$v:expr) => {
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::Once;
|
||||
use $crate::{BoxSlab, Slab, SlabPool};
|
||||
|
||||
pub struct $name;
|
||||
|
||||
impl SlabPool for $name {
|
||||
type SlabType = $t;
|
||||
fn get_slab() -> &'static Slab<Self> {
|
||||
const MAYBE_INIT: MaybeUninit<$t> = MaybeUninit::uninit();
|
||||
static mut SLAB_POOL: [MaybeUninit<$t>; $v] = [MAYBE_INIT; $v];
|
||||
static mut SLAB_SPACE: Option<Slab<$name>> = None;
|
||||
static mut INIT: Once = Once::new();
|
||||
unsafe {
|
||||
INIT.call_once(|| {
|
||||
SLAB_SPACE = Some(Slab::<$name>::init(&mut SLAB_POOL, $v));
|
||||
});
|
||||
SLAB_SPACE.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub trait SlabPool {
|
||||
type SlabType: 'static;
|
||||
fn get_slab() -> &'static Slab<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
pub struct Inner<T: 'static + SlabPool> {
|
||||
pool: &'static mut [MaybeUninit<T::SlabType>],
|
||||
map: Bitmap,
|
||||
}
|
||||
|
||||
// TODO: Instead of a mutex, we should replace this with a CAS loop
|
||||
pub struct Slab<T: 'static + SlabPool>(Mutex<Inner<T>>);
|
||||
|
||||
impl<T: SlabPool> Slab<T> {
|
||||
pub fn init(pool: &'static mut [MaybeUninit<T::SlabType>], size: usize) -> Self {
|
||||
Self(Mutex::new(Inner {
|
||||
pool,
|
||||
map: Bitmap::new(size),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn try_new(new_object: T::SlabType) -> Option<BoxSlab<T>> {
|
||||
let slab = T::get_slab();
|
||||
let mut inner = slab.0.lock().unwrap();
|
||||
if let Some(index) = inner.map.first_false_index() {
|
||||
inner.map.set(index);
|
||||
inner.pool[index].write(new_object);
|
||||
let cell_ptr = unsafe { &mut *inner.pool[index].as_mut_ptr() };
|
||||
Some(BoxSlab {
|
||||
data: cell_ptr,
|
||||
index,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn free(&self, index: usize) {
|
||||
let mut inner = self.0.lock().unwrap();
|
||||
inner.map.reset(index);
|
||||
let old_value = std::mem::replace(&mut inner.pool[index], MaybeUninit::uninit());
|
||||
let _old_value = unsafe { old_value.assume_init() };
|
||||
// This will drop the old_value
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BoxSlab<T: 'static + SlabPool> {
|
||||
// Because the data is a reference within the MaybeUninit, we don't have a mechanism
|
||||
// to go out to the MaybeUninit from this reference. Hence this index
|
||||
index: usize,
|
||||
// TODO: We should figure out a way to get rid of the index too
|
||||
data: &'static mut T::SlabType,
|
||||
}
|
||||
|
||||
impl<T: 'static + SlabPool> Drop for BoxSlab<T> {
|
||||
fn drop(&mut self) {
|
||||
T::get_slab().free(self.index);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SlabPool> Deref for BoxSlab<T> {
|
||||
type Target = T::SlabType;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SlabPool> DerefMut for BoxSlab<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
pub struct Test {
|
||||
val: Arc<u32>,
|
||||
}
|
||||
|
||||
box_slab!(TestSlab, Test, 3);
|
||||
|
||||
#[test]
|
||||
fn simple_alloc_free() {
|
||||
{
|
||||
let a = Slab::<TestSlab>::try_new(Test { val: Arc::new(10) }).unwrap();
|
||||
assert_eq!(*a.val.deref(), 10);
|
||||
let inner = TestSlab::get_slab().0.lock().unwrap();
|
||||
assert!(!inner.map.is_empty());
|
||||
}
|
||||
// Validates that the 'Drop' got executed
|
||||
let inner = TestSlab::get_slab().0.lock().unwrap();
|
||||
assert!(inner.map.is_empty());
|
||||
println!("Box Size {}", std::mem::size_of::<Box<Test>>());
|
||||
println!("BoxSlab Size {}", std::mem::size_of::<BoxSlab<TestSlab>>());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alloc_full_block() {
|
||||
{
|
||||
let a = Slab::<TestSlab>::try_new(Test { val: Arc::new(10) }).unwrap();
|
||||
let b = Slab::<TestSlab>::try_new(Test { val: Arc::new(11) }).unwrap();
|
||||
let c = Slab::<TestSlab>::try_new(Test { val: Arc::new(12) }).unwrap();
|
||||
// Test that at overflow, we return None
|
||||
assert!(Slab::<TestSlab>::try_new(Test { val: Arc::new(13) }).is_none(),);
|
||||
assert_eq!(*b.val.deref(), 11);
|
||||
|
||||
{
|
||||
let inner = TestSlab::get_slab().0.lock().unwrap();
|
||||
// Test that the bitmap is marked as full
|
||||
assert!(inner.map.is_full());
|
||||
}
|
||||
|
||||
// Purposefully drop, to test that new allocation is possible
|
||||
std::mem::drop(b);
|
||||
let d = Slab::<TestSlab>::try_new(Test { val: Arc::new(21) }).unwrap();
|
||||
assert_eq!(*d.val.deref(), 21);
|
||||
|
||||
// Ensure older allocations are still valid
|
||||
assert_eq!(*a.val.deref(), 10);
|
||||
assert_eq!(*c.val.deref(), 12);
|
||||
}
|
||||
|
||||
// Validates that the 'Drop' got executed - test that the bitmap is empty
|
||||
let inner = TestSlab::get_slab().0.lock().unwrap();
|
||||
assert!(inner.map.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_drop_logic() {
|
||||
let root = Arc::new(10);
|
||||
{
|
||||
let _a = Slab::<TestSlab>::try_new(Test { val: root.clone() }).unwrap();
|
||||
let _b = Slab::<TestSlab>::try_new(Test { val: root.clone() }).unwrap();
|
||||
let _c = Slab::<TestSlab>::try_new(Test { val: root.clone() }).unwrap();
|
||||
assert_eq!(Arc::strong_count(&root), 4);
|
||||
}
|
||||
// Test that Drop was correctly called on all the members of the pool
|
||||
assert_eq!(Arc::strong_count(&root), 1);
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ fn main() {
|
|||
//let mut mdns = matter::mdns::astro::AstroMdns::new().unwrap();
|
||||
let mut mdns = matter::mdns::libmdns::LibMdns::new().unwrap();
|
||||
|
||||
let matter = Matter::new_default(&dev_info, &mut mdns);
|
||||
let matter = Matter::new_default(&dev_info, &mut mdns, matter::transport::udp::MATTER_PORT);
|
||||
|
||||
let dev_att = dev_att::HardCodedDevAtt::new();
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ crypto_esp_mbedtls = ["esp-idf-sys"]
|
|||
crypto_rustcrypto = ["sha2", "hmac", "pbkdf2", "hkdf", "aes", "ccm", "p256", "elliptic-curve", "crypto-bigint", "x509-cert"]
|
||||
|
||||
[dependencies]
|
||||
boxslab = { path = "../boxslab" }
|
||||
matter_macro_derive = { path = "../matter_macro_derive" }
|
||||
bitflags = "1.3"
|
||||
byteorder = "1.4.3"
|
||||
|
@ -35,6 +34,7 @@ num-derive = "0.3.3"
|
|||
num-traits = "0.2.15"
|
||||
strum = { version = "0.24", features = ["derive"], default-features = false, no-default-feature = true }
|
||||
log = { version = "0.4.17", features = ["max_level_debug", "release_max_level_debug"] }
|
||||
no-std-net = "0.6"
|
||||
subtle = "2.4.1"
|
||||
safemem = "0.3.3"
|
||||
colored = "2.0.0" # TODO: Requires STD
|
||||
|
|
|
@ -25,7 +25,6 @@ use crate::{
|
|||
mdns::{Mdns, MdnsMgr},
|
||||
pairing::{print_pairing_code_and_qr, DiscoveryCapabilities},
|
||||
secure_channel::{pake::PaseMgr, spake2p::VerifierData},
|
||||
transport::udp::MATTER_PORT,
|
||||
utils::{
|
||||
epoch::{Epoch, UtcCalendar},
|
||||
rand::Rand,
|
||||
|
@ -55,11 +54,11 @@ pub struct Matter<'a> {
|
|||
|
||||
impl<'a> Matter<'a> {
|
||||
#[cfg(feature = "std")]
|
||||
pub fn new_default(dev_det: &'a BasicInfoConfig, mdns: &'a mut dyn Mdns) -> Self {
|
||||
pub fn new_default(dev_det: &'a BasicInfoConfig, mdns: &'a mut dyn Mdns, port: u16) -> Self {
|
||||
use crate::utils::epoch::{sys_epoch, sys_utc_calendar};
|
||||
use crate::utils::rand::sys_rand;
|
||||
|
||||
Self::new(dev_det, mdns, sys_epoch, sys_rand, sys_utc_calendar)
|
||||
Self::new(dev_det, mdns, sys_epoch, sys_rand, sys_utc_calendar, port)
|
||||
}
|
||||
|
||||
/// Creates a new Matter object
|
||||
|
@ -74,6 +73,7 @@ impl<'a> Matter<'a> {
|
|||
epoch: Epoch,
|
||||
rand: Rand,
|
||||
utc_calendar: UtcCalendar,
|
||||
port: u16,
|
||||
) -> Self {
|
||||
Self {
|
||||
fabric_mgr: RefCell::new(FabricMgr::new()),
|
||||
|
@ -84,7 +84,7 @@ impl<'a> Matter<'a> {
|
|||
dev_det.vid,
|
||||
dev_det.pid,
|
||||
dev_det.device_name,
|
||||
MATTER_PORT,
|
||||
port,
|
||||
mdns,
|
||||
)),
|
||||
epoch,
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
use core::{array::TryFromSliceError, fmt, str::Utf8Error};
|
||||
|
||||
use log::error;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Error {
|
||||
AttributeNotFound,
|
||||
|
@ -122,7 +120,7 @@ impl<T> From<std::sync::PoisonError<T>> for Error {
|
|||
#[cfg(feature = "crypto_openssl")]
|
||||
impl From<openssl::error::ErrorStack> for Error {
|
||||
fn from(e: openssl::error::ErrorStack) -> Self {
|
||||
error!("Error in TLS: {}", e);
|
||||
::log::error!("Error in TLS: {}", e);
|
||||
Self::TLSStack
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +128,7 @@ impl From<openssl::error::ErrorStack> for Error {
|
|||
#[cfg(feature = "crypto_mbedtls")]
|
||||
impl From<mbedtls::Error> for Error {
|
||||
fn from(e: mbedtls::Error) -> Self {
|
||||
error!("Error in TLS: {}", e);
|
||||
::log::error!("Error in TLS: {}", e);
|
||||
Self::TLSStack
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,6 @@ pub mod mdns;
|
|||
pub mod pairing;
|
||||
pub mod persist;
|
||||
pub mod secure_channel;
|
||||
pub mod sys;
|
||||
pub mod tlv;
|
||||
pub mod transport;
|
||||
pub mod utils;
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2020-2022 Project CHIP Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// The Packet Pool that is allocated from. POSIX systems can use
|
||||
// higher values unlike embedded systems
|
||||
pub const MAX_PACKET_POOL_SIZE: usize = 25;
|
|
@ -25,4 +25,5 @@ pub mod plain_hdr;
|
|||
pub mod proto_ctx;
|
||||
pub mod proto_hdr;
|
||||
pub mod session;
|
||||
#[cfg(feature = "std")]
|
||||
pub mod udp;
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
|
||||
use core::fmt::{Debug, Display};
|
||||
#[cfg(not(feature = "std"))]
|
||||
use no_std_net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
#[cfg(feature = "std")]
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
|
|
|
@ -15,14 +15,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
use log::{error, trace};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use boxslab::box_slab;
|
||||
use log::error;
|
||||
|
||||
use crate::{
|
||||
error::Error,
|
||||
sys::MAX_PACKET_POOL_SIZE,
|
||||
utils::{parsebuf::ParseBuf, writebuf::WriteBuf},
|
||||
};
|
||||
|
||||
|
@ -34,54 +30,6 @@ use super::{
|
|||
|
||||
pub const MAX_RX_BUF_SIZE: usize = 1583;
|
||||
pub const MAX_TX_BUF_SIZE: usize = 1280 - 40/*IPV6 header size*/ - 8/*UDP header size*/;
|
||||
type Buffer = [u8; MAX_RX_BUF_SIZE];
|
||||
|
||||
// TODO: I am not very happy with this construction, need to find another way to do this
|
||||
pub struct BufferPool {
|
||||
buffers: [Option<Buffer>; MAX_PACKET_POOL_SIZE],
|
||||
}
|
||||
|
||||
impl BufferPool {
|
||||
const INIT: Option<Buffer> = None;
|
||||
fn get() -> &'static Mutex<BufferPool> {
|
||||
static mut BUFFER_HOLDER: Option<Mutex<BufferPool>> = None;
|
||||
static ONCE: Once = Once::new();
|
||||
unsafe {
|
||||
ONCE.call_once(|| {
|
||||
BUFFER_HOLDER = Some(Mutex::new(BufferPool {
|
||||
buffers: [BufferPool::INIT; MAX_PACKET_POOL_SIZE],
|
||||
}));
|
||||
});
|
||||
BUFFER_HOLDER.as_ref().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc() -> Option<(usize, &'static mut Buffer)> {
|
||||
trace!("Buffer Alloc called\n");
|
||||
|
||||
let mut pool = BufferPool::get().lock().unwrap();
|
||||
for i in 0..MAX_PACKET_POOL_SIZE {
|
||||
if pool.buffers[i].is_none() {
|
||||
pool.buffers[i] = Some([0; MAX_RX_BUF_SIZE]);
|
||||
// Sigh! to by-pass the borrow-checker telling us we are stealing a mutable reference
|
||||
// from under the lock
|
||||
// In this case the lock only protects against the setting of Some/None,
|
||||
// the objects then are independently accessed in a unique way
|
||||
let buffer = unsafe { &mut *(pool.buffers[i].as_mut().unwrap() as *mut Buffer) };
|
||||
return Some((i, buffer));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn free(index: usize) {
|
||||
trace!("Buffer Free called\n");
|
||||
let mut pool = BufferPool::get().lock().unwrap();
|
||||
if pool.buffers[index].is_some() {
|
||||
pool.buffers[index] = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum RxState {
|
||||
|
@ -229,5 +177,3 @@ impl<'a> Packet<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
box_slab!(PacketPool, Packet<'static>, MAX_PACKET_POOL_SIZE);
|
||||
|
|
Loading…
Add table
Reference in a new issue