use bitcode::{Decode, Encode};
use parking_lot::Mutex;
use crate::address::AddressManager;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct PageFlushRange {
pub from_address: u64,
pub until_address: u64,
}
impl PageFlushRange {
#[inline]
pub const fn new(from_address: u64, until_address: u64) -> Self {
Self {
from_address,
until_address,
}
}
#[inline]
pub const fn len(&self) -> u64 {
self.until_address.saturating_sub(self.from_address)
}
#[inline]
pub const fn is_empty(&self) -> bool {
self.from_address >= self.until_address
}
}
#[derive(Debug, Default)]
pub struct PendingFlushList {
list: Mutex<Vec<PageFlushRange>>,
completed: Mutex<Vec<PageFlushRange>>,
}
impl PendingFlushList {
pub fn new() -> Self {
Self {
list: Mutex::new(Vec::with_capacity(16)),
completed: Mutex::new(Vec::with_capacity(16)),
}
}
pub fn add(&self, range: PageFlushRange) {
if range.is_empty() {
return;
}
self.list.lock().push(range);
}
pub fn remove_previous_adjacent(&self, address: u64) -> Option<PageFlushRange> {
let mut list = self.list.lock();
list
.iter()
.position(|r| r.until_address == address)
.map(|pos| list.swap_remove(pos))
}
pub fn remove_next_adjacent(&self, address: u64) -> Option<PageFlushRange> {
let mut list = self.list.lock();
list
.iter()
.position(|r| r.from_address == address)
.map(|pos| list.swap_remove(pos))
}
pub fn coalesce(&self, mut range: PageFlushRange) -> PageFlushRange {
let mut list = self.list.lock();
while let Some(pos) = list
.iter()
.position(|r| r.until_address == range.from_address)
{
range.from_address = list.swap_remove(pos).from_address;
}
while let Some(pos) = list
.iter()
.position(|r| r.from_address == range.until_address)
{
range.until_address = list.swap_remove(pos).until_address;
}
range
}
pub fn complete_flush_range(&self, range: PageFlushRange, addrs: &AddressManager) {
if range.is_empty() {
return;
}
let tail_cap = addrs.tail();
let mut completed = self.completed.lock();
let current_flushed = addrs.flushed_until();
if range.from_address <= current_flushed {
let mut new_flushed = range.until_address.min(tail_cap).max(current_flushed);
completed.retain(|r| {
if r.from_address <= new_flushed {
let next_until = r.until_address.min(tail_cap);
if next_until > new_flushed {
new_flushed = next_until;
}
false
} else {
true
}
});
addrs.shift_flushed_until_address(new_flushed);
} else {
let pos = completed.partition_point(|r| r.from_address < range.from_address);
completed.insert(pos, range);
}
}
pub fn clear(&self) {
self.list.lock().clear();
self.completed.lock().clear();
}
pub fn len(&self) -> usize {
self.list.lock().len()
}
pub fn is_empty(&self) -> bool {
self.list.lock().is_empty() && self.completed.lock().is_empty()
}
pub fn completed_len(&self) -> usize {
self.completed.lock().len()
}
}