use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(u8)]
#[non_exhaustive]
pub enum Space {
Inline = 0,
Arena = 1,
Log = 2,
Hash = 3,
Set = 4,
ZSet = 5,
List = 6,
Stream = 7,
Doc = 8,
Vector = 9,
Graph = 10,
}
impl Space {
pub const ALL: &'static [Space] = &[
Space::Inline,
Space::Arena,
Space::Log,
Space::Hash,
Space::Set,
Space::ZSet,
Space::List,
Space::Stream,
Space::Doc,
Space::Vector,
Space::Graph,
];
#[inline]
pub const fn from_bits(bits: u8) -> Option<Space> {
match bits {
0 => Some(Space::Inline),
1 => Some(Space::Arena),
2 => Some(Space::Log),
3 => Some(Space::Hash),
4 => Some(Space::Set),
5 => Some(Space::ZSet),
6 => Some(Space::List),
7 => Some(Space::Stream),
8 => Some(Space::Doc),
9 => Some(Space::Vector),
10 => Some(Space::Graph),
_ => None,
}
}
#[inline]
pub const fn name(self) -> &'static str {
match self {
Space::Inline => "inline",
Space::Arena => "arena",
Space::Log => "log",
Space::Hash => "hash",
Space::Set => "set",
Space::ZSet => "zset",
Space::List => "list",
Space::Stream => "stream",
Space::Doc => "doc",
Space::Vector => "vector",
Space::Graph => "graph",
}
}
}
pub const OFFSET_BITS: u32 = 52;
pub const SPACE_BITS: u32 = 4;
pub const ADDR_BITS: u32 = OFFSET_BITS + SPACE_BITS;
pub const MAX_OFFSET: u64 = (1u64 << OFFSET_BITS) - 1;
const OFFSET_MASK: u64 = MAX_OFFSET;
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct Addr(u64);
impl Addr {
pub const NONE: Addr = Addr(0);
#[inline]
pub const fn new(space: Space, offset: u64) -> Addr {
assert!(offset <= MAX_OFFSET, "offset does not fit in 52 bits");
Addr(((space as u64) << OFFSET_BITS) | offset)
}
#[inline]
pub const unsafe fn new_unchecked(space: Space, offset: u64) -> Addr {
Addr(((space as u64) << OFFSET_BITS) | offset)
}
#[inline]
pub const fn to_bits(self) -> u64 {
self.0
}
#[inline]
pub const fn from_bits(bits: u64) -> Addr {
Addr(bits & ((1u64 << ADDR_BITS) - 1))
}
#[inline]
pub const fn is_none(self) -> bool {
self.0 == 0
}
#[inline]
pub const fn is_some(self) -> bool {
self.0 != 0
}
#[inline]
pub const fn offset(self) -> u64 {
self.0 & OFFSET_MASK
}
#[inline]
pub const fn space_bits(self) -> u8 {
(self.0 >> OFFSET_BITS) as u8
}
#[inline]
pub const fn space(self) -> Option<Space> {
Space::from_bits(self.space_bits())
}
}
impl fmt::Debug for Addr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_none() {
return f.write_str("Addr(none)");
}
match self.space() {
Some(s) => write!(f, "Addr({}+{:#x})", s.name(), self.offset()),
None => write!(f, "Addr(space{}+{:#x})", self.space_bits(), self.offset()),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ShardId(pub u16);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn address_widths_are_what_the_bucket_assumes() {
assert_eq!(ADDR_BITS, 56);
assert_eq!(ADDR_BITS % 8, 0);
}
#[test]
fn round_trips_through_bits() {
for &space in Space::ALL {
for offset in [0u64, 1, 4096, MAX_OFFSET] {
let a = Addr::new(space, offset);
assert_eq!(a.offset(), offset);
assert_eq!(a.space(), Some(space));
assert_eq!(Addr::from_bits(a.to_bits()), a);
}
}
}
#[test]
fn zero_is_absent() {
assert!(Addr::NONE.is_none());
assert!(Addr::new(Space::Arena, 0).is_some());
assert!(Addr::new(Space::Inline, 1).is_some());
}
#[test]
fn unknown_space_reports_rather_than_panics() {
let a = Addr::from_bits(15u64 << OFFSET_BITS | 99);
assert_eq!(a.space(), None);
assert_eq!(a.space_bits(), 15);
assert_eq!(a.offset(), 99);
}
#[test]
fn high_byte_from_disk_is_dropped() {
let a = Addr::from_bits(0xFF00_0000_0000_0000 | 7);
assert_eq!(a.offset(), 7);
assert!(a.space_bits() <= 15);
}
#[test]
#[should_panic(expected = "52 bits")]
fn oversized_offset_panics() {
let _ = Addr::new(Space::Arena, MAX_OFFSET + 1);
}
}