use crate::{Machine, Terminated};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HierarchicalDispatch {
Child,
ChildTerminated,
Parent,
Unhandled,
}
pub struct Hierarchical<P, C> {
parent: P,
child: C,
child_active: bool,
}
impl<P, C> Hierarchical<P, C> {
pub const fn new(parent: P, child: C) -> Self {
Self {
parent,
child,
child_active: false,
}
}
pub const fn new_active(parent: P, child: C) -> Self {
Self {
parent,
child,
child_active: true,
}
}
pub fn activate_child(&mut self) {
self.child_active = true;
}
pub fn deactivate_child(&mut self) {
self.child_active = false;
}
pub fn reset_child(&mut self, child: C) {
self.child = child;
self.child_active = true;
}
pub const fn child_is_active(&self) -> bool {
self.child_active
}
pub fn parent(&self) -> &P {
&self.parent
}
pub fn parent_mut(&mut self) -> &mut P {
&mut self.parent
}
pub fn child(&self) -> &C {
&self.child
}
pub fn child_mut(&mut self) -> &mut C {
&mut self.child
}
pub fn process_event<E, FC, FP>(
&mut self,
event: E,
mut child_dispatch: FC,
mut parent_dispatch: FP,
) -> HierarchicalDispatch
where
E: Clone,
C: Terminated,
FC: FnMut(&mut C, E) -> bool,
FP: FnMut(&mut P, E) -> bool,
{
if self.child_active && child_dispatch(&mut self.child, event.clone()) {
return if self.child.is_terminated() {
HierarchicalDispatch::ChildTerminated
} else {
HierarchicalDispatch::Child
};
}
if parent_dispatch(&mut self.parent, event) {
HierarchicalDispatch::Parent
} else {
HierarchicalDispatch::Unhandled
}
}
pub fn process_event_with_completion<E, FC, FP, FT>(
&mut self,
event: E,
mut child_dispatch: FC,
mut parent_dispatch: FP,
mut child_completion: FT,
) -> HierarchicalDispatch
where
E: Clone,
C: Terminated,
FC: FnMut(&mut C, E) -> bool,
FP: FnMut(&mut P, E) -> bool,
FT: FnMut(&mut P) -> bool,
{
if self.child_active && child_dispatch(&mut self.child, event.clone()) {
if self.child.is_terminated() {
if child_completion(&mut self.parent) {
self.child_active = false;
return HierarchicalDispatch::Parent;
}
return HierarchicalDispatch::ChildTerminated;
}
return HierarchicalDispatch::Child;
}
if parent_dispatch(&mut self.parent, event) {
HierarchicalDispatch::Parent
} else {
HierarchicalDispatch::Unhandled
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct QueueFull;
pub struct EventQueue<E, const N: usize> {
events: [Option<E>; N],
head: usize,
len: usize,
}
impl<E, const N: usize> EventQueue<E, N> {
pub const fn new() -> Self {
Self {
events: [const { None }; N],
head: 0,
len: 0,
}
}
pub const fn len(&self) -> usize {
self.len
}
pub const fn is_empty(&self) -> bool {
self.len == 0
}
pub fn defer(&mut self, event: E) -> Result<(), QueueFull> {
if self.len == N || N == 0 {
return Err(QueueFull);
}
let tail = (self.head + self.len) % N;
self.events[tail] = Some(event);
self.len += 1;
Ok(())
}
pub fn process(&mut self, event: E) -> Result<(), QueueFull> {
if self.len == N || N == 0 {
return Err(QueueFull);
}
self.head = (self.head + N - 1) % N;
self.events[self.head] = Some(event);
self.len += 1;
Ok(())
}
pub fn pop(&mut self) -> Option<E> {
if self.len == 0 {
return None;
}
let event = self.events[self.head].take();
self.head = (self.head + 1) % N;
self.len -= 1;
event
}
pub fn clear(&mut self) {
while self.pop().is_some() {}
}
}
impl<E, const N: usize> Default for EventQueue<E, N> {
fn default() -> Self {
Self::new()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DispatchStatus {
pub handled: bool,
pub transitioned: bool,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct DispatchSummary {
pub dispatched: usize,
pub handled: usize,
pub transitioned: usize,
}
impl DispatchSummary {
fn record(&mut self, status: DispatchStatus) {
self.dispatched += 1;
self.handled += usize::from(status.handled);
self.transitioned += usize::from(status.transitioned);
}
}
pub struct EventQueues<E, const DEFERRED: usize, const PROCESSED: usize> {
deferred: EventQueue<E, DEFERRED>,
processed: EventQueue<E, PROCESSED>,
}
impl<E, const DEFERRED: usize, const PROCESSED: usize> EventQueues<E, DEFERRED, PROCESSED> {
pub const fn new() -> Self {
Self {
deferred: EventQueue::new(),
processed: EventQueue::new(),
}
}
pub fn defer(&mut self, event: E) -> Result<(), QueueFull> {
self.deferred.defer(event)
}
pub fn process(&mut self, event: E) -> Result<(), QueueFull> {
self.processed.defer(event)
}
pub const fn deferred_len(&self) -> usize {
self.deferred.len()
}
pub const fn processed_len(&self) -> usize {
self.processed.len()
}
pub fn dispatch<M, F>(&mut self, machine: &mut M, event: E, mut dispatch: F) -> DispatchSummary
where
F: FnMut(&mut M, &mut Self, E) -> DispatchStatus,
{
let mut summary = DispatchSummary::default();
let initial = dispatch(machine, self, event);
summary.record(initial);
self.drain_processed(machine, &mut dispatch, &mut summary);
if summary.transitioned > 0 {
let retries = self.deferred.len();
for _ in 0..retries {
if let Some(event) = self.deferred.pop() {
let status = dispatch(machine, self, event);
summary.record(status);
self.drain_processed(machine, &mut dispatch, &mut summary);
}
}
}
summary
}
fn drain_processed<M, F>(
&mut self,
machine: &mut M,
dispatch: &mut F,
summary: &mut DispatchSummary,
) where
F: FnMut(&mut M, &mut Self, E) -> DispatchStatus,
{
while let Some(event) = self.processed.pop() {
summary.record(dispatch(machine, self, event));
}
}
}
impl<E, const DEFERRED: usize, const PROCESSED: usize> Default
for EventQueues<E, DEFERRED, PROCESSED>
{
fn default() -> Self {
Self::new()
}
}
pub struct DispatchTable<'a, M, Raw, R> {
machine: &'a mut M,
first_id: usize,
handlers: &'a [fn(&mut M, &Raw) -> R],
}
impl<'a, M, Raw, R> DispatchTable<'a, M, Raw, R> {
pub const fn new(
machine: &'a mut M,
first_id: usize,
handlers: &'a [fn(&mut M, &Raw) -> R],
) -> Self {
Self {
machine,
first_id,
handlers,
}
}
#[inline]
pub fn dispatch(&mut self, raw: &Raw, id: usize) -> Option<R> {
let index = id.checked_sub(self.first_id)?;
let handler = *self.handlers.get(index)?;
Some(handler(self.machine, raw))
}
pub fn machine(&self) -> &M {
self.machine
}
pub fn machine_mut(&mut self) -> &mut M {
self.machine
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IndexedEvent<E> {
pub index: usize,
pub event: E,
}
pub const fn with_id<E>(index: usize, event: E) -> IndexedEvent<E> {
IndexedEvent { index, event }
}
pub struct SmPool<S> {
storage: S,
}
pub struct OrthogonalRegions<S> {
regions: S,
}
impl<S> OrthogonalRegions<S> {
pub const fn new(regions: S) -> Self {
Self { regions }
}
pub fn regions(&self) -> &S {
&self.regions
}
pub fn regions_mut(&mut self) -> &mut S {
&mut self.regions
}
pub fn process_event<M, E>(&mut self, event: E) -> usize
where
S: AsMut<[M]>,
M: Machine<E>,
E: Clone,
{
self.regions.as_mut().iter_mut().fold(0, |handled, region| {
handled + usize::from(Machine::process_event(region, event.clone()))
})
}
}
impl<S> SmPool<S> {
pub const fn new(storage: S) -> Self {
Self { storage }
}
pub fn storage(&self) -> &S {
&self.storage
}
pub fn storage_mut(&mut self) -> &mut S {
&mut self.storage
}
pub fn reset<M, F>(&mut self, mut initialize: F)
where
S: AsMut<[M]>,
F: FnMut(usize) -> M,
{
for (index, machine) in self.storage.as_mut().iter_mut().enumerate() {
*machine = initialize(index);
}
}
#[inline]
pub fn process_indexed<M, E, R, F>(&mut self, index: usize, event: E, dispatch: F) -> Option<R>
where
S: AsMut<[M]>,
F: FnOnce(&mut M, E) -> R,
{
self.storage
.as_mut()
.get_mut(index)
.map(|machine| dispatch(machine, event))
}
pub fn process_indexed_batch<M, E, I, F>(
&mut self,
indices: I,
event: E,
mut dispatch: F,
) -> usize
where
S: AsMut<[M]>,
E: Clone,
I: IntoIterator<Item = usize>,
F: FnMut(&mut M, E),
{
let machines = self.storage.as_mut();
let mut handled = 0;
for index in indices {
if let Some(machine) = machines.get_mut(index) {
dispatch(machine, event.clone());
handled += 1;
}
}
handled
}
pub fn process_event_batch<M, E, I, F>(&mut self, events: I, mut dispatch: F) -> usize
where
S: AsMut<[M]>,
I: IntoIterator<Item = IndexedEvent<E>>,
F: FnMut(&mut M, E),
{
let machines = self.storage.as_mut();
let mut handled = 0;
for IndexedEvent { index, event } in events {
if let Some(machine) = machines.get_mut(index) {
dispatch(machine, event);
handled += 1;
}
}
handled
}
}