1use serde::{Deserialize, Serialize};
2
3use crate::{MongrelError, Result};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
13pub struct RowId(pub u64);
14
15impl RowId {
16 pub const MIN: RowId = RowId(0);
17 pub const NULL_SORT_KEY: u16 = 0xFFFF;
18
19 #[inline]
20 pub fn next(self) -> Option<RowId> {
21 self.0.checked_add(1).map(RowId)
22 }
23}
24
25impl std::fmt::Display for RowId {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 write!(f, "RowId({})", self.0)
28 }
29}
30
31impl From<u64> for RowId {
32 fn from(v: u64) -> Self {
33 RowId(v)
34 }
35}
36
37#[derive(Debug, Default, Clone)]
39pub struct RowIdAllocator {
40 next: u64,
41}
42
43impl RowIdAllocator {
44 pub fn new(start: u64) -> Self {
45 Self { next: start }
46 }
47
48 #[inline]
50 pub fn alloc(&mut self) -> Result<RowId> {
51 let id = self.next;
52 self.next = self
53 .next
54 .checked_add(1)
55 .filter(|next| *next < u64::MAX)
56 .ok_or_else(row_id_exhausted)?;
57 Ok(RowId(id))
58 }
59
60 pub fn alloc_range(&mut self, n: u64) -> Result<RowId> {
62 let start = self.next;
63 if n != 0 {
64 self.next = self
65 .next
66 .checked_add(n)
67 .filter(|next| *next < u64::MAX)
68 .ok_or_else(row_id_exhausted)?;
69 }
70 Ok(RowId(start))
71 }
72
73 #[inline]
74 pub fn current(&self) -> RowId {
75 RowId(self.next)
76 }
77
78 pub fn advance_to(&mut self, id: RowId) -> Result<()> {
80 if id.0 >= self.next {
81 self.next =
82 id.0.checked_add(1)
83 .filter(|next| *next < u64::MAX)
84 .ok_or_else(row_id_exhausted)?;
85 }
86 Ok(())
87 }
88}
89
90fn row_id_exhausted() -> MongrelError {
91 MongrelError::Full("row-id namespace exhausted".into())
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn allocates_monotonically() {
100 let mut a = RowIdAllocator::default();
101 assert_eq!(a.alloc().unwrap(), RowId(0));
102 assert_eq!(a.alloc().unwrap(), RowId(1));
103 let start = a.alloc_range(3).unwrap();
104 assert_eq!(start, RowId(2));
105 assert_eq!(a.current(), RowId(5));
106 assert_eq!(a.alloc().unwrap(), RowId(5));
107 }
108
109 #[test]
110 fn advance_to_moves_head() {
111 let mut a = RowIdAllocator::default();
112 a.advance_to(RowId(100)).unwrap();
113 assert_eq!(a.alloc().unwrap(), RowId(101));
114 }
115
116 #[test]
117 fn exhaustion_never_wraps_or_partially_allocates() {
118 let mut a = RowIdAllocator::new(u64::MAX - 2);
119 assert_eq!(a.alloc().unwrap(), RowId(u64::MAX - 2));
120 assert!(matches!(a.alloc(), Err(MongrelError::Full(_))));
121 assert_eq!(a.current(), RowId(u64::MAX - 1));
122 assert!(matches!(a.alloc_range(2), Err(MongrelError::Full(_))));
123 assert_eq!(a.current(), RowId(u64::MAX - 1));
124 assert!(matches!(
125 a.advance_to(RowId(u64::MAX)),
126 Err(MongrelError::Full(_))
127 ));
128 assert_eq!(a.current(), RowId(u64::MAX - 1));
129 }
130}