Skip to main content

TaggedIndex

Enum TaggedIndex 

Source
pub enum TaggedIndex<const INDEX_BITS: u32> {}
Expand description

A packed (index | tag) word with a compile-time-chosen index width.

The low INDEX_BITS bits carry a slot index; the high 64 - INDEX_BITS bits carry a strictly monotonic generation ABA tag that SEALS at TAG_MAX rather than wrapping. The all-ones index value (empty_index) is reserved as the empty-stack sentinel, so valid indices are 0 .. (1 << INDEX_BITS) - 1.

This is a namespace of const fn bit operations, not a value type — no state, no memory, no unsafe, strict-provenance-clean by construction (it packs a plain integer index, never a pointer/address). Declared as an UNINHABITED enum (zero variants) rather than a unit struct: a unit struct is freely constructible, and closing that off later with a private field would be a breaking change once published, whereas an uninhabited enum has no constructor at all from the start.

Implementations§

Source§

impl<const INDEX_BITS: u32> TaggedIndex<INDEX_BITS>

Source

pub const INDEX_MASK: u64

Bit-mask for the low INDEX_BITS (the index half), e.g. 0xFFFF for INDEX_BITS = 16. Its u32-typed form is the empty_index value.

Forces _CHECK_BITS to evaluate here too — see _CHECK_BITS’s doc.

Source

pub const TAG_BITS: u32

Number of bits carrying the tag (64 - INDEX_BITS). The tag is strictly monotonic and SEALS at TAG_MAX — it does not wrap.

Source

pub const TAG_MAX: u64

Largest tag a head word can carry: 2^TAG_BITS - 1. A push that observes this tag on the current head is refused (Err(TagExhausted)) instead of bumping it to 2^TAG_BITS, which would wrap back to 0 and re-issue a (index, tag) head word that a popper parked since the previous cycle may still hold as its stale CAS expectation — see push_index’s # Errors section and the crate-root docs’ “Tag-width budget” section. pack(_, TAG_MAX) is Some; pack(_, TAG_MAX + 1) is None.

Source

pub const fn pack(index: u32, tag: u64) -> Option<u64>

Pack (index, tag) into one u64, CHECKED: Some(word) for an in-range pair, None when either half is out of range — index >= 2^INDEX_BITS over the u32 index parameter (which unchecked masking would silently turn into a DIFFERENT, valid-looking index, or into the empty sentinel if the low bits happen to be all ones) or tag >= 2^TAG_BITS (whose high bits a tag << INDEX_BITS shift would silently drop). The index parameter is u32; the tag half is u64. For an accepted pair the word is exactly (index | tag << INDEX_BITS): both halves are already within their bit budgets, so no masking takes place and unpack recovers both halves exactly.

Note the two bounds in this crate are deliberately different ranges: < 2^INDEX_BITS is this function’s acceptance boundary, while push_index’s < INDEX_MASK (INDEX_MASK == 2^INDEX_BITS - 1) is stricter because it also excludes the reserved empty sentinel. Packing the empty index with a tag IS accepted here — the legitimate tag-preserving empty transition (empty_index).

push_index/pop_index do NOT call this function on the hot path: their inputs are already proven within range by the crate’s own guards, so they pack through the crate-private truncating fast path pack_truncating purely to skip this function’s redundant range re-check (see its doc).

Source

pub const fn unpack(word: u64) -> (u32, u64)

Split a packed word back into (u32 index, u64 tag).

Source

pub const fn empty_index() -> u32

The empty sentinel’s index half: the u32 form of INDEX_MASK, for packing it with a NON-zero, caller-supplied RUNNING tag (pack(empty_index(), running_tag)) instead of the crate-private bootstrap helper, which always zeroes the tag.

Empty-transition tag preservation: the transition in pop_index uses this, packing the tag it just observed on the popped head, so the ABA tag keeps counting forward across the empty→non-empty churn cycle. is_empty inspects only the index half, so a non-zero tag here is still unambiguously “empty”.

Source

pub const fn is_empty(word: u64) -> bool

Whether a packed word denotes the empty stack (index half == the empty sentinel), REGARDLESS of the tag half.

Auto Trait Implementations§

§

impl<const INDEX_BITS: u32> Freeze for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> RefUnwindSafe for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> Send for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> Sync for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> Unpin for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> UnsafeUnpin for TaggedIndex<INDEX_BITS>

§

impl<const INDEX_BITS: u32> UnwindSafe for TaggedIndex<INDEX_BITS>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.