Files
tangentbord1/lib/src/util.rs
2024-05-29 23:14:35 +02:00

215 lines
5.8 KiB
Rust

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<T> {
// active_slot: 1 bit
// readers: 31 bits
entries: AtomicU32,
exits: AtomicU32,
slot0: UnsafeCell<MaybeUninit<T>>,
slot1: UnsafeCell<MaybeUninit<T>>,
}
pub struct SwapCellWrite<T: 'static> {
cell: &'static SwapCell<T>,
}
#[derive(Clone)]
pub struct SwapCellRead<T: 'static> {
cell: &'static SwapCell<T>,
}
pub struct SwapCellGuard<'a, T> {
cell: &'a SwapCell<T>,
slot: &'a T,
}
const SLOT_MASK: u32 = 0x80000000;
const ENTRIES_MASK: u32 = 0x7FFFFFFF;
impl<T> SwapCell<T> {
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<T>, SwapCellWrite<T>) {
(SwapCellRead { cell: self }, SwapCellWrite { cell: self })
}
}
impl<T> SwapCellWrite<T> {
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<T> SwapCellRead<T> {
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<T> Drop for SwapCellGuard<'_, T> {
fn drop(&mut self) {
self.cell.exits.fetch_add(1, Ordering::SeqCst);
}
}
impl<T> 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<T>(pub T);
impl<T: Display> MsgPack for DisplayPack<T> {
fn pack(&self) -> impl Iterator<Item = msgpck::Piece<'_>> {
"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<usize, PackErr> {
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<T: Debug> Debug for DisplayPack<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.0, f)
}
}