1use std::{
2 fmt::Display,
3 num::{NonZero, ParseIntError},
4 ops::{Bound, RangeBounds, RangeInclusive},
5};
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use zerocopy::{ByteHash, Immutable, IntoBytes, KnownLayout, TryFromBytes, ValidityError};
10
11#[derive(Debug, Error)]
12#[error("LSN must be non-zero")]
13pub struct InvalidLSN;
14
15impl<S, D: TryFromBytes> From<ValidityError<S, D>> for InvalidLSN {
16 fn from(_: ValidityError<S, D>) -> Self {
17 InvalidLSN
18 }
19}
20
21#[derive(
22 Debug,
23 Clone,
24 Copy,
25 PartialEq,
26 Eq,
27 PartialOrd,
28 Ord,
29 Serialize,
30 Deserialize,
31 TryFromBytes,
32 KnownLayout,
33 IntoBytes,
34 Immutable,
35 ByteHash,
36)]
37#[repr(transparent)]
38pub struct LSN(NonZero<u64>);
39
40static_assertions::assert_eq_size!(Option<LSN>, LSN);
42
43impl LSN {
44 pub const FIRST: Self = unsafe { Self::new_unchecked(1) };
45 pub const LAST: Self = unsafe { Self::new_unchecked(u64::MAX) };
46 pub const ALL: RangeInclusive<Self> = Self::FIRST..=Self::LAST;
47
48 #[inline]
49 pub fn new(lsn: u64) -> Self {
50 Self(NonZero::new(lsn).expect("LSN must be non-zero"))
51 }
52
53 #[inline]
54 const unsafe fn new_unchecked(lsn: u64) -> Self {
55 unsafe { Self(NonZero::new_unchecked(lsn)) }
56 }
57
58 #[inline]
59 pub fn next(&self) -> Option<Self> {
60 self.0.checked_add(1).map(Self)
61 }
62
63 #[inline]
64 pub fn saturating_next(&self) -> Self {
65 Self(self.0.saturating_add(1))
66 }
67
68 #[inline]
69 pub fn prev(&self) -> Option<Self> {
70 let n = self.0.get().saturating_sub(1);
71 if n == 0 {
72 None
73 } else {
74 unsafe { Some(Self(NonZero::new_unchecked(n))) }
76 }
77 }
78
79 #[inline]
80 pub fn saturating_prev(&self) -> Self {
81 let n = self.0.get().saturating_sub(1);
82 if n == 0 {
83 Self::FIRST
84 } else {
85 unsafe { Self(NonZero::new_unchecked(n)) }
87 }
88 }
89
90 #[inline]
93 pub fn since(&self, other: &Self) -> Option<u64> {
94 let me = self.0.get();
95 let other = other.0.get();
96 if me >= other { Some(me - other) } else { None }
97 }
98
99 pub fn format_fixed_hex(&self) -> String {
102 format!("{:0>16x}", self.0)
103 }
104
105 pub fn from_hex(s: &str) -> Result<Self, ParseIntError> {
107 Ok(Self::new(u64::from_str_radix(s, 16)?))
108 }
109}
110
111impl Display for LSN {
112 #[inline]
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 self.0.fmt(f)
115 }
116}
117
118impl<'a> TryFrom<&'a u64> for &'a LSN {
119 type Error = InvalidLSN;
120
121 #[inline]
122 fn try_from(value: &'a u64) -> Result<Self, Self::Error> {
123 Ok(zerocopy::try_transmute_ref!(value)?)
124 }
125}
126
127impl From<&LSN> for u64 {
128 #[inline]
129 fn from(value: &LSN) -> Self {
130 value.0.get()
131 }
132}
133
134impl From<LSN> for u64 {
135 #[inline]
136 fn from(lsn: LSN) -> Self {
137 lsn.0.get()
138 }
139}
140
141impl TryFrom<u64> for LSN {
142 type Error = InvalidLSN;
143
144 #[inline]
145 fn try_from(lsn: u64) -> Result<Self, Self::Error> {
146 match NonZero::new(lsn) {
147 Some(lsn) => Ok(Self(lsn)),
148 None => Err(InvalidLSN),
149 }
150 }
151}
152
153impl PartialEq<u64> for LSN {
154 #[inline]
155 fn eq(&self, other: &u64) -> bool {
156 self.0.get() == *other
157 }
158}
159
160impl PartialOrd<u64> for LSN {
161 #[inline]
162 fn partial_cmp(&self, other: &u64) -> Option<std::cmp::Ordering> {
163 self.0.get().partial_cmp(other)
164 }
165}
166
167impl<E: zerocopy::ByteOrder> From<LSN> for zerocopy::U64<E> {
168 #[inline]
169 fn from(val: LSN) -> Self {
170 zerocopy::U64::new(val.0.get())
171 }
172}
173
174impl<E: zerocopy::ByteOrder> TryFrom<zerocopy::U64<E>> for LSN {
175 type Error = InvalidLSN;
176
177 #[inline]
178 fn try_from(value: zerocopy::U64<E>) -> Result<Self, Self::Error> {
179 match NonZero::new(value.get()) {
180 Some(lsn) => Ok(Self(lsn)),
181 None => Err(InvalidLSN),
182 }
183 }
184}
185
186pub trait LSNRangeExt {
187 fn try_len(&self) -> Option<usize>;
188 fn try_start(&self) -> Option<LSN>;
189 fn try_start_exclusive(&self) -> Option<LSN>;
190 fn try_end(&self) -> Option<LSN>;
191 fn try_end_exclusive(&self) -> Option<LSN>;
192 fn iter(&self) -> LSNRangeIter;
193}
194
195impl<T: RangeBounds<LSN>> LSNRangeExt for T {
196 fn try_len(&self) -> Option<usize> {
197 let start = self.try_start()?;
198 let end = self.try_end_exclusive()?;
199 end.since(&start).map(|len| len as usize)
200 }
201
202 fn try_start(&self) -> Option<LSN> {
203 match self.start_bound() {
204 Bound::Included(lsn) => Some(*lsn),
205 Bound::Excluded(lsn) => Some(lsn.saturating_next()),
206 Bound::Unbounded => None,
207 }
208 }
209
210 fn try_start_exclusive(&self) -> Option<LSN> {
211 match self.start_bound() {
212 Bound::Included(lsn) => lsn.prev(),
213 Bound::Excluded(lsn) => Some(*lsn),
214 Bound::Unbounded => None,
215 }
216 }
217
218 fn try_end(&self) -> Option<LSN> {
219 match self.end_bound() {
220 Bound::Included(lsn) => Some(*lsn),
221 Bound::Excluded(lsn) => Some(lsn.saturating_prev()),
222 Bound::Unbounded => None,
223 }
224 }
225
226 fn try_end_exclusive(&self) -> Option<LSN> {
227 match self.end_bound() {
228 Bound::Included(lsn) => lsn.next(),
229 Bound::Excluded(lsn) => Some(*lsn),
230 Bound::Unbounded => None,
231 }
232 }
233
234 fn iter(&self) -> LSNRangeIter {
235 let start = self.try_start().unwrap_or(LSN::FIRST).into();
236 let end = self.try_end().unwrap_or(LSN::LAST).into();
237 LSNRangeIter { range: start..=end }
238 }
239}
240
241#[derive(Debug, Clone)]
242#[must_use = "iterators are lazy and do nothing unless consumed"]
243pub struct LSNRangeIter {
244 range: RangeInclusive<u64>,
245}
246
247impl Iterator for LSNRangeIter {
248 type Item = LSN;
249
250 fn next(&mut self) -> Option<Self::Item> {
251 self.range.next().map(|n| {
252 unsafe { LSN::new_unchecked(n) }
255 })
256 }
257}
258
259#[cfg(test)]
260mod tests {
261 use super::*;
262
263 #[graft_test::test]
264 fn test_lsn_next() {
265 let lsn = LSN::FIRST;
266 assert_eq!(lsn.saturating_next(), 2);
267 }
268}