use std::fmt;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct BTreeNodeId(pub u64);
impl BTreeNodeId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for BTreeNodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "btree:{}", self.0)
}
}
impl From<u64> for BTreeNodeId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<BTreeNodeId> for u64 {
fn from(id: BTreeNodeId) -> Self {
id.0
}
}
static BTREE_NODE_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
pub fn next_btree_node_id() -> BTreeNodeId {
BTreeNodeId(BTREE_NODE_ID_COUNTER.fetch_add(1, Ordering::SeqCst))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct HnswNodeId(pub u64);
impl HnswNodeId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for HnswNodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "hnsw:{}", self.0)
}
}
impl From<u64> for HnswNodeId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<HnswNodeId> for u64 {
fn from(id: HnswNodeId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct TxnId(pub u64);
impl TxnId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const ZERO: TxnId = TxnId(0);
pub const fn get(self) -> u64 {
self.0
}
pub const fn is_zero(self) -> bool {
self.0 == 0
}
}
impl fmt::Display for TxnId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "txn:{}", self.0)
}
}
impl From<u64> for TxnId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<TxnId> for u64 {
fn from(id: TxnId) -> Self {
id.0
}
}
impl Add<u64> for TxnId {
type Output = TxnId;
fn add(self, rhs: u64) -> TxnId {
TxnId(self.0 + rhs)
}
}
impl AddAssign<u64> for TxnId {
fn add_assign(&mut self, rhs: u64) {
self.0 += rhs;
}
}
impl Sub<u64> for TxnId {
type Output = TxnId;
fn sub(self, rhs: u64) -> TxnId {
TxnId(self.0 - rhs)
}
}
static TXN_ID_COUNTER: AtomicU64 = AtomicU64::new(1);
pub fn next_txn_id() -> TxnId {
TxnId(TXN_ID_COUNTER.fetch_add(1, Ordering::SeqCst))
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct VectorId(pub u64);
impl VectorId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for VectorId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "vec:{}", self.0)
}
}
impl From<u64> for VectorId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<VectorId> for u64 {
fn from(id: VectorId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct SegmentId(pub u64);
impl SegmentId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for SegmentId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "seg:{}", self.0)
}
}
impl From<u64> for SegmentId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<SegmentId> for u64 {
fn from(id: SegmentId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct PageId(pub u64);
impl PageId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const NULL: PageId = PageId(0);
pub const fn get(self) -> u64 {
self.0
}
pub const fn is_null(self) -> bool {
self.0 == 0
}
}
impl fmt::Display for PageId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "page:{}", self.0)
}
}
impl From<u64> for PageId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<PageId> for u64 {
fn from(id: PageId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct EntityId(pub u64);
impl EntityId {
pub const fn new(id: u64) -> Self {
Self(id)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for EntityId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "entity:{}", self.0)
}
}
impl From<u64> for EntityId {
fn from(id: u64) -> Self {
Self(id)
}
}
impl From<EntityId> for u64 {
fn from(id: EntityId) -> Self {
id.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
#[repr(transparent)]
pub struct Timestamp(pub u64);
impl Timestamp {
pub const fn new(ts: u64) -> Self {
Self(ts)
}
pub const EPOCH: Timestamp = Timestamp(0);
pub const MAX: Timestamp = Timestamp(u64::MAX);
pub const fn get(self) -> u64 {
self.0
}
pub const fn is_epoch(self) -> bool {
self.0 == 0
}
}
impl fmt::Display for Timestamp {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ts:{}", self.0)
}
}
impl From<u64> for Timestamp {
fn from(ts: u64) -> Self {
Self(ts)
}
}
impl From<Timestamp> for u64 {
fn from(ts: Timestamp) -> Self {
ts.0
}
}
impl Add<u64> for Timestamp {
type Output = Timestamp;
fn add(self, rhs: u64) -> Timestamp {
Timestamp(self.0 + rhs)
}
}
impl Add<Timestamp> for Timestamp {
type Output = Timestamp;
fn add(self, rhs: Timestamp) -> Timestamp {
Timestamp(self.0 + rhs.0)
}
}
impl AddAssign<u64> for Timestamp {
fn add_assign(&mut self, rhs: u64) {
self.0 += rhs;
}
}
impl Sub<u64> for Timestamp {
type Output = Timestamp;
fn sub(self, rhs: u64) -> Timestamp {
Timestamp(self.0 - rhs)
}
}
impl Sub<Timestamp> for Timestamp {
type Output = Timestamp;
fn sub(self, rhs: Timestamp) -> Timestamp {
Timestamp(self.0 - rhs.0)
}
}
impl SubAssign<u64> for Timestamp {
fn sub_assign(&mut self, rhs: u64) {
self.0 -= rhs;
}
}
impl Timestamp {
pub const fn saturating_sub(self, rhs: Self) -> Self {
Timestamp(self.0.saturating_sub(rhs.0))
}
pub fn min(self, other: Self) -> Self {
Timestamp(self.0.min(other.0))
}
pub fn max(self, other: Self) -> Self {
Timestamp(self.0.max(other.0))
}
}
static GLOBAL_TIMESTAMP: AtomicU64 = AtomicU64::new(1);
pub fn next_timestamp() -> Timestamp {
Timestamp(GLOBAL_TIMESTAMP.fetch_add(1, Ordering::SeqCst))
}
pub fn current_timestamp() -> Timestamp {
Timestamp(GLOBAL_TIMESTAMP.load(Ordering::SeqCst))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_btree_node_id() {
let id1 = BTreeNodeId::new(42);
let id2 = BTreeNodeId::from(42u64);
assert_eq!(id1, id2);
assert_eq!(id1.get(), 42);
assert_eq!(format!("{}", id1), "btree:42");
}
#[test]
fn test_hnsw_node_id() {
let id = HnswNodeId::new(100);
assert_eq!(id.get(), 100);
assert_eq!(format!("{}", id), "hnsw:100");
}
#[test]
fn test_txn_id() {
assert!(TxnId::ZERO.is_zero());
let id = TxnId::new(5);
assert!(!id.is_zero());
assert_eq!(format!("{}", id), "txn:5");
}
#[test]
fn test_vector_id() {
let id = VectorId::new(999);
assert_eq!(id.get(), 999);
assert_eq!(u64::from(id), 999);
}
#[test]
fn test_page_id() {
assert!(PageId::NULL.is_null());
let id = PageId::new(1);
assert!(!id.is_null());
}
#[test]
fn test_timestamp() {
assert!(Timestamp::EPOCH.is_epoch());
let ts = Timestamp::new(100);
assert!(!ts.is_epoch());
assert!(ts < Timestamp::MAX);
}
#[test]
fn test_id_generation() {
let id1 = next_btree_node_id();
let id2 = next_btree_node_id();
assert!(id2.get() > id1.get());
let txn1 = next_txn_id();
let txn2 = next_txn_id();
assert!(txn2.get() > txn1.get());
let ts1 = next_timestamp();
let ts2 = next_timestamp();
assert!(ts2.get() > ts1.get());
}
#[test]
fn test_type_safety() {
let btree_id = BTreeNodeId::new(1);
let hnsw_id = HnswNodeId::new(1);
let txn_id = TxnId::new(1);
assert_eq!(btree_id.get(), hnsw_id.get());
assert_eq!(btree_id.get(), txn_id.get());
assert_ne!(format!("{}", btree_id), format!("{}", hnsw_id));
assert_ne!(format!("{}", btree_id), format!("{}", txn_id));
}
}