#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
const PROCESSORS_PER_GROUP: u32 = 64;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct ProcessorGroupAffinity {
group: u16,
mask: usize,
}
impl ProcessorGroupAffinity {
#[must_use]
pub fn from_processor(processor: u32) -> Option<Self> {
let group = u16::try_from(processor / PROCESSORS_PER_GROUP).ok()?;
let bit = processor % PROCESSORS_PER_GROUP;
let mask = 1usize.checked_shl(bit)?;
Some(Self { group, mask })
}
#[must_use]
pub const fn group(self) -> u16 {
self.group
}
#[must_use]
pub const fn mask(self) -> usize {
self.mask
}
#[must_use]
pub const fn processor_count(self) -> u32 {
self.mask.count_ones()
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ProcessorAffinityGroups {
groups: Box<[ProcessorGroupAffinity]>,
unassigned_processors: Box<[u32]>,
}
impl ProcessorAffinityGroups {
#[must_use]
pub fn from_processors(processors: impl IntoIterator<Item = u32>) -> Self {
let mut groups: Vec<ProcessorGroupAffinity> = Vec::new();
let mut unassigned_processors = Vec::new();
for processor in processors {
let Some(affinity) = ProcessorGroupAffinity::from_processor(processor) else {
if let Err(position) = unassigned_processors.binary_search(&processor) {
unassigned_processors.insert(position, processor);
}
continue;
};
match groups.binary_search_by_key(&affinity.group, |candidate| candidate.group) {
Ok(position) => groups[position].mask |= affinity.mask,
Err(position) => groups.insert(position, affinity),
}
}
Self {
groups: groups.into_boxed_slice(),
unassigned_processors: unassigned_processors.into_boxed_slice(),
}
}
#[must_use]
pub fn groups(&self) -> &[ProcessorGroupAffinity] {
&self.groups
}
#[must_use]
pub fn group(&self, group: u16) -> Option<ProcessorGroupAffinity> {
self.groups
.binary_search_by_key(&group, |candidate| candidate.group)
.ok()
.map(|position| self.groups[position])
}
#[must_use]
pub fn unassigned_processors(&self) -> &[u32] {
&self.unassigned_processors
}
#[must_use]
pub fn is_complete(&self) -> bool {
self.unassigned_processors.is_empty()
}
#[must_use]
pub fn assigned_processor_count(&self) -> usize {
self.groups
.iter()
.map(|group| group.processor_count() as usize)
.sum()
}
#[must_use]
pub fn requested_processor_count(&self) -> usize {
self.assigned_processor_count() + self.unassigned_processors.len()
}
#[must_use]
pub fn largest_group(&self) -> Option<ProcessorGroupAffinity> {
let mut groups = self.groups.iter().copied();
let mut largest = groups.next()?;
for candidate in groups {
if candidate.processor_count() > largest.processor_count() {
largest = candidate;
}
}
Some(largest)
}
}