make bark-alloc fallible and add pbuf implementation

This commit is contained in:
Hailey Somerville 2023-09-18 18:29:38 +10:00
parent 760e81ecf6
commit 422ce25c9f
11 changed files with 151 additions and 128 deletions

23
Cargo.lock generated
View file

@ -88,7 +88,6 @@ version = "0.1.0"
dependencies = [
"bytemuck",
"derive_more",
"esp-alloc",
]
[[package]]
@ -298,12 +297,6 @@ dependencies = [
"windows",
]
[[package]]
name = "critical-section"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "dasp_sample"
version = "0.11.0"
@ -329,16 +322,6 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "esp-alloc"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83792eb7261a375bb838679fea2b45654b8f4a48da6bef10a96da5054aa81c7d"
dependencies = [
"critical-section",
"linked_list_allocator",
]
[[package]]
name = "getrandom"
version = "0.2.10"
@ -476,12 +459,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
[[package]]
name = "lock_api"
version = "0.4.10"

View file

@ -5,9 +5,8 @@ edition = "2021"
[features]
alloc = ["bytemuck/extern_crate_alloc"]
esp_alloc = ["esp-alloc"]
pbuf = []
[dependencies]
esp-alloc = { version = "0.3", optional = true }
bytemuck = { workspace = true, optional = true }
derive_more = { workspace = true }

View file

@ -8,7 +8,7 @@ use derive_more::{Deref, DerefMut};
pub struct FixedBuffer<const N: usize>(alloc::boxed::Box<[u8]>);
impl<const N: usize> FixedBuffer<N> {
pub fn alloc_zeroed() -> Self {
FixedBuffer(bytemuck::allocation::zeroed_slice_box(N))
pub fn alloc_zeroed() -> Result<Self, crate::AllocError> {
Ok(FixedBuffer(bytemuck::allocation::zeroed_slice_box(N)))
}
}

View file

@ -1,71 +0,0 @@
use core::alloc::{GlobalAlloc, Layout};
use core::ops::{Deref, DerefMut};
use core::ptr::null_mut;
use core::slice;
use core::sync::atomic::{AtomicPtr, Ordering};
use esp_alloc::EspHeap;
static HEAP: AtomicPtr<EspHeap> = AtomicPtr::new(null_mut());
pub unsafe fn set_heap(heap: &'static EspHeap) {
let result = HEAP.compare_exchange(
null_mut(),
heap as *const _ as *mut _,
Ordering::SeqCst,
Ordering::Relaxed,
);
if result.is_err() {
panic!("bark_alloc: attempted to call set_heap twice");
}
}
fn heap() -> &'static EspHeap {
let ptr = HEAP.load(Ordering::Relaxed);
if ptr == null_mut() {
panic!("bark_alloc: heap accessed before set! call set_heap first.")
}
unsafe { &*(ptr as *const _) }
}
#[repr(transparent)]
pub struct FixedBuffer<const N: usize>(*mut u8);
impl<const N: usize> FixedBuffer<N> {
const LAYOUT: Layout = unsafe {
// Layout::from_size_align is const but returns a Result,
// we can't const unwrap results on stable rust yet.
Layout::from_size_align_unchecked(N, 4)
};
pub fn alloc_zeroed() -> Self {
let ptr = unsafe { heap().alloc_zeroed(Self::LAYOUT) };
if ptr == null_mut() {
panic!("bark_alloc: allocation failed! requsted size: {N}");
}
FixedBuffer(ptr)
}
}
impl<const N: usize> Drop for FixedBuffer<N> {
fn drop(&mut self) {
unsafe { heap().dealloc(self.0, Self::LAYOUT); }
}
}
impl<const N: usize> Deref for FixedBuffer<N> {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self.0, N) }
}
}
impl<const N: usize> DerefMut for FixedBuffer<N> {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.0, N) }
}
}

View file

@ -1,14 +1,19 @@
#![no_std]
#[cfg(not(any(feature = "alloc", feature = "esp_alloc")))]
#[cfg(not(any(feature = "alloc", feature = "pbuf")))]
compile_error!("must enable alloc feature!");
#[derive(Debug, Clone, Copy)]
pub struct AllocError {
pub requested_bytes: usize
}
#[cfg(feature = "alloc")]
#[path = "alloc_box_impl.rs"]
mod impl_;
#[cfg(feature = "esp_alloc")]
#[path = "esp_alloc_impl.rs"]
#[cfg(feature = "pbuf")]
#[path = "pbuf_impl.rs"]
mod impl_;
pub use impl_::*;

101
bark-alloc/src/pbuf_impl.rs Normal file
View file

@ -0,0 +1,101 @@
use core::ops::{Deref, DerefMut};
use core::ptr::NonNull;
use crate::AllocError;
#[repr(transparent)]
pub struct FixedBuffer<const N: usize> {
pbuf: NonNull<ffi::pbuf>,
}
unsafe impl<const N: usize> Send for FixedBuffer<N> {}
unsafe impl<const N: usize> Sync for FixedBuffer<N> {}
impl<const N: usize> FixedBuffer<N> {
pub fn alloc_zeroed() -> Result<Self, AllocError> {
let err = AllocError { requested_bytes: ffi::PBUF_TRANSPORT + N };
let len = u16::try_from(N).map_err(|_| err)?;
// alloc the pbuf:
let pbuf = unsafe {
ffi::pbuf_alloc(ffi::PBUF_TRANSPORT as i32, len, ffi::PBUF_RAM)
};
let mut pbuf = NonNull::new(pbuf).ok_or(err)?;
// zero its contents:
unsafe {
let payload = pbuf.as_mut().payload as *mut u8;
core::ptr::write_bytes(payload, 0, N);
}
Ok(FixedBuffer { pbuf })
}
}
impl<const N: usize> Deref for FixedBuffer<N> {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
let pbuf = self.pbuf.as_ref();
let payload = pbuf.payload as *mut u8 as *const u8;
let len = usize::from(pbuf.len);
core::slice::from_raw_parts(payload, len)
}
}
}
impl<const N: usize> DerefMut for FixedBuffer<N> {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
let pbuf = self.pbuf.as_mut();
let payload = pbuf.payload as *mut u8;
let len = usize::from(pbuf.len);
core::slice::from_raw_parts_mut(payload, len)
}
}
}
impl<const N: usize> Drop for FixedBuffer<N> {
fn drop(&mut self) {
unsafe {
ffi::pbuf_free(self.pbuf.as_ptr());
}
}
}
// bindings to esp-lwip pbuf.h
// https://github.com/espressif/esp-lwip/blob/7896c6cad020d17a986f7e850f603e084e319328/src/include/lwip/pbuf.h
pub mod ffi {
use core::ffi::c_void;
const PBUF_ALLOC_FLAG_DATA_CONTIGUOUS: i32 = 0x0200;
const PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS: i32 = 0x80;
const PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP: i32 = 0x00;
/// Downstream crates should statically assert that this is equal to or
/// larger than their PBUF_TRANSPORT constant
pub const PBUF_TRANSPORT: usize = 74;
/// Downstream crates should statically assert that this is equal to their
/// PBUF_RAM constant
pub const PBUF_RAM: i32 = PBUF_ALLOC_FLAG_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP;
#[repr(C)]
pub struct pbuf {
pub next: *mut pbuf,
pub payload: *mut c_void,
pub tot_len: u16,
pub len: u16,
pub type_internal: u8,
pub flags: u8,
// fields continue but this is all we need
}
extern "C" {
pub fn pbuf_alloc(layer: i32, length: u16, type_: i32) -> *mut pbuf;
pub fn pbuf_free(p: *mut pbuf) -> u8;
}
}

View file

@ -12,6 +12,8 @@ use crate::time::SampleDuration;
use super::types::{AudioPacketHeader, StatsReplyFlags, SessionId};
pub use bark_alloc::AllocError;
pub const MAX_PACKET_SIZE: usize =
size_of::<types::PacketHeader>() +
size_of::<types::AudioPacketHeader>() +
@ -29,11 +31,11 @@ impl Debug for PacketBuffer {
}
impl PacketBuffer {
pub fn allocate() -> Self {
PacketBuffer {
raw: bark_alloc::FixedBuffer::alloc_zeroed(),
pub fn allocate() -> Result<Self, AllocError> {
Ok(PacketBuffer {
raw: bark_alloc::FixedBuffer::alloc_zeroed()?,
len: 0,
}
})
}
pub fn len(&self) -> usize {
@ -61,11 +63,11 @@ impl PacketBuffer {
pub struct Packet(PacketBuffer);
impl Packet {
fn allocate(magic: Magic, len: usize) -> Self {
let mut packet = Packet(PacketBuffer::allocate());
fn allocate(magic: Magic, len: usize) -> Result<Self, AllocError> {
let mut packet = Packet(PacketBuffer::allocate()?);
packet.set_len(len);
packet.header_mut().magic = magic;
return packet;
Ok(packet)
}
pub fn from_buffer(buffer: PacketBuffer) -> Option<Packet> {
@ -140,13 +142,13 @@ impl Audio {
size_of::<types::AudioPacketHeader>() +
size_of::<types::AudioPacketBuffer>();
pub fn write() -> AudioWriter {
let packet = Packet::allocate(Magic::AUDIO, Self::LENGTH);
pub fn write() -> Result<AudioWriter, AllocError> {
let packet = Packet::allocate(Magic::AUDIO, Self::LENGTH)?;
AudioWriter {
Ok(AudioWriter {
packet: Audio(packet),
written: SampleDuration::zero(),
}
})
}
pub fn parse(packet: Packet) -> Option<Self> {
@ -253,8 +255,8 @@ impl Time {
const DATA_RANGE: Range<usize> =
0..size_of::<types::TimePacket>();
pub fn allocate() -> Self {
Time(Packet::allocate(Magic::TIME, Self::LENGTH))
pub fn allocate() -> Result<Self, AllocError> {
Ok(Time(Packet::allocate(Magic::TIME, Self::LENGTH)?))
}
pub fn parse(packet: Packet) -> Option<Self> {
@ -288,8 +290,8 @@ impl Time {
pub struct StatsRequest(Packet);
impl StatsRequest {
pub fn new() -> Self {
StatsRequest(Packet::allocate(Magic::STATS_REQ, 0))
pub fn new() -> Result<Self, AllocError> {
Ok(StatsRequest(Packet::allocate(Magic::STATS_REQ, 0)?))
}
pub fn parse(packet: Packet) -> Option<Self> {
@ -315,17 +317,17 @@ pub struct StatsReply(Packet);
impl StatsReply {
const LENGTH: usize = size_of::<types::StatsReplyPacket>();
fn new(flags: StatsReplyFlags, data: types::StatsReplyPacket) -> Self {
let mut packet = Packet::allocate(Magic::STATS_REPLY, Self::LENGTH);
fn new(flags: StatsReplyFlags, data: types::StatsReplyPacket) -> Result<Self, AllocError> {
let mut packet = Packet::allocate(Magic::STATS_REPLY, Self::LENGTH)?;
packet.header_mut().flags = bytemuck::cast(flags);
let mut reply = StatsReply(packet);
*reply.data_mut() = data;
reply
Ok(reply)
}
pub fn source(sid: SessionId, node: NodeStats) -> Self {
pub fn source(sid: SessionId, node: NodeStats) -> Result<Self, AllocError> {
let receiver = ReceiverStats::zeroed();
Self::new(
@ -334,7 +336,7 @@ impl StatsReply {
)
}
pub fn receiver(sid: SessionId, receiver: ReceiverStats, node: NodeStats) -> Self {
pub fn receiver(sid: SessionId, receiver: ReceiverStats, node: NodeStats) -> Result<Self, AllocError> {
Self::new(
StatsReplyFlags::IS_RECEIVER,
types::StatsReplyPacket { sid, receiver, node },

View file

@ -576,7 +576,9 @@ pub fn run(opt: ReceiveOpt) -> Result<(), RunError> {
let receiver = *state.recv.stats();
drop(state);
let reply = StatsReply::receiver(sid, receiver, node);
let reply = StatsReply::receiver(sid, receiver, node)
.expect("allocate StatsReply packet");
let _ = protocol.send_to(reply.as_packet(), peer);
}
Some(PacketKind::StatsReply(_)) => {

View file

@ -138,7 +138,8 @@ impl ProtocolSocket {
pub fn recv_from(&self) -> Result<(Packet, PeerId), io::Error> {
loop {
let mut buffer = PacketBuffer::allocate();
let mut buffer = PacketBuffer::allocate()
.expect("allocate PacketBuffer");
let (nbytes, peer) = self.socket.recv_from(buffer.as_full_buffer_mut())?;
buffer.set_len(nbytes);

View file

@ -33,7 +33,9 @@ pub fn run(opt: StatsOpt) -> Result<(), RunError> {
std::thread::spawn({
let protocol = Arc::clone(&protocol);
move || {
let request = StatsRequest::new();
let request = StatsRequest::new()
.expect("allocate StatsRequest packet");
loop {
let _ = protocol.broadcast(request.as_packet());
std::thread::sleep(Duration::from_millis(100));

View file

@ -62,7 +62,8 @@ pub fn run(opt: StreamOpt) -> Result<(), RunError> {
dts: TimestampMicros(0),
};
let mut audio_buffer = Audio::write();
let mut audio_buffer = Audio::write()
.expect("allocate Audio packet");
let stream = device.build_input_stream(&config,
{
@ -95,7 +96,8 @@ pub fn run(opt: StreamOpt) -> Result<(), RunError> {
// if packet buffer is full, finalize it and send off the packet:
if audio_buffer.valid_length() {
// take packet writer and replace with new
let audio = std::mem::replace(&mut audio_buffer, Audio::write());
let audio = std::mem::replace(&mut audio_buffer,
Audio::write().expect("allocate Audio packet"));
// finalize packet
let audio_packet = audio.finalize(AudioPacketHeader {
@ -134,7 +136,8 @@ pub fn run(opt: StreamOpt) -> Result<(), RunError> {
let protocol = Arc::clone(&protocol);
move || {
let mut time = packet::Time::allocate();
let mut time = packet::Time::allocate()
.expect("allocate Time packet");
// set up packet
let data = time.data_mut();
@ -190,7 +193,9 @@ pub fn run(opt: StreamOpt) -> Result<(), RunError> {
}
Some(PacketKind::StatsRequest(_)) => {
let reply = StatsReply::source(sid, node);
let reply = StatsReply::source(sid, node)
.expect("allocate StatsReply packet");
let _ = protocol.send_to(reply.as_packet(), peer);
}
Some(PacketKind::StatsReply(_)) => {