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>
impl<const INDEX_BITS: u32> TaggedIndex<INDEX_BITS>
Sourcepub const INDEX_MASK: u64
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.
Sourcepub const TAG_BITS: u32
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.
Sourcepub const TAG_MAX: u64
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.
Sourcepub const fn pack(index: u32, tag: u64) -> Option<u64>
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).
Sourcepub const fn unpack(word: u64) -> (u32, u64)
pub const fn unpack(word: u64) -> (u32, u64)
Split a packed word back into (u32 index, u64 tag).
Sourcepub const fn empty_index() -> u32
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”.