extern crate alloc;
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use core::num::NonZeroU64;
use super::placement::Dot;
pub(super) type PageKey = (u32, u64);
const PAGE: u64 = 64;
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(super) struct OccupancyPlane {
stations: BTreeMap<u32, Vec<(u64, u64)>>,
}
impl OccupancyPlane {
pub(super) const fn new() -> Self {
Self {
stations: BTreeMap::new(),
}
}
#[cfg(feature = "instrumentation")]
pub(super) fn page_count(&self) -> usize {
self.stations.values().map(Vec::len).sum()
}
#[cfg(feature = "instrumentation")]
pub(super) fn station_count(&self) -> usize {
self.stations.len()
}
const fn page_bit(index: u64) -> (u64, u64) {
(index / PAGE, 1u64 << (index % PAGE))
}
pub(super) fn set(&mut self, station: u32, index: u64, present: bool) {
let (page, bit) = Self::page_bit(index);
if present {
let rows = self.stations.entry(station).or_default();
match rows.binary_search_by_key(&page, |&(held, _)| held) {
Ok(at) => rows[at].1 |= bit,
Err(at) => rows.insert(at, (page, bit)),
}
} else if let Some(rows) = self.stations.get_mut(&station)
&& let Ok(at) = rows.binary_search_by_key(&page, |&(held, _)| held)
{
rows[at].1 &= !bit;
if rows[at].1 == 0 {
let _ = rows.remove(at);
if rows.is_empty() {
let _ = self.stations.remove(&station);
}
}
}
}
pub(super) fn set_run(&mut self, station: u32, first: u64, len: u32) {
if len == 0 || first.max(1) > first.saturating_add(u64::from(len) - 1) {
return;
}
let start = first.max(1);
let last = first.saturating_add(u64::from(len) - 1);
let (first_page, last_page) = (start / PAGE, last / PAGE);
let mut staged: Vec<(PageKey, u64)> = Vec::new();
let mut page = first_page;
loop {
let low = if page == first_page { start % PAGE } else { 0 };
let high = if page == last_page {
last % PAGE
} else {
PAGE - 1
};
let mask = if (high - low) == PAGE - 1 {
u64::MAX
} else {
((1u64 << (high - low + 1)) - 1) << low
};
let current = self
.stations
.get(&station)
.and_then(|rows| {
rows.binary_search_by_key(&page, |&(held, _)| held)
.ok()
.map(|slot| rows[slot].1)
})
.unwrap_or(0);
staged.push(((station, page), current | mask));
if page == last_page {
break;
}
page += 1;
}
self.apply_pages(&staged);
}
pub(super) fn from_pages(pages: impl Iterator<Item = (PageKey, u64)>) -> Self {
let mut stations: BTreeMap<u32, Vec<(u64, u64)>> = BTreeMap::new();
for ((station, page), mask) in pages {
debug_assert!(mask != 0, "the page walk carries no zero masks");
let rows = stations.entry(station).or_default();
match rows.last_mut() {
Some((held, bits)) if *held == page => *bits |= mask,
_ => {
debug_assert!(
rows.last().is_none_or(|&(held, _)| held < page),
"the page walk ascends"
);
rows.push((page, mask));
}
}
}
Self { stations }
}
pub(super) fn apply_pages(&mut self, changes: &[(PageKey, u64)]) {
debug_assert!(
changes.windows(2).all(|pair| pair[0].0 < pair[1].0),
"the change batch ascends strictly"
);
let mut at = 0;
while at < changes.len() {
let station = changes[at].0.0;
let end = at
+ changes[at..]
.iter()
.take_while(|&&((held, _), _)| held == station)
.count();
self.apply_station_pages(station, &changes[at..end]);
at = end;
}
}
fn apply_station_pages(&mut self, station: u32, changes: &[(PageKey, u64)]) {
let rows = self.stations.entry(station).or_default();
let incremental = changes.iter().all(|&((_, page), mask)| {
mask != 0
&& match rows.binary_search_by_key(&page, |&(held, _)| held) {
Ok(_) => true,
Err(at) => at == rows.len(),
}
});
if incremental {
for &((_, page), mask) in changes {
match rows.binary_search_by_key(&page, |&(held, _)| held) {
Ok(slot) => rows[slot].1 = mask,
Err(at) => {
debug_assert_eq!(at, rows.len(), "a birth lands past the tail");
rows.push((page, mask));
}
}
}
return;
}
let mut merged: Vec<(u64, u64)> = Vec::with_capacity(rows.len() + changes.len());
let mut held = rows.iter().copied().peekable();
let mut edits = changes
.iter()
.map(|&((_, page), mask)| (page, mask))
.peekable();
loop {
let take_edit = match (held.peek(), edits.peek()) {
(None, None) => break,
(Some(_), None) => false,
(None, Some(_)) => true,
(Some(&(row_page, _)), Some(&(edit_page, _))) => {
if row_page == edit_page {
let _ = held.next();
}
row_page >= edit_page
}
};
let (page, mask) = if take_edit {
edits.next().expect("peeked")
} else {
held.next().expect("peeked")
};
if mask != 0 {
merged.push((page, mask));
}
}
if merged.is_empty() {
let _ = self.stations.remove(&station);
} else {
*rows = merged;
}
}
pub(super) fn apply_flips(&mut self, flips: impl Iterator<Item = (Dot, bool)>) {
let mut staged: Vec<(PageKey, u64)> = Vec::new();
for (dot, present) in flips {
let (page, bit) = Self::page_bit(dot.counter());
let key = (dot.station(), page);
if staged.last().is_none_or(|&(held, _)| held != key) {
debug_assert!(
staged.last().is_none_or(|&(held, _)| held < key),
"the flip stream ascends"
);
let current = self
.stations
.get(&dot.station())
.and_then(|rows| {
rows.binary_search_by_key(&page, |&(held, _)| held)
.ok()
.map(|slot| rows[slot].1)
})
.unwrap_or(0);
staged.push((key, current));
}
let mask = &mut staged.last_mut().expect("just staged").1;
if present {
*mask |= bit;
} else {
*mask &= !bit;
}
}
self.apply_pages(&staged);
}
#[cfg(test)]
pub(super) fn adopt(&mut self, other: &Self) {
self.stations.clone_from(&other.stations);
}
pub(super) fn changed_pages<'a>(
&'a self,
other: &'a Self,
) -> impl Iterator<Item = (PageKey, u64, u64)> + 'a {
let mut held = PageCursor::new(&self.stations);
let mut live = PageCursor::new(&other.stations);
core::iter::from_fn(move || {
loop {
let (key, held_mask, live_mask) = match (held.peek(), live.peek()) {
(None, None) => return None,
(Some(h), None) => {
held.bump();
(h.0, h.1, 0)
}
(None, Some(l)) => {
live.bump();
(l.0, 0, l.1)
}
(Some(h), Some(l)) => match h.0.cmp(&l.0) {
core::cmp::Ordering::Less => {
held.bump();
(h.0, h.1, 0)
}
core::cmp::Ordering::Greater => {
live.bump();
(l.0, 0, l.1)
}
core::cmp::Ordering::Equal => {
held.bump();
live.bump();
(h.0, h.1, l.1)
}
},
};
if held_mask != live_mask {
return Some((key, held_mask, live_mask));
}
}
})
}
pub(super) fn dots_of(key: PageKey, mut mask: u64) -> impl Iterator<Item = Dot> {
core::iter::from_fn(move || {
loop {
if mask == 0 {
return None;
}
let bit = mask.trailing_zeros();
mask &= mask - 1;
if let Some(counter) = NonZeroU64::new(key.1 * PAGE + u64::from(bit)) {
return Some(Dot::new(key.0, counter));
}
}
})
}
pub(super) fn contains(&self, station: u32, index: u64) -> bool {
let (page, bit) = Self::page_bit(index);
self.stations.get(&station).is_some_and(|rows| {
rows.binary_search_by_key(&page, |&(held, _)| held)
.is_ok_and(|slot| rows[slot].1 & bit != 0)
})
}
#[cfg(test)]
fn from_dots(dots: impl Iterator<Item = Dot>) -> Self {
let mut plane = Self::new();
for dot in dots {
plane.set(dot.station(), dot.counter(), true);
}
plane
}
#[cfg(test)]
pub(super) fn check_against(&self, dots: impl Iterator<Item = Dot>) {
for rows in self.stations.values() {
assert!(!rows.is_empty(), "no empty stations retained");
assert!(
rows.windows(2).all(|pair| pair[0].0 < pair[1].0),
"pages ascend strictly within a fiber"
);
assert!(
rows.iter().all(|&(_, mask)| mask != 0),
"no zero masks retained"
);
}
assert_eq!(
self,
&Self::from_dots(dots),
"the occupancy plane agrees with its carried set"
);
}
}
struct PageCursor<'a> {
stations: alloc::collections::btree_map::Iter<'a, u32, Vec<(u64, u64)>>,
current: Option<(u32, &'a [(u64, u64)])>,
}
impl<'a> PageCursor<'a> {
fn new(stations: &'a BTreeMap<u32, Vec<(u64, u64)>>) -> Self {
let mut cursor = Self {
stations: stations.iter(),
current: None,
};
cursor.refill();
cursor
}
fn refill(&mut self) {
while self.current.is_none_or(|(_, rows)| rows.is_empty()) {
if let Some((&station, rows)) = self.stations.next() {
self.current = Some((station, rows.as_slice()));
} else {
self.current = None;
return;
}
}
}
fn peek(&self) -> Option<(PageKey, u64)> {
self.current.map(|(station, rows)| {
let (page, mask) = rows[0];
((station, page), mask)
})
}
fn bump(&mut self) {
if let Some((station, rows)) = self.current {
self.current = Some((station, &rows[1..]));
self.refill();
}
}
}
#[cfg(test)]
mod tests;