#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cursor(u64);
pub(crate) const PREFIX_BITS: u32 = 48;
pub(crate) const PREFIX_SHIFT: u32 = 16;
pub(crate) const BUCKET_BITS: u32 = 6;
pub const STRIPE_BITS: u32 = 8;
const STRIPE_SHIFT: u32 = BUCKET_BITS;
const STRIPE_MASK: u64 = ((1 << STRIPE_BITS) - 1) << STRIPE_SHIFT;
impl Cursor {
pub const START: Cursor = Cursor(0);
#[must_use]
pub const fn from_raw(raw: u64) -> Cursor {
Cursor(raw)
}
#[must_use]
pub const fn raw(self) -> u64 {
self.0
}
#[must_use]
pub const fn is_end(self) -> bool {
self.0 == 0
}
#[must_use]
pub const fn stripe(self) -> usize {
((self.0 & STRIPE_MASK) >> STRIPE_SHIFT) as usize
}
#[must_use]
pub const fn with_stripe(self, stripe: usize) -> Cursor {
Cursor((self.0 & !STRIPE_MASK) | (((stripe as u64) << STRIPE_SHIFT) & STRIPE_MASK))
}
#[must_use]
pub const fn without_stripe(self) -> Cursor {
Cursor(self.0 & !STRIPE_MASK)
}
#[must_use]
pub(crate) const fn prefix(self) -> u64 {
(self.0 >> PREFIX_SHIFT) & ((1 << PREFIX_BITS) - 1)
}
#[must_use]
pub(crate) const fn bucket(self) -> usize {
(self.0 & ((1 << BUCKET_BITS) - 1)) as usize
}
#[must_use]
pub(crate) const fn at(prefix: u64, bucket: usize) -> Cursor {
if prefix >= (1 << PREFIX_BITS) {
return Cursor::START;
}
Cursor((prefix << PREFIX_SHIFT) | (bucket as u64 & ((1 << BUCKET_BITS) - 1)))
}
#[cfg(test)]
#[must_use]
pub(crate) const fn prefix_of(hash: u64) -> u64 {
(hash >> (super::index::DIR_BITS - PREFIX_BITS)) & ((1 << PREFIX_BITS) - 1)
}
}
impl From<u64> for Cursor {
fn from(raw: u64) -> Cursor {
Cursor::from_raw(raw)
}
}
impl From<Cursor> for u64 {
fn from(c: Cursor) -> u64 {
c.raw()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_two_halves_survive_the_round_trip() {
for prefix in [0u64, 1, 255, (1 << PREFIX_BITS) - 1] {
for bucket in [0usize, 1, 63] {
let c = Cursor::at(prefix, bucket);
assert_eq!(c.prefix(), prefix, "prefix {prefix} bucket {bucket}");
assert_eq!(c.bucket(), bucket, "prefix {prefix} bucket {bucket}");
}
}
}
#[test]
fn a_prefix_past_the_end_is_the_end() {
assert_eq!(Cursor::at(1 << PREFIX_BITS, 0), Cursor::START);
assert!(Cursor::at(1 << PREFIX_BITS, 7).is_end());
assert!(!Cursor::at(0, 1).is_end());
}
#[test]
fn a_prefix_is_the_directory_index_at_every_depth() {
let hash = 0x1234_5678_9abc_def0u64;
let prefix = Cursor::prefix_of(hash);
for g in 1..=16u32 {
let dir_bits = super::super::index::DIR_BITS;
let want = (hash >> (dir_bits - g)) & ((1 << g) - 1);
assert_eq!(prefix >> (PREFIX_BITS - g), want, "depth {g}");
}
}
#[test]
fn a_stripe_number_sits_beside_the_other_two_halves() {
let c = Cursor::at(0x1234, 42).with_stripe(200);
assert_eq!(c.prefix(), 0x1234);
assert_eq!(c.bucket(), 42);
assert_eq!(c.stripe(), 200);
assert_eq!(c.without_stripe(), Cursor::at(0x1234, 42));
assert_eq!(Cursor::START.with_stripe(0), Cursor::START);
assert!(!Cursor::START.with_stripe(1).is_end());
assert!(Cursor::START.with_stripe(1).without_stripe().is_end());
}
#[test]
fn the_cursor_a_client_holds_is_just_a_number() {
let c = Cursor::from_raw(0x0001_0000_0000_002a);
assert_eq!(u64::from(c), 0x0001_0000_0000_002a);
assert_eq!(Cursor::from(7u64).bucket(), 7);
}
}