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 {
pub const SNAPSHOT_SIZE: usize = 56;
#[inline(always)]
pub const fn to_bytes(&self) -> [u8; Self::SNAPSHOT_SIZE] {
let b0 = self.tail.to_le_bytes();
let b1 = self.read_only.to_le_bytes();
let b2 = self.safe_read_only.to_le_bytes();
let b3 = self.head.to_le_bytes();
let b4 = self.safe_head.to_le_bytes();
let b5 = self.begin.to_le_bytes();
let b6 = self.flushed_until.to_le_bytes();
[
b0[0], b0[1], b0[2], b0[3], b0[4], b0[5], b0[6], b0[7], b1[0], b1[1], b1[2], b1[3], b1[4],
b1[5], b1[6], b1[7], b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6], b2[7], b3[0], b3[1],
b3[2], b3[3], b3[4], b3[5], b3[6], b3[7], b4[0], b4[1], b4[2], b4[3], b4[4], b4[5], b4[6],
b4[7], b5[0], b5[1], b5[2], b5[3], b5[4], b5[5], b5[6], b5[7], b6[0], b6[1], b6[2], b6[3],
b6[4], b6[5], b6[6], b6[7],
]
}
#[inline(always)]
pub const fn from_bytes(bytes: [u8; Self::SNAPSHOT_SIZE]) -> Self {
let tail = u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
let read_only = u64::from_le_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]);
let safe_read_only = u64::from_le_bytes([
bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23],
]);
let head = u64::from_le_bytes([
bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31],
]);
let safe_head = u64::from_le_bytes([
bytes[32], bytes[33], bytes[34], bytes[35], bytes[36], bytes[37], bytes[38], bytes[39],
]);
let begin = u64::from_le_bytes([
bytes[40], bytes[41], bytes[42], bytes[43], bytes[44], bytes[45], bytes[46], bytes[47],
]);
let flushed_until = u64::from_le_bytes([
bytes[48], bytes[49], bytes[50], bytes[51], bytes[52], bytes[53], bytes[54], bytes[55],
]);
Self {
tail,
read_only,
safe_read_only,
head,
safe_head,
begin,
flushed_until,
}
}
#[inline(always)]
pub const fn decode_opt(src: &[u8]) -> Option<Self> {
if let Some(bytes) = src.first_chunk::<{ Self::SNAPSHOT_SIZE }>() {
Some(Self::from_bytes(*bytes))
} else {
None
}
}
#[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
}
}