use crate::cache_padded::CachePadded;
use core::sync::atomic::{AtomicI64, Ordering};
pub const INITIAL_CURSOR_VALUE: i64 = -1;
#[repr(C)]
pub struct Cursor {
sequence: CachePadded<AtomicI64>,
}
impl Cursor {
#[inline]
pub const fn new() -> Self {
Self {
sequence: CachePadded::new(AtomicI64::new(INITIAL_CURSOR_VALUE)),
}
}
#[inline]
pub const fn with_value(sequence: i64) -> Self {
Self {
sequence: CachePadded::new(AtomicI64::new(sequence)),
}
}
#[inline(always)]
pub fn value(&self) -> i64 {
self.sequence.load(Ordering::Acquire)
}
#[inline(always)]
pub fn set(&self, sequence: i64) {
self.sequence.store(sequence, Ordering::Release);
}
#[inline(always)]
pub fn increment(&self) -> i64 {
self.sequence.fetch_add(1, Ordering::AcqRel) + 1
}
#[inline(always)]
pub fn add(&self, delta: i64) -> i64 {
self.sequence.fetch_add(delta, Ordering::AcqRel) + delta
}
#[inline(always)]
pub fn compare_exchange(&self, current: i64, new: i64) -> Result<i64, i64> {
self.sequence
.compare_exchange(current, new, Ordering::AcqRel, Ordering::Acquire)
}
#[inline(always)]
pub fn compare_exchange_weak(&self, current: i64, new: i64) -> Result<i64, i64> {
self.sequence
.compare_exchange_weak(current, new, Ordering::AcqRel, Ordering::Acquire)
}
#[inline(always)]
pub fn value_relaxed(&self) -> i64 {
self.sequence.load(Ordering::Relaxed)
}
#[inline(always)]
pub fn set_relaxed(&self, sequence: i64) {
self.sequence.store(sequence, Ordering::Relaxed);
}
#[inline]
pub fn as_atomic(&self) -> &AtomicI64 {
&self.sequence
}
}
impl Default for Cursor {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for Cursor {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Cursor")
.field("sequence", &self.value_relaxed())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::{Cursor, INITIAL_CURSOR_VALUE};
use core::sync::atomic::Ordering;
#[test]
fn starts_at_initial_value() {
let cursor = Cursor::new();
assert_eq!(cursor.value(), INITIAL_CURSOR_VALUE);
}
#[test]
fn increments_and_adds_sequences() {
let cursor = Cursor::with_value(0);
assert_eq!(cursor.increment(), 1);
assert_eq!(cursor.add(4), 5);
assert_eq!(cursor.value(), 5);
}
#[test]
fn compare_exchange_reports_actual_value() {
let cursor = Cursor::with_value(7);
assert_eq!(cursor.compare_exchange(6, 8), Err(7));
assert_eq!(cursor.compare_exchange(7, 8), Ok(7));
assert_eq!(cursor.value(), 8);
}
#[test]
fn exposes_underlying_atomic() {
let cursor = Cursor::new();
cursor.as_atomic().store(42, Ordering::Release);
assert_eq!(cursor.value(), 42);
}
}