use std::sync::atomic::{AtomicU64, Ordering};
use bitcode::{Decode, Encode};
#[derive(Debug)]
pub struct AddressManager {
pub tail_address: AtomicU64,
pub read_only_address: AtomicU64,
pub safe_read_only_address: AtomicU64,
pub head_address: AtomicU64,
pub safe_head_address: AtomicU64,
pub begin_address: AtomicU64,
pub flushed_until_address: AtomicU64,
}
impl AddressManager {
pub fn new(initial_addr: u64) -> Self {
Self {
tail_address: AtomicU64::new(initial_addr),
read_only_address: AtomicU64::new(initial_addr),
safe_read_only_address: AtomicU64::new(initial_addr),
head_address: AtomicU64::new(initial_addr),
safe_head_address: AtomicU64::new(initial_addr),
begin_address: AtomicU64::new(initial_addr),
flushed_until_address: AtomicU64::new(initial_addr),
}
}
#[inline]
pub fn is_mutable(&self, addr: u64) -> bool {
let tail = self.tail_address.load(Ordering::Acquire);
let ro = self.read_only_address.load(Ordering::Acquire);
let head = self.head_address.load(Ordering::Acquire);
Self::is_mutable_snapshot(addr, head, ro, tail)
}
#[inline]
pub const fn is_mutable_snapshot(addr: u64, head: u64, read_only: u64, tail: u64) -> bool {
let effective_ro = if read_only > head { read_only } else { head };
addr >= effective_ro && addr < tail
}
#[inline]
pub fn is_read_only(&self, addr: u64) -> bool {
let ro = self.read_only_address.load(Ordering::Acquire);
let head = self.head_address.load(Ordering::Acquire);
addr >= head && addr < ro
}
#[inline]
pub fn is_in_memory(&self, addr: u64) -> bool {
let tail = self.tail_address.load(Ordering::Acquire);
let head = self.head_address.load(Ordering::Acquire);
addr >= head && addr < tail
}
#[inline]
pub fn is_on_disk(&self, addr: u64) -> bool {
let head = self.head_address.load(Ordering::Acquire);
let begin = self.begin_address.load(Ordering::Acquire);
addr >= begin && addr < head
}
#[inline]
pub fn is_valid(&self, addr: u64) -> bool {
let tail = self.tail_address.load(Ordering::Acquire);
let begin = self.begin_address.load(Ordering::Acquire);
addr >= begin && addr < tail
}
#[inline]
pub fn shift_read_only_address(&self, new_ro: u64) -> u64 {
self.read_only_address.fetch_max(new_ro, Ordering::AcqRel)
}
#[inline]
pub fn shift_safe_read_only_address(&self, new_safe_ro: u64) -> u64 {
self
.safe_read_only_address
.fetch_max(new_safe_ro, Ordering::AcqRel)
}
#[inline]
pub fn shift_head_address(&self, new_head: u64) -> u64 {
self.shift_read_only_address(new_head);
self.head_address.fetch_max(new_head, Ordering::AcqRel)
}
#[inline]
pub fn shift_safe_head_address(&self, new_safe_head: u64) -> u64 {
self.shift_safe_read_only_address(new_safe_head);
self
.safe_head_address
.fetch_max(new_safe_head, Ordering::AcqRel)
}
#[inline]
pub fn shift_begin_address(&self, new_begin: u64) -> u64 {
self.begin_address.fetch_max(new_begin, Ordering::AcqRel)
}
#[inline]
pub fn tail(&self) -> u64 {
self.tail_address.load(Ordering::Acquire)
}
#[inline]
pub fn read_only(&self) -> u64 {
self.read_only_address.load(Ordering::Acquire)
}
#[inline]
pub fn safe_read_only(&self) -> u64 {
self.safe_read_only_address.load(Ordering::Acquire)
}
#[inline]
pub fn head(&self) -> u64 {
self.head_address.load(Ordering::Acquire)
}
#[inline]
pub fn safe_head(&self) -> u64 {
self.safe_head_address.load(Ordering::Acquire)
}
#[inline]
pub fn begin(&self) -> u64 {
self.begin_address.load(Ordering::Acquire)
}
#[inline]
pub fn shift_flushed_until_address(&self, new_flushed: u64) -> u64 {
self
.flushed_until_address
.fetch_max(new_flushed, Ordering::AcqRel)
}
#[inline]
pub fn flushed_until(&self) -> u64 {
self.flushed_until_address.load(Ordering::Acquire)
}
pub fn with_snapshot(snapshot: AddressSnapshot) -> Self {
Self {
tail_address: AtomicU64::new(snapshot.tail),
read_only_address: AtomicU64::new(snapshot.read_only),
safe_read_only_address: AtomicU64::new(snapshot.safe_read_only),
head_address: AtomicU64::new(snapshot.head),
safe_head_address: AtomicU64::new(snapshot.safe_head),
begin_address: AtomicU64::new(snapshot.begin),
flushed_until_address: AtomicU64::new(snapshot.flushed_until),
}
}
#[inline]
pub fn validate_invariants(&self) -> bool {
self.snapshot().validate()
}
pub fn snapshot(&self) -> AddressSnapshot {
AddressSnapshot {
tail: self.tail(),
read_only: self.read_only(),
safe_read_only: self.safe_read_only(),
head: self.head(),
safe_head: self.safe_head(),
begin: self.begin(),
flushed_until: self.flushed_until(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
pub struct AddressSnapshot {
pub tail: u64,
pub read_only: u64,
pub safe_read_only: u64,
pub head: u64,
pub safe_head: u64,
pub begin: u64,
pub flushed_until: u64,
}
impl AddressSnapshot {
#[inline]
pub const fn new(
begin: u64,
safe_head: u64,
head: u64,
safe_read_only: u64,
read_only: u64,
tail: u64,
flushed_until: u64,
) -> Self {
Self {
tail,
read_only,
safe_read_only,
head,
safe_head,
begin,
flushed_until,
}
}
#[inline]
pub const fn from_bounds(
begin: u64,
head: u64,
flushed_until: u64,
read_only: u64,
tail: u64,
) -> Self {
Self {
tail,
read_only,
safe_read_only: read_only,
head,
safe_head: head,
begin,
flushed_until,
}
}
#[inline]
pub const fn validate(&self) -> bool {
self.begin <= self.safe_head
&& self.safe_head <= self.head
&& self.head <= self.safe_read_only
&& self.safe_read_only <= self.read_only
&& self.read_only <= self.tail
&& self.flushed_until <= self.tail
&& self.head <= self.flushed_until
}
#[inline]
pub const fn is_mutable(&self, addr: u64) -> bool {
let effective_ro = if self.read_only > self.head {
self.read_only
} else {
self.head
};
addr >= effective_ro && addr < self.tail
}
#[inline]
pub const fn is_read_only(&self, addr: u64) -> bool {
addr >= self.head && addr < self.read_only
}
#[inline]
pub const fn is_in_memory(&self, addr: u64) -> bool {
addr >= self.head && addr < self.tail
}
#[inline]
pub const fn is_on_disk(&self, addr: u64) -> bool {
addr >= self.begin && addr < self.head
}
#[inline]
pub const fn is_valid(&self, addr: u64) -> bool {
addr >= self.begin && addr < self.tail
}
}