use core::{ cell::UnsafeCell, fmt::{self, Debug, Display, Write}, mem::MaybeUninit, ops::Deref, }; use embassy_futures::yield_now; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time::{Duration, Timer}; use msgpck::{Marker, MsgPack, PackErr, Piece}; use portable_atomic::{AtomicU32, Ordering}; use crate::rgb::Rgb; pub type CS = CriticalSectionRawMutex; #[derive()] pub struct SwapCell { // active_slot: 1 bit // readers: 31 bits entries: AtomicU32, exits: AtomicU32, slot0: UnsafeCell>, slot1: UnsafeCell>, } pub struct SwapCellWrite { cell: &'static SwapCell, } #[derive(Clone)] pub struct SwapCellRead { cell: &'static SwapCell, } pub struct SwapCellGuard<'a, T> { cell: &'a SwapCell, slot: &'a T, } const SLOT_MASK: u32 = 0x80000000; const ENTRIES_MASK: u32 = 0x7FFFFFFF; impl SwapCell { pub const fn new(initial: T) -> Self { SwapCell { entries: AtomicU32::new(0), exits: AtomicU32::new(0), slot0: UnsafeCell::new(MaybeUninit::new(initial)), slot1: UnsafeCell::new(MaybeUninit::uninit()), } } pub fn split(&'static mut self) -> (SwapCellRead, SwapCellWrite) { (SwapCellRead { cell: self }, SwapCellWrite { cell: self }) } } impl SwapCellWrite { pub async fn write(&mut self, t: T) { let x = self.cell.entries.load(Ordering::SeqCst); let active_slot = (x & SLOT_MASK).rotate_left(1) as usize; let mut slots = [&self.cell.slot0, &self.cell.slot1]; slots.rotate_left(active_slot); let [active_slot, inactive_slot] = slots; // SAFETY: no one elase is allowed to touch the inactive slot. unsafe { inactive_slot.get().write(MaybeUninit::new(t)) }; // swap active/inactive slots let x = self.cell.entries.fetch_xor(SLOT_MASK, Ordering::SeqCst); let (_active_slot, inactive_slot) = (inactive_slot, active_slot); // wait until there are no more readers in the previously active slot let entries = x & ENTRIES_MASK; loop { let exits = self.cell.exits.load(Ordering::SeqCst); if exits >= entries { break; } yield_now().await; } // drop the now inactive slot // SAFETY: we waited until everyone else stopped using this slot. unsafe { inactive_slot.get().drop_in_place() }; } } impl SwapCellRead { pub fn read(&self) -> SwapCellGuard<'_, T> { let x = self.cell.entries.fetch_add(1, Ordering::SeqCst); let entries = x & ENTRIES_MASK; debug_assert!(entries < ENTRIES_MASK, "SwapCell overflowed"); let slot = (x & SLOT_MASK).rotate_left(1) as usize; let slot = [&self.cell.slot0, &self.cell.slot1][slot]; let slot = unsafe { &*slot.get() }; let slot = unsafe { slot.assume_init_ref() }; SwapCellGuard { cell: self.cell, slot, } } } impl Drop for SwapCellGuard<'_, T> { fn drop(&mut self) { self.cell.exits.fetch_add(1, Ordering::SeqCst); } } impl Deref for SwapCellGuard<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { self.slot } } pub async fn stall() -> ! { loop { Timer::after(Duration::from_secs(1)).await; } } /// Input a value 0 to 255 to get a color value // The colours are a transition r - g - b - back to r. pub fn wheel(mut wheel_pos: u8) -> Rgb { wheel_pos = 255 - wheel_pos; if wheel_pos < 85 { Rgb::new(255 - wheel_pos * 3, 0, wheel_pos * 3) } else if wheel_pos < 170 { wheel_pos -= 85; Rgb::new(0, wheel_pos * 3, 255 - wheel_pos * 3) } else { wheel_pos -= 170; Rgb::new(wheel_pos * 3, 255 - wheel_pos * 3, 0) } } /// Calculate the length of the formatted string of a type that impls Display. pub fn display_len(t: &impl Display) -> usize { // impl fmt::Write for a dummy struct that just increments a length struct Write<'a>(&'a mut usize); impl fmt::Write for Write<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { *self.0 += s.len(); Ok(()) } } let mut n = 0; let mut w = Write(&mut n); write!(&mut w, "{}", t).expect("Write impl is infallible"); n } /// Wrapper type that impls [MsgPack] for `T: Display`, by serializing `T` as a msgpck string. pub struct DisplayPack(pub T); impl MsgPack for DisplayPack { fn pack(&self) -> impl Iterator> { "TODO".pack() //let len = display_len(&self.0) as u32; //[msgpck::Marker::Str32.into(), len.into()] // .into_iter() // .chain(iter::from_fn(move || None)) // TODO } fn pack_with_writer(&self, w: &mut dyn msgpck::Write) -> Result { let mut n = 0; let str_len = display_len(&self.0); for bytes in [ Piece::from(Marker::Str32).as_bytes(), Piece::from(str_len as u32).as_bytes(), ] { w.write_all(bytes)?; n += bytes.len(); } // this is advanced stupid. struct Write<'a>(&'a mut dyn msgpck::Write); impl fmt::Write for Write<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { self.0 .write_all(s.as_bytes()) .map_err(|_| fmt::Error::default())?; Ok(()) } } write!(&mut Write(w), "{}", self.0).map_err(|_| PackErr::BufferOverflow)?; n += str_len; Ok(n) } } impl Debug for DisplayPack { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Debug::fmt(&self.0, f) } }