use core::sync::atomic::{AtomicU64, Ordering};
pub const LINE_WORDS: usize = 8;
#[repr(C, align(64))]
#[derive(Debug, Default)]
pub struct CompletionLine {
pub words: [AtomicU64; LINE_WORDS],
}
impl AsRef<CompletionLine> for CompletionLine {
fn as_ref(&self) -> &CompletionLine {
self
}
}
const _: () = assert!(core::mem::size_of::<CompletionLine>() == 64);
const _: () = assert!(core::mem::align_of::<CompletionLine>() == 64);
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Slot(u8);
impl Slot {
#[must_use]
pub const fn new(i: usize) -> Option<Self> {
if i < LINE_WORDS {
#[expect(clippy::cast_possible_truncation, reason = "i < LINE_WORDS = 8")]
Some(Self(i as u8))
} else {
None
}
}
#[must_use]
pub const fn index(self) -> usize {
self.0 as usize
}
}
#[derive(Clone, Copy, Debug)]
pub struct MemoryDescriptor<'l> {
line: &'l CompletionLine,
slot: Slot,
target: u64,
}
impl<'l> MemoryDescriptor<'l> {
#[must_use]
pub const fn new(line: &'l CompletionLine, slot: Slot, target: u64) -> Self {
Self { line, slot, target }
}
#[must_use]
pub const fn line(&self) -> &'l CompletionLine {
self.line
}
#[must_use]
pub const fn slot(&self) -> Slot {
self.slot
}
#[must_use]
pub const fn target(&self) -> u64 {
self.target
}
#[must_use]
pub fn is_complete(&self) -> bool {
reached(self.line, self.slot, self.target)
}
}
#[must_use]
pub fn reached(line: &CompletionLine, slot: Slot, target: u64) -> bool {
let w = line.words[slot.index()].load(Ordering::Acquire);
w.wrapping_sub(target) < (1 << 63)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_word_below_its_target_is_pending_and_at_or_past_it_is_complete() {
let line = CompletionLine::default();
let s = Slot::new(3).expect("slot 3");
let d = MemoryDescriptor::new(&line, s, 5);
assert!(!d.is_complete(), "0 < 5");
line.words[3].store(4, Ordering::Release);
assert!(!d.is_complete(), "4 < 5");
line.words[3].store(5, Ordering::Release);
assert!(d.is_complete(), "5 == 5");
line.words[3].store(6, Ordering::Release);
assert!(d.is_complete(), "a later store still completes it");
line.words[2].store(99, Ordering::Release);
assert!(MemoryDescriptor::new(&line, Slot::new(2).expect("2"), 99).is_complete());
}
#[test]
fn reached_is_wrapping_order() {
let line = CompletionLine::default();
let s = Slot::new(0).expect("slot 0");
line.words[0].store(1, Ordering::Release);
assert!(reached(&line, s, u64::MAX), "1 follows u64::MAX after a wrap");
line.words[0].store(u64::MAX, Ordering::Release);
assert!(!reached(&line, s, 1), "u64::MAX precedes 1");
}
#[test]
fn a_slot_past_the_line_is_refused() {
assert!(Slot::new(LINE_WORDS).is_none());
assert_eq!(Slot::new(LINE_WORDS - 1).map(Slot::index), Some(LINE_WORDS - 1));
}
}