use std::sync::atomic::{AtomicU64, Ordering};
pub const SWIZZLED_TAG: u64 = 0x1;
const PAGE_ID_SHIFT: u32 = 1;
pub const MAX_PAGE_ID: u64 = u64::MAX >> PAGE_ID_SHIFT;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SwizzleState {
Unswizzled { page_id: u64 },
Swizzled { frame_addr: u64 },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PageTemperature {
Hot,
Cooling,
Cold,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SwizzleError {
PageIdOverflow { page_id: u64 },
FrameAddrUnaligned { frame_addr: u64 },
CompareExchangeFailed { expected: u64, observed: u64 },
InvalidTemperatureTransition {
from: PageTemperature,
to: PageTemperature,
},
}
impl PageTemperature {
#[must_use]
pub const fn can_transition_to(self, next: Self) -> bool {
if matches!(
(self, next),
(Self::Hot, Self::Hot) | (Self::Cooling, Self::Cooling) | (Self::Cold, Self::Cold)
) {
return true;
}
matches!(
(self, next),
(Self::Hot, Self::Cooling)
| (Self::Cooling | Self::Cold, Self::Hot)
| (Self::Cooling, Self::Cold)
)
}
pub fn transition(self, next: Self) -> Result<Self, SwizzleError> {
if self.can_transition_to(next) {
Ok(next)
} else {
Err(SwizzleError::InvalidTemperatureTransition {
from: self,
to: next,
})
}
}
}
#[derive(Debug)]
pub struct SwizzlePtr {
raw: AtomicU64,
}
impl SwizzlePtr {
pub fn new_unswizzled(page_id: u64) -> Result<Self, SwizzleError> {
Ok(Self {
raw: AtomicU64::new(encode_unswizzled(page_id)?),
})
}
pub fn new_swizzled(frame_addr: u64) -> Result<Self, SwizzleError> {
Ok(Self {
raw: AtomicU64::new(encode_swizzled(frame_addr)?),
})
}
#[must_use]
pub fn load_raw(&self, ordering: Ordering) -> u64 {
self.raw.load(ordering)
}
#[must_use]
pub fn state(&self, ordering: Ordering) -> SwizzleState {
decode_state(self.load_raw(ordering))
}
#[must_use]
pub fn is_swizzled(&self, ordering: Ordering) -> bool {
self.load_raw(ordering) & SWIZZLED_TAG == SWIZZLED_TAG
}
pub fn try_swizzle(&self, expected_page_id: u64, frame_addr: u64) -> Result<(), SwizzleError> {
let expected = encode_unswizzled(expected_page_id)?;
let replacement = encode_swizzled(frame_addr)?;
self.raw
.compare_exchange(expected, replacement, Ordering::AcqRel, Ordering::Acquire)
.map(|_| ())
.map_err(|observed| SwizzleError::CompareExchangeFailed { expected, observed })
}
pub fn try_unswizzle(
&self,
expected_frame_addr: u64,
page_id: u64,
) -> Result<(), SwizzleError> {
let expected = encode_swizzled(expected_frame_addr)?;
let replacement = encode_unswizzled(page_id)?;
self.raw
.compare_exchange(expected, replacement, Ordering::AcqRel, Ordering::Acquire)
.map(|_| ())
.map_err(|observed| SwizzleError::CompareExchangeFailed { expected, observed })
}
}
fn encode_unswizzled(page_id: u64) -> Result<u64, SwizzleError> {
if page_id > MAX_PAGE_ID {
return Err(SwizzleError::PageIdOverflow { page_id });
}
Ok(page_id << PAGE_ID_SHIFT)
}
fn encode_swizzled(frame_addr: u64) -> Result<u64, SwizzleError> {
if frame_addr & SWIZZLED_TAG == SWIZZLED_TAG {
return Err(SwizzleError::FrameAddrUnaligned { frame_addr });
}
Ok(frame_addr | SWIZZLED_TAG)
}
const fn decode_state(raw: u64) -> SwizzleState {
if raw & SWIZZLED_TAG == SWIZZLED_TAG {
return SwizzleState::Swizzled {
frame_addr: raw & !SWIZZLED_TAG,
};
}
SwizzleState::Unswizzled {
page_id: raw >> PAGE_ID_SHIFT,
}
}
use std::collections::HashMap;
use std::sync::Mutex;
use crate::instrumentation::{
record_swizzle_fault, record_swizzle_in, record_swizzle_out, set_swizzle_ratio,
};
#[derive(Debug)]
pub struct SwizzleRegistry {
state: Mutex<RegistryState>,
}
#[derive(Debug)]
struct RegistryState {
entries: HashMap<u64, SwizzleEntry>,
swizzled_count: usize,
}
#[derive(Debug, Clone, Copy)]
struct SwizzleEntry {
temperature: PageTemperature,
swizzled: bool,
frame_addr: u64,
}
impl SwizzleRegistry {
#[must_use]
pub fn new() -> Self {
Self {
state: Mutex::new(RegistryState {
entries: HashMap::new(),
swizzled_count: 0,
}),
}
}
pub fn register_page(&self, page_id: u64) {
let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
state.entries.entry(page_id).or_insert(SwizzleEntry {
temperature: PageTemperature::Cold,
swizzled: false,
frame_addr: 0,
});
}
pub fn try_swizzle(&self, page_id: u64, frame_addr: u64) -> bool {
let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
if let Some(entry) = state.entries.get_mut(&page_id) {
if entry.swizzled {
record_swizzle_fault();
return false;
}
entry.swizzled = true;
entry.frame_addr = frame_addr;
entry.temperature = PageTemperature::Hot;
state.swizzled_count += 1;
Self::update_ratio_internal(&state);
drop(state);
record_swizzle_in(page_id);
true
} else {
record_swizzle_fault();
false
}
}
pub fn try_unswizzle(&self, page_id: u64) -> bool {
let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
if let Some(entry) = state.entries.get_mut(&page_id) {
if !entry.swizzled {
record_swizzle_fault();
return false;
}
entry.swizzled = false;
entry.frame_addr = 0;
entry.temperature = PageTemperature::Cold;
state.swizzled_count = state.swizzled_count.saturating_sub(1);
Self::update_ratio_internal(&state);
drop(state);
record_swizzle_out(page_id);
true
} else {
record_swizzle_fault();
false
}
}
#[must_use]
pub fn is_swizzled(&self, page_id: u64) -> bool {
let state = self.state.lock().unwrap_or_else(|e| e.into_inner());
state
.entries
.get(&page_id)
.is_some_and(|entry| entry.swizzled)
}
#[must_use]
pub fn frame_addr(&self, page_id: u64) -> Option<u64> {
let state = self.state.lock().unwrap_or_else(|e| e.into_inner());
state.entries.get(&page_id).and_then(|entry| {
if entry.swizzled {
Some(entry.frame_addr)
} else {
None
}
})
}
#[must_use]
pub fn tracked_count(&self) -> usize {
self.state
.lock()
.unwrap_or_else(|e| e.into_inner())
.entries
.len()
}
#[must_use]
pub fn swizzled_count(&self) -> usize {
self.state
.lock()
.unwrap_or_else(|e| e.into_inner())
.swizzled_count
}
fn update_ratio_internal(state: &RegistryState) {
let total = state.entries.len();
if total == 0 {
set_swizzle_ratio(0);
return;
}
let ratio_milli = (state.swizzled_count as u64 * 1000) / total as u64;
set_swizzle_ratio(ratio_milli);
}
}
impl Default for SwizzleRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
const BEAD_ID: &str = "bd-2uza4.1";
#[test]
fn unswizzled_round_trips_page_id() {
let ptr = SwizzlePtr::new_unswizzled(42).expect("page id should encode");
assert_eq!(
ptr.state(Ordering::Acquire),
SwizzleState::Unswizzled { page_id: 42 },
"bead_id={BEAD_ID} case=unswizzled_round_trip"
);
assert!(
!ptr.is_swizzled(Ordering::Acquire),
"bead_id={BEAD_ID} case=unswizzled_tag"
);
}
#[test]
fn swizzled_round_trips_frame_addr() {
let ptr = SwizzlePtr::new_swizzled(0x1000).expect("aligned frame address should encode");
assert_eq!(
ptr.state(Ordering::Acquire),
SwizzleState::Swizzled { frame_addr: 0x1000 },
"bead_id={BEAD_ID} case=swizzled_round_trip"
);
assert!(
ptr.is_swizzled(Ordering::Acquire),
"bead_id={BEAD_ID} case=swizzled_tag"
);
}
#[test]
fn page_id_overflow_is_rejected() {
let err = SwizzlePtr::new_unswizzled(MAX_PAGE_ID + 1).expect_err("must reject overflow");
assert_eq!(
err,
SwizzleError::PageIdOverflow {
page_id: MAX_PAGE_ID + 1,
},
"bead_id={BEAD_ID} case=page_id_overflow"
);
}
#[test]
fn swizzle_ptr_round_trips_boundary_values() {
let zero = SwizzlePtr::new_unswizzled(0).expect("zero page id encodes");
assert_eq!(
zero.state(Ordering::Acquire),
SwizzleState::Unswizzled { page_id: 0 }
);
assert!(!zero.is_swizzled(Ordering::Acquire));
let max = SwizzlePtr::new_unswizzled(MAX_PAGE_ID).expect("max page id encodes");
assert_eq!(
max.state(Ordering::Acquire),
SwizzleState::Unswizzled {
page_id: MAX_PAGE_ID
}
);
assert!(!max.is_swizzled(Ordering::Acquire));
let top_frame = !SWIZZLED_TAG;
let swz = SwizzlePtr::new_swizzled(top_frame).expect("aligned top frame encodes");
assert_eq!(
swz.state(Ordering::Acquire),
SwizzleState::Swizzled {
frame_addr: top_frame
}
);
assert!(swz.is_swizzled(Ordering::Acquire));
}
#[test]
fn unaligned_frame_address_is_rejected() {
let err = SwizzlePtr::new_swizzled(0x1001).expect_err("must reject unaligned frame addr");
assert_eq!(
err,
SwizzleError::FrameAddrUnaligned { frame_addr: 0x1001 },
"bead_id={BEAD_ID} case=unaligned_frame_addr"
);
}
#[test]
fn try_swizzle_updates_atomically() {
let ptr = SwizzlePtr::new_unswizzled(11).expect("page id should encode");
ptr.try_swizzle(11, 0x2000)
.expect("swizzle should succeed for expected state");
assert_eq!(
ptr.state(Ordering::Acquire),
SwizzleState::Swizzled { frame_addr: 0x2000 },
"bead_id={BEAD_ID} case=swizzle_success"
);
}
#[test]
fn try_swizzle_reports_observed_state_on_compare_exchange_failure() {
let ptr = SwizzlePtr::new_unswizzled(11).expect("page id should encode");
let err = ptr
.try_swizzle(12, 0x2000)
.expect_err("mismatched expected page id must fail");
let expected = 12_u64 << PAGE_ID_SHIFT;
let observed = 11_u64 << PAGE_ID_SHIFT;
assert_eq!(
err,
SwizzleError::CompareExchangeFailed { expected, observed },
"bead_id={BEAD_ID} case=swizzle_compare_exchange_failure"
);
}
#[test]
fn try_unswizzle_updates_atomically() {
let ptr = SwizzlePtr::new_swizzled(0x4000).expect("aligned frame address should encode");
ptr.try_unswizzle(0x4000, 77)
.expect("unswizzle should succeed for expected state");
assert_eq!(
ptr.state(Ordering::Acquire),
SwizzleState::Unswizzled { page_id: 77 },
"bead_id={BEAD_ID} case=unswizzle_success"
);
}
#[test]
fn try_unswizzle_reports_observed_state_on_compare_exchange_failure() {
let ptr = SwizzlePtr::new_swizzled(0x4000).expect("aligned frame address should encode");
let err = ptr
.try_unswizzle(0x5000, 77)
.expect_err("mismatched expected frame addr must fail");
let expected = 0x5000_u64 | SWIZZLED_TAG;
let observed = 0x4000_u64 | SWIZZLED_TAG;
assert_eq!(
err,
SwizzleError::CompareExchangeFailed { expected, observed }
);
assert_eq!(
ptr.state(Ordering::Acquire),
SwizzleState::Swizzled { frame_addr: 0x4000 }
);
}
#[test]
fn temperature_state_machine_transitions_match_design_contract() {
assert!(
PageTemperature::Hot
.transition(PageTemperature::Cooling)
.is_ok(),
"bead_id={BEAD_ID} case=hot_to_cooling"
);
assert!(
PageTemperature::Cooling
.transition(PageTemperature::Cold)
.is_ok(),
"bead_id={BEAD_ID} case=cooling_to_cold"
);
assert!(
PageTemperature::Cold
.transition(PageTemperature::Hot)
.is_ok(),
"bead_id={BEAD_ID} case=cold_to_hot"
);
assert_eq!(
PageTemperature::Hot
.transition(PageTemperature::Cold)
.expect_err("hot_to_cold must be invalid"),
SwizzleError::InvalidTemperatureTransition {
from: PageTemperature::Hot,
to: PageTemperature::Cold,
},
"bead_id={BEAD_ID} case=reject_hot_to_cold"
);
}
#[test]
fn can_transition_to_covers_full_temperature_matrix() {
use PageTemperature::{Cold, Cooling, Hot};
assert!(Hot.can_transition_to(Hot));
assert!(Cooling.can_transition_to(Cooling));
assert!(Cold.can_transition_to(Cold));
assert!(Hot.can_transition_to(Cooling));
assert!(Cooling.can_transition_to(Hot));
assert!(Cooling.can_transition_to(Cold));
assert!(Cold.can_transition_to(Hot));
assert!(!Hot.can_transition_to(Cold));
assert!(!Cold.can_transition_to(Cooling));
assert_eq!(
Cold.transition(Cooling)
.expect_err("cold_to_cooling must be invalid"),
SwizzleError::InvalidTemperatureTransition {
from: Cold,
to: Cooling,
}
);
}
const BEAD_REGISTRY: &str = "bd-3ta.3";
#[test]
fn registry_register_and_query() {
let reg = SwizzleRegistry::new();
reg.register_page(1);
assert_eq!(
reg.tracked_count(),
1,
"bead_id={BEAD_REGISTRY} case=tracked_count_after_register"
);
assert!(
!reg.is_swizzled(1),
"bead_id={BEAD_REGISTRY} case=newly_registered_not_swizzled"
);
assert_eq!(
reg.frame_addr(1),
None,
"bead_id={BEAD_REGISTRY} case=no_frame_addr_when_unswizzled"
);
}
#[test]
fn registry_try_swizzle_success() {
let reg = SwizzleRegistry::new();
reg.register_page(10);
assert!(
reg.try_swizzle(10, 0x8000),
"bead_id={BEAD_REGISTRY} case=swizzle_registered_page"
);
assert!(
reg.is_swizzled(10),
"bead_id={BEAD_REGISTRY} case=swizzled_after_try_swizzle"
);
assert_eq!(
reg.frame_addr(10),
Some(0x8000),
"bead_id={BEAD_REGISTRY} case=frame_addr_after_swizzle"
);
assert_eq!(
reg.swizzled_count(),
1,
"bead_id={BEAD_REGISTRY} case=swizzled_count_one"
);
}
#[test]
fn registry_double_swizzle_returns_false() {
let reg = SwizzleRegistry::new();
reg.register_page(20);
assert!(reg.try_swizzle(20, 0x4000));
assert!(
!reg.try_swizzle(20, 0x6000),
"bead_id={BEAD_REGISTRY} case=double_swizzle_rejected"
);
assert_eq!(
reg.frame_addr(20),
Some(0x4000),
"bead_id={BEAD_REGISTRY} case=frame_addr_unchanged_after_double_swizzle"
);
}
#[test]
fn registry_swizzle_unregistered_page_returns_false() {
let reg = SwizzleRegistry::new();
assert!(
!reg.try_swizzle(999, 0x2000),
"bead_id={BEAD_REGISTRY} case=swizzle_unregistered"
);
}
#[test]
fn registry_try_unswizzle_success() {
let reg = SwizzleRegistry::new();
reg.register_page(30);
reg.try_swizzle(30, 0xA000);
assert!(
reg.try_unswizzle(30),
"bead_id={BEAD_REGISTRY} case=unswizzle_success"
);
assert!(
!reg.is_swizzled(30),
"bead_id={BEAD_REGISTRY} case=not_swizzled_after_unswizzle"
);
assert_eq!(
reg.frame_addr(30),
None,
"bead_id={BEAD_REGISTRY} case=no_frame_addr_after_unswizzle"
);
assert_eq!(
reg.swizzled_count(),
0,
"bead_id={BEAD_REGISTRY} case=swizzled_count_zero_after_unswizzle"
);
}
#[test]
fn registry_unswizzle_already_cold_returns_false() {
let reg = SwizzleRegistry::new();
reg.register_page(40);
assert!(
!reg.try_unswizzle(40),
"bead_id={BEAD_REGISTRY} case=unswizzle_cold_page"
);
}
#[test]
fn registry_unswizzle_unregistered_returns_false() {
let reg = SwizzleRegistry::new();
assert!(
!reg.try_unswizzle(777),
"bead_id={BEAD_REGISTRY} case=unswizzle_unregistered"
);
}
#[test]
fn registry_duplicate_register_is_idempotent() {
let reg = SwizzleRegistry::new();
reg.register_page(50);
reg.try_swizzle(50, 0xC000);
reg.register_page(50);
assert!(
reg.is_swizzled(50),
"bead_id={BEAD_REGISTRY} case=duplicate_register_preserves_state"
);
}
#[test]
fn registry_swizzle_ratio_updates() {
let reg = SwizzleRegistry::new();
reg.register_page(1);
reg.register_page(2);
reg.register_page(3);
reg.register_page(4);
assert_eq!(reg.swizzled_count(), 0);
reg.try_swizzle(1, 0x1000);
reg.try_swizzle(2, 0x2000);
assert_eq!(
reg.swizzled_count(),
2,
"bead_id={BEAD_REGISTRY} case=swizzled_count_two_of_four"
);
reg.try_swizzle(3, 0x3000);
reg.try_swizzle(4, 0x4000);
assert_eq!(
reg.swizzled_count(),
4,
"bead_id={BEAD_REGISTRY} case=all_four_swizzled"
);
}
#[test]
fn registry_default_impl() {
let reg = SwizzleRegistry::default();
assert_eq!(
reg.tracked_count(),
0,
"bead_id={BEAD_REGISTRY} case=default_empty"
);
}
#[test]
fn registry_swizzle_unswizzle_cycle() {
let reg = SwizzleRegistry::new();
reg.register_page(60);
assert!(reg.try_swizzle(60, 0xD000));
assert!(reg.is_swizzled(60));
assert!(reg.try_unswizzle(60));
assert!(!reg.is_swizzled(60));
assert!(
reg.try_swizzle(60, 0xE000),
"bead_id={BEAD_REGISTRY} case=re_swizzle_after_unswizzle"
);
assert_eq!(
reg.frame_addr(60),
Some(0xE000),
"bead_id={BEAD_REGISTRY} case=new_frame_addr_after_re_swizzle"
);
}
#[test]
fn swizzle_metrics_appear_in_btree_snapshot() {
use crate::instrumentation::{btree_metrics_snapshot, record_swizzle_in};
let before = btree_metrics_snapshot();
record_swizzle_in(42);
let after = btree_metrics_snapshot();
assert!(
after.fsqlite_swizzle_in_total > before.fsqlite_swizzle_in_total,
"bead_id={BEAD_REGISTRY} case=swizzle_in_metric_increments"
);
}
#[test]
fn swizzle_fault_metric_increments() {
use crate::instrumentation::{btree_metrics_snapshot, record_swizzle_fault};
let before = btree_metrics_snapshot();
record_swizzle_fault();
record_swizzle_fault();
let after = btree_metrics_snapshot();
assert!(
after.fsqlite_swizzle_faults_total >= before.fsqlite_swizzle_faults_total + 2,
"bead_id={BEAD_REGISTRY} case=swizzle_fault_metric"
);
}
}