1use alloc::boxed::Box;
38use alloc::collections::BTreeMap;
39use alloc::{vec, vec::Vec};
40use core::any::{Any, TypeId};
41use core::marker::PhantomData;
42use core::sync::atomic::{AtomicU32, Ordering};
43
44use crate::metadata::{ModuleID, ModuleSlot};
45
46type CapabilityMap = BTreeMap<TypeId, Box<dyn Any + Send>>;
47
48static NEXT_REGISTRY_ID: AtomicU32 = AtomicU32::new(0);
49
50#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
52pub struct ResourceId {
53 kind: ResourceIdKind,
54}
55
56#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
57enum ResourceIdKind {
58 ModuleLocal { slot: ModuleSlot, local_id: u16 },
59 RegistryAllocated(u32),
60}
61
62impl ResourceId {
63 pub const fn module_local(slot: ModuleSlot, local_id: u16) -> Self {
65 Self {
66 kind: ResourceIdKind::ModuleLocal { slot, local_id },
67 }
68 }
69
70 pub const fn module_local_parts(self) -> Option<(ModuleSlot, u16)> {
73 match self.kind {
74 ResourceIdKind::ModuleLocal { slot, local_id } => Some((slot, local_id)),
75 ResourceIdKind::RegistryAllocated(_) => None,
76 }
77 }
78
79 const fn registry_allocated(id: u32) -> Self {
80 Self {
81 kind: ResourceIdKind::RegistryAllocated(id),
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
88pub enum ResourceOrigin {
89 Platform,
91 Module,
93}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)]
96enum ResourceMetadata {
97 PlatformAllocated {
98 allocated_id: u32,
99 },
100 ModuleAllocated {
101 allocated_id: u32,
102 slot: ModuleSlot,
103 module_id: ModuleID,
104 },
105 ModuleLocal {
106 local_id: u16,
107 slot: ModuleSlot,
108 module_id: ModuleID,
109 },
110}
111
112impl ResourceMetadata {
113 const fn id(self) -> ResourceId {
114 match self {
115 Self::PlatformAllocated { allocated_id }
116 | Self::ModuleAllocated { allocated_id, .. } => {
117 ResourceId::registry_allocated(allocated_id)
118 }
119 Self::ModuleLocal { slot, local_id, .. } => ResourceId::module_local(slot, local_id),
120 }
121 }
122
123 const fn origin(self) -> ResourceOrigin {
124 match self {
125 Self::PlatformAllocated { .. } => ResourceOrigin::Platform,
126 Self::ModuleAllocated { .. } | Self::ModuleLocal { .. } => ResourceOrigin::Module,
127 }
128 }
129
130 const fn slot(self) -> Option<ModuleSlot> {
131 match self {
132 Self::PlatformAllocated { .. } => None,
133 Self::ModuleAllocated { slot, .. } | Self::ModuleLocal { slot, .. } => Some(slot),
134 }
135 }
136
137 const fn module_id(self) -> Option<ModuleID> {
138 match self {
139 Self::PlatformAllocated { .. } => None,
140 Self::ModuleAllocated { module_id, .. } | Self::ModuleLocal { module_id, .. } => {
141 Some(module_id)
142 }
143 }
144 }
145}
146
147struct ResourceGroup {
148 metadata: ResourceMetadata,
149 capabilities: CapabilityMap,
150}
151
152struct ResourceGroupSlot {
153 id: ResourceId,
154 available: Option<ResourceGroup>,
155}
156
157#[must_use = "dropping a resource lease permanently removes its complete group"]
163pub struct ResourceLease<T> {
164 registry_id: u32,
165 group: ResourceGroup,
166 resource_type: PhantomData<T>,
167}
168
169impl<T: 'static> ResourceLease<T> {
170 pub const fn id(&self) -> ResourceId {
172 self.group.metadata.id()
173 }
174
175 pub const fn origin(&self) -> ResourceOrigin {
177 self.group.metadata.origin()
178 }
179
180 pub const fn slot(&self) -> Option<ModuleSlot> {
182 self.group.metadata.slot()
183 }
184
185 pub const fn module_id(&self) -> Option<ModuleID> {
187 self.group.metadata.module_id()
188 }
189
190 pub fn resource(&self) -> &T {
192 self.group
193 .capabilities
194 .get(&TypeId::of::<T>())
195 .and_then(|resource| resource.downcast_ref())
196 .expect("resource lease contains its selected capability")
197 }
198
199 pub fn resource_mut(&mut self) -> &mut T {
201 self.group
202 .capabilities
203 .get_mut(&TypeId::of::<T>())
204 .and_then(|resource| resource.downcast_mut())
205 .expect("resource lease contains its selected capability")
206 }
207}
208
209mod private {
210 pub trait GroupCapabilitiesSealed {}
211 pub trait ResourceGroupsSealed {}
212 pub trait ResourceSetSealed {}
213}
214
215pub trait ResourceGroupCapabilities: private::GroupCapabilitiesSealed {
218 #[doc(hidden)]
219 fn into_capabilities(self) -> Result<CapabilityMap, RegistryError>;
220}
221
222pub trait ResourceGroups: private::ResourceGroupsSealed {
224 #[doc(hidden)]
225 fn into_groups(self) -> Result<Vec<CapabilityMap>, RegistryError>;
226}
227
228pub trait ResourceSet: private::ResourceSetSealed {
232 type Leases;
234
235 #[doc(hidden)]
236 fn is_available(registry: &Registry) -> bool;
237
238 #[doc(hidden)]
239 fn take(registry: &mut Registry) -> Option<Self::Leases>;
240}
241
242#[derive(Debug, Clone, Copy, PartialEq, Eq, defmt::Format)]
243pub enum RegistryError {
245 DuplicateCapability,
247 DuplicateResourceId,
249}
250
251pub struct Registry {
257 id: u32,
258 groups: Vec<ResourceGroupSlot>,
259 next_registry_allocated_id: u32,
260}
261
262impl Default for Registry {
263 fn default() -> Self {
264 Self::new()
265 }
266}
267
268impl Registry {
269 pub fn new() -> Self {
274 Self {
275 id: NEXT_REGISTRY_ID
276 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
277 .expect("registry id counter overflowed"),
278 groups: Vec::new(),
279 next_registry_allocated_id: 0,
280 }
281 }
282
283 pub fn register<T: 'static + Send>(
285 &mut self,
286 slot: ModuleSlot,
287 module_id: ModuleID,
288 resource: T,
289 ) {
290 let allocated_id = self.next_registry_allocated_id();
291 self.insert_group(
292 ResourceMetadata::ModuleAllocated {
293 allocated_id,
294 slot,
295 module_id,
296 },
297 single_capability(resource),
298 )
299 .expect("registry-allocated resource IDs are unique");
300 }
301
302 pub fn register_group<C: ResourceGroupCapabilities>(
310 &mut self,
311 slot: ModuleSlot,
312 module_id: ModuleID,
313 capabilities: C,
314 ) -> Result<(), RegistryError> {
315 let capabilities = capabilities.into_capabilities()?;
316 let allocated_id = self.next_registry_allocated_id();
317 self.insert_group(
318 ResourceMetadata::ModuleAllocated {
319 allocated_id,
320 slot,
321 module_id,
322 },
323 capabilities,
324 )
325 }
326
327 pub fn register_local_group<C: ResourceGroupCapabilities>(
338 &mut self,
339 slot: ModuleSlot,
340 module_id: ModuleID,
341 local_id: u16,
342 capabilities: C,
343 ) -> Result<(), RegistryError> {
344 self.insert_group(
345 ResourceMetadata::ModuleLocal {
346 local_id,
347 slot,
348 module_id,
349 },
350 capabilities.into_capabilities()?,
351 )
352 }
353
354 pub fn register_groups<G: ResourceGroups>(
361 &mut self,
362 slot: ModuleSlot,
363 module_id: ModuleID,
364 groups: G,
365 ) -> Result<(), RegistryError> {
366 let groups = groups.into_groups()?;
367 for capabilities in groups {
368 let allocated_id = self.next_registry_allocated_id();
369 self.insert_group(
370 ResourceMetadata::ModuleAllocated {
371 allocated_id,
372 slot,
373 module_id,
374 },
375 capabilities,
376 )
377 .expect("registry-allocated resource IDs are unique");
378 }
379 Ok(())
380 }
381
382 pub fn register_platform<T: 'static + Send>(&mut self, resource: T) {
384 let allocated_id = self.next_registry_allocated_id();
385 self.insert_group(
386 ResourceMetadata::PlatformAllocated { allocated_id },
387 single_capability(resource),
388 )
389 .expect("registry-allocated resource IDs are unique");
390 }
391
392 pub fn register_platform_group<C: ResourceGroupCapabilities>(
400 &mut self,
401 capabilities: C,
402 ) -> Result<(), RegistryError> {
403 let capabilities = capabilities.into_capabilities()?;
404 let allocated_id = self.next_registry_allocated_id();
405 self.insert_group(
406 ResourceMetadata::PlatformAllocated { allocated_id },
407 capabilities,
408 )
409 }
410
411 pub fn resource_count<T: 'static + Send>(&self) -> usize {
413 let resource_type = TypeId::of::<T>();
414 self.groups
415 .iter()
416 .filter_map(|slot| slot.available.as_ref())
417 .filter(|group| group.capabilities.contains_key(&resource_type))
418 .count()
419 }
420
421 pub fn has<T: 'static + Send>(&self) -> bool {
423 self.has_at_least::<T>(1)
424 }
425
426 pub fn has_at_least<T: 'static + Send>(&self, count: usize) -> bool {
428 self.resource_count::<T>() >= count
429 }
430
431 pub fn has_resource_set<S: ResourceSet>(&self) -> bool {
434 S::is_available(self)
435 }
436
437 pub fn take_resource<T: 'static + Send>(&mut self) -> Option<ResourceLease<T>> {
439 let resource_type = TypeId::of::<T>();
440 let index = self.groups.iter().rposition(|slot| {
441 slot.available
442 .as_ref()
443 .is_some_and(|group| group.capabilities.contains_key(&resource_type))
444 })?;
445 Some(self.take_group_capability(index))
446 }
447
448 pub fn take_resources<T: 'static + Send>(
452 &mut self,
453 count: usize,
454 ) -> Option<Vec<ResourceLease<T>>> {
455 if count == 0 {
456 return Some(Vec::new());
457 }
458
459 let mut ids = self.resource_ids::<T>();
460 if ids.len() < count {
461 return None;
462 }
463 ids.truncate(count);
464
465 Some(
466 ids.into_iter()
467 .map(|id| self.take_resource_with_id(id))
468 .collect(),
469 )
470 }
471
472 pub fn take_resource_set<S: ResourceSet>(&mut self) -> Option<S::Leases> {
475 S::take(self)
476 }
477
478 pub fn return_resource<T: 'static + Send>(&mut self, lease: ResourceLease<T>) {
485 let ResourceLease {
486 registry_id,
487 group,
488 resource_type: _,
489 } = lease;
490 assert_eq!(
491 registry_id, self.id,
492 "resource lease returned to a different registry"
493 );
494 let id = group.metadata.id();
495 let slot = self
496 .groups
497 .iter_mut()
498 .find(|slot| slot.id == id)
499 .expect("leased resource group remains registered");
500 assert!(
501 slot.available.is_none(),
502 "resource group cannot be returned while already available"
503 );
504 slot.available = Some(group);
505 }
506
507 fn insert_group(
508 &mut self,
509 metadata: ResourceMetadata,
510 capabilities: CapabilityMap,
511 ) -> Result<(), RegistryError> {
512 let id = metadata.id();
513 if self.groups.iter().any(|group| group.id == id) {
514 return Err(RegistryError::DuplicateResourceId);
515 }
516 self.groups.push(ResourceGroupSlot {
517 id,
518 available: Some(ResourceGroup {
519 metadata,
520 capabilities,
521 }),
522 });
523 Ok(())
524 }
525
526 fn next_registry_allocated_id(&mut self) -> u32 {
527 let id = self.next_registry_allocated_id;
528 self.next_registry_allocated_id = self
529 .next_registry_allocated_id
530 .checked_add(1)
531 .expect("registry-allocated resource id counter overflowed");
532 id
533 }
534
535 fn resource_ids<T: 'static + Send>(&self) -> Vec<ResourceId> {
537 let resource_type = TypeId::of::<T>();
538 self.groups
539 .iter()
540 .rev()
541 .filter_map(|slot| slot.available.as_ref())
542 .filter(|group| group.capabilities.contains_key(&resource_type))
543 .map(|group| group.metadata.id())
544 .collect()
545 }
546
547 fn take_resource_with_id<T: 'static + Send>(&mut self, id: ResourceId) -> ResourceLease<T> {
549 let resource_type = TypeId::of::<T>();
550 let index = self
551 .groups
552 .iter()
553 .position(|slot| {
554 slot.id == id
555 && slot
556 .available
557 .as_ref()
558 .is_some_and(|group| group.capabilities.contains_key(&resource_type))
559 })
560 .expect("resource assignment references an available capability");
561 self.take_group_capability(index)
562 }
563
564 fn take_group_capability<T: 'static + Send>(&mut self, index: usize) -> ResourceLease<T> {
566 let group = self.groups[index]
567 .available
568 .take()
569 .expect("selected resource group is available");
570 assert!(
571 group
572 .capabilities
573 .get(&TypeId::of::<T>())
574 .is_some_and(|resource| resource.is::<T>()),
575 "selected resource group contains the requested capability"
576 );
577
578 ResourceLease {
579 registry_id: self.id,
580 group,
581 resource_type: PhantomData,
582 }
583 }
584}
585
586fn single_capability<T: 'static + Send>(resource: T) -> CapabilityMap {
588 let mut capabilities = BTreeMap::new();
589 capabilities.insert(TypeId::of::<T>(), Box::new(resource) as Box<dyn Any + Send>);
590 capabilities
591}
592
593fn resource_assignment(candidates: &[Vec<ResourceId>]) -> Option<Vec<ResourceId>> {
596 fn assign(
597 candidates: &[Vec<ResourceId>],
598 candidate_index: usize,
599 used: &mut Vec<ResourceId>,
600 ) -> bool {
601 if candidate_index == candidates.len() {
602 return true;
603 }
604
605 for &id in &candidates[candidate_index] {
606 if used.contains(&id) {
607 continue;
608 }
609
610 used.push(id);
611 if assign(candidates, candidate_index + 1, used) {
612 return true;
613 }
614 used.pop();
615 }
616
617 false
618 }
619
620 let mut assignment = Vec::new();
621 assign(candidates, 0, &mut assignment).then_some(assignment)
622}
623
624fn types_are_distinct(types: &[TypeId]) -> bool {
626 types
627 .iter()
628 .enumerate()
629 .all(|(index, resource_type)| !types[..index].contains(resource_type))
630}
631
632macro_rules! impl_group_capabilities {
633 ($(($resource:ident, $value:ident)),+) => {
634 impl<$($resource: 'static + Send),+> private::GroupCapabilitiesSealed
635 for ($($resource,)+)
636 {
637 }
638
639 impl<$($resource: 'static + Send),+> ResourceGroupCapabilities for ($($resource,)+) {
640 fn into_capabilities(self) -> Result<CapabilityMap, RegistryError> {
641 if !types_are_distinct(&[$(TypeId::of::<$resource>()),+]) {
642 return Err(RegistryError::DuplicateCapability);
643 }
644
645 let ($($value,)+) = self;
646 let mut capabilities = BTreeMap::new();
647 $(
648 capabilities.insert(
649 TypeId::of::<$resource>(),
650 Box::new($value) as Box<dyn Any + Send>,
651 );
652 )+
653 Ok(capabilities)
654 }
655 }
656 };
657}
658
659impl_group_capabilities!((T1, t1), (T2, t2));
660impl_group_capabilities!((T1, t1), (T2, t2), (T3, t3));
661impl_group_capabilities!((T1, t1), (T2, t2), (T3, t3), (T4, t4));
662impl_group_capabilities!((T1, t1), (T2, t2), (T3, t3), (T4, t4), (T5, t5));
663impl_group_capabilities!((T1, t1), (T2, t2), (T3, t3), (T4, t4), (T5, t5), (T6, t6));
664impl_group_capabilities!(
665 (T1, t1),
666 (T2, t2),
667 (T3, t3),
668 (T4, t4),
669 (T5, t5),
670 (T6, t6),
671 (T7, t7)
672);
673impl_group_capabilities!(
674 (T1, t1),
675 (T2, t2),
676 (T3, t3),
677 (T4, t4),
678 (T5, t5),
679 (T6, t6),
680 (T7, t7),
681 (T8, t8)
682);
683
684macro_rules! impl_resource_groups {
685 ($(($group:ident, $value:ident)),+) => {
686 impl<$($group: ResourceGroupCapabilities),+> private::ResourceGroupsSealed
687 for ($($group,)+)
688 {
689 }
690
691 impl<$($group: ResourceGroupCapabilities),+> ResourceGroups for ($($group,)+) {
692 fn into_groups(self) -> Result<Vec<CapabilityMap>, RegistryError> {
693 let ($($value,)+) = self;
694 Ok(vec![$(($value.into_capabilities()?),)+])
695 }
696 }
697 };
698}
699
700impl_resource_groups!((G1, g1), (G2, g2));
701impl_resource_groups!((G1, g1), (G2, g2), (G3, g3));
702impl_resource_groups!((G1, g1), (G2, g2), (G3, g3), (G4, g4));
703impl_resource_groups!((G1, g1), (G2, g2), (G3, g3), (G4, g4), (G5, g5));
704impl_resource_groups!((G1, g1), (G2, g2), (G3, g3), (G4, g4), (G5, g5), (G6, g6));
705impl_resource_groups!(
706 (G1, g1),
707 (G2, g2),
708 (G3, g3),
709 (G4, g4),
710 (G5, g5),
711 (G6, g6),
712 (G7, g7)
713);
714impl_resource_groups!(
715 (G1, g1),
716 (G2, g2),
717 (G3, g3),
718 (G4, g4),
719 (G5, g5),
720 (G6, g6),
721 (G7, g7),
722 (G8, g8)
723);
724
725macro_rules! impl_resource_set {
726 ($($resource:ident),+) => {
727 impl<$($resource: 'static + Send),+> private::ResourceSetSealed for ($($resource,)+) {}
728
729 impl<$($resource: 'static + Send),+> ResourceSet for ($($resource,)+) {
730 type Leases = ($(ResourceLease<$resource>,)+);
731
732 fn is_available(registry: &Registry) -> bool {
733 if !types_are_distinct(&[$(TypeId::of::<$resource>()),+]) {
734 return false;
735 }
736
737 let candidates = [$(registry.resource_ids::<$resource>()),+];
738 resource_assignment(&candidates).is_some()
739 }
740
741 fn take(registry: &mut Registry) -> Option<Self::Leases> {
742 if !types_are_distinct(&[$(TypeId::of::<$resource>()),+]) {
743 return None;
744 }
745
746 let candidates = [$(registry.resource_ids::<$resource>()),+];
747 let assignment = resource_assignment(&candidates)?;
748 let mut ids = assignment.into_iter();
749 Some(($(
750 registry.take_resource_with_id::<$resource>(
751 ids.next().expect("resource assignment contains every requested type"),
752 ),
753 )+))
754 }
755 }
756 };
757}
758
759impl_resource_set!(T1, T2);
760impl_resource_set!(T1, T2, T3);
761impl_resource_set!(T1, T2, T3, T4);
762impl_resource_set!(T1, T2, T3, T4, T5);
763impl_resource_set!(T1, T2, T3, T4, T5, T6);
764impl_resource_set!(T1, T2, T3, T4, T5, T6, T7);
765impl_resource_set!(T1, T2, T3, T4, T5, T6, T7, T8);
766impl_resource_set!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
767impl_resource_set!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);