use std::collections::BTreeMap;
pub(crate) const MAX_PROCESSORS_PER_GROUP: u32 = usize::BITS;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ProcessorSet {
groups: BTreeMap<u16, usize>,
}
impl ProcessorSet {
#[must_use]
pub fn empty() -> Self {
Self::default()
}
#[must_use]
pub fn from_group_mask(group: u16, mask: usize) -> Self {
let mut set = Self::empty();
if mask != 0 {
set.groups.insert(group, mask);
}
set
}
#[must_use]
pub fn can_represent(number: u8) -> bool {
u32::from(number) < MAX_PROCESSORS_PER_GROUP
}
pub fn insert(&mut self, group: u16, number: u8) {
assert!(
u32::from(number) < MAX_PROCESSORS_PER_GROUP,
"a processor group has at most {MAX_PROCESSORS_PER_GROUP} processors, got {number}"
);
*self.groups.entry(group).or_insert(0) |= 1_usize << number;
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.groups.is_empty()
}
#[must_use]
pub fn len(&self) -> usize {
self.groups
.values()
.map(|mask| mask.count_ones() as usize)
.sum()
}
#[must_use]
pub fn contains(&self, group: u16, number: u8) -> bool {
if u32::from(number) >= MAX_PROCESSORS_PER_GROUP {
return false;
}
self.groups
.get(&group)
.is_some_and(|mask| mask & (1_usize << number) != 0)
}
#[must_use]
pub fn group_mask(&self, group: u16) -> usize {
self.groups.get(&group).copied().unwrap_or(0)
}
pub fn group_masks(&self) -> impl Iterator<Item = (u16, usize)> + '_ {
self.groups.iter().map(|(&group, &mask)| (group, mask))
}
pub fn iter(&self) -> impl Iterator<Item = (u16, u8)> + '_ {
self.groups.iter().flat_map(|(&group, &mask)| {
(0..MAX_PROCESSORS_PER_GROUP as u8)
.filter(move |&n| mask & (1_usize << n) != 0)
.map(move |n| (group, n))
})
}
#[must_use]
pub fn intersection(&self, other: &Self) -> Self {
let mut result = Self::empty();
for (&group, &mask) in &self.groups {
let combined = mask & other.groups.get(&group).copied().unwrap_or(0);
if combined != 0 {
result.groups.insert(group, combined);
}
}
result
}
#[must_use]
pub fn union(&self, other: &Self) -> Self {
let mut result = self.clone();
for (&group, &mask) in &other.groups {
*result.groups.entry(group).or_insert(0) |= mask;
}
result
}
#[must_use]
pub fn is_disjoint(&self, other: &Self) -> bool {
self.groups.iter().all(|(group, &mask)| {
other
.groups
.get(group)
.is_none_or(|&other_mask| mask & other_mask == 0)
})
}
#[must_use]
pub fn is_subset(&self, other: &Self) -> bool {
self.groups.iter().all(|(group, &mask)| {
other
.groups
.get(group)
.is_some_and(|&other_mask| mask & !other_mask == 0)
})
}
}
impl FromIterator<(u16, u8)> for ProcessorSet {
fn from_iter<T: IntoIterator<Item = (u16, u8)>>(iter: T) -> Self {
let mut set = Self::empty();
for (group, number) in iter {
set.insert(group, number);
}
set
}
}
#[cfg(feature = "serde")]
#[derive(serde::Serialize, serde::Deserialize)]
struct WireProcessor {
group: u16,
number: u8,
}
#[cfg(feature = "serde")]
impl serde::Serialize for ProcessorSet {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for (group, number) in self.iter() {
seq.serialize_element(&WireProcessor { group, number })?;
}
seq.end()
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for ProcessorSet {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let wire = Vec::<WireProcessor>::deserialize(deserializer)?;
let mut set = Self::empty();
for entry in wire {
if u32::from(entry.number) >= MAX_PROCESSORS_PER_GROUP {
return Err(serde::de::Error::custom(format!(
"processor number {} is out of range: a processor group has at most \
{MAX_PROCESSORS_PER_GROUP} processors on this platform",
entry.number
)));
}
set.insert(entry.group, entry.number);
}
Ok(set)
}
}
#[cfg(test)]
mod tests;