use std::{
fmt::Debug,
ops::{Index, IndexMut},
};
use zenoh_config::WhatAmI;
use zenoh_protocol::core::Region;
#[derive(Debug, Default)]
pub(crate) struct RegionMap<D> {
buf: Vec<Option<D>>,
}
impl<D> RegionMap<D> {
pub(crate) fn get(&self, region: &Region) -> Option<&D> {
self.buf
.get(Self::region_to_index(region))
.and_then(|o| o.as_ref())
}
pub(crate) fn clear(&mut self) {
self.buf.clear();
}
pub(crate) fn get_mut(&mut self, region: &Region) -> Option<&mut D> {
self.buf
.get_mut(Self::region_to_index(region))
.and_then(|o| o.as_mut())
}
pub(crate) fn insert(&mut self, region: Region, value: D) -> Option<D> {
let idx = Self::region_to_index(®ion);
if self.buf.len() < idx + 1 {
self.buf.resize_with(idx + 1, || None);
}
self.buf[idx].replace(value)
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (Region, &D)> {
self.buf
.iter()
.enumerate()
.filter_map(|(i, v)| v.as_ref().map(|v| (Self::index_to_region(i), v)))
}
pub(crate) fn iter_mut(&mut self) -> impl Iterator<Item = (Region, &mut D)> {
self.buf
.iter_mut()
.enumerate()
.filter_map(|(i, v)| v.as_mut().map(|v| (Self::index_to_region(i), v)))
}
pub(crate) fn partition_mut(&mut self, region: &Region) -> Option<(&mut D, RegionMap<&mut D>)> {
let mut main = None;
let mut others = vec![];
others.resize_with(self.buf.len(), || None);
for (i, v) in self.buf.iter_mut().enumerate() {
if i == Self::region_to_index(region) {
main = Some(v.as_mut().unwrap_or_else(|| unreachable!()));
} else {
others[i] = v.as_mut();
}
}
Some((main?, RegionMap { buf: others }))
}
pub(crate) fn partition(&self, region: &Region) -> Option<(&D, RegionMap<&D>)> {
let mut main = None;
let mut others = vec![];
others.resize_with(self.buf.len(), || None);
for (i, v) in self.buf.iter().enumerate() {
if i == Self::region_to_index(region) {
main = Some(v.as_ref().unwrap_or_else(|| unreachable!()));
} else {
others[i] = v.as_ref();
}
}
Some((main?, RegionMap { buf: others }))
}
pub(crate) fn regions(&self) -> impl Iterator<Item = Region> + '_ {
self.buf
.iter()
.enumerate()
.filter_map(|(i, v)| v.is_some().then_some(Self::index_to_region(i)))
}
pub(crate) fn values(&self) -> impl Iterator<Item = &D> + '_ {
self.buf.iter().filter_map(|v| v.as_ref())
}
pub(crate) fn values_mut(&mut self) -> impl Iterator<Item = &mut D> + '_ {
self.buf.iter_mut().filter_map(|v| v.as_mut())
}
pub(crate) fn map_ref<F, E>(&self, f: F) -> RegionMap<E>
where
F: Fn(&D) -> E,
{
RegionMap {
buf: self.buf.iter().map(|d| d.as_ref().map(&f)).collect(),
}
}
pub(crate) fn map<F, E>(self, f: F) -> RegionMap<E>
where
F: Fn(D) -> E,
{
RegionMap {
buf: self.buf.into_iter().map(|d| d.map(&f)).collect(),
}
}
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Region, D)> {
self.buf
.into_iter()
.enumerate()
.filter_map(|(i, v)| v.map(|v| (Self::index_to_region(i), v)))
}
fn region_to_index(region: &Region) -> usize {
let mode_offset = |mode: &WhatAmI| match mode {
WhatAmI::Router => 0,
WhatAmI::Peer => 1,
WhatAmI::Client => 2,
};
match region {
Region::North => 0,
Region::Local => 1,
Region::South { id, mode } => 2 + 3 * id + mode_offset(mode),
}
}
fn index_to_region(idx: usize) -> Region {
match idx {
0 => Region::North,
1 => Region::Local,
n => Region::South {
id: (n - 2) / 3,
mode: match (n - 2) % 3 {
0 => WhatAmI::Router,
1 => WhatAmI::Peer,
2 => WhatAmI::Client,
_ => unreachable!(),
},
},
}
}
}
impl<D> FromIterator<(Region, D)> for RegionMap<D> {
fn from_iter<T: IntoIterator<Item = (Region, D)>>(iter: T) -> Self {
let mut res = Self { buf: vec![] };
for (r, d) in iter.into_iter() {
res.insert(r, d);
}
res
}
}
impl<D> Index<&Region> for RegionMap<D> {
type Output = D;
fn index(&self, region: &Region) -> &Self::Output {
self.get(region).unwrap()
}
}
impl<D> Index<Region> for RegionMap<D> {
type Output = D;
fn index(&self, region: Region) -> &Self::Output {
self.index(®ion)
}
}
impl<D> IndexMut<&Region> for RegionMap<D> {
fn index_mut(&mut self, region: &Region) -> &mut Self::Output {
self.get_mut(region).unwrap()
}
}
impl<D> IndexMut<Region> for RegionMap<D> {
fn index_mut(&mut self, region: Region) -> &mut Self::Output {
self.index_mut(®ion)
}
}
#[test]
fn region_indexes() {
let regions = [
Region::North,
Region::Local,
Region::South {
id: 0,
mode: WhatAmI::Router,
},
Region::South {
id: 3,
mode: WhatAmI::Peer,
},
Region::South {
id: 35,
mode: WhatAmI::Client,
},
];
for region in regions {
assert_eq!(
region,
RegionMap::<()>::index_to_region(RegionMap::<()>::region_to_index(®ion))
);
}
}