Skip to main content

open_wal/
lsn.rs

1//! Log Sequence Numbers.
2
3use std::fmt;
4
5/// Log Sequence Number. Newtype to prevent mixing with byte offsets/counts.
6///
7/// `Lsn(0)` is reserved as "none"; real records are dense starting at 1
8/// (§6 of `docs/wal_design_v6.md`).
9#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
10pub struct Lsn(pub u64);
11
12impl Lsn {
13    /// The reserved "none" sentinel. Never assigned to a real record.
14    pub const NONE: Lsn = Lsn(0);
15
16    /// The first valid record LSN. Records are dense starting here.
17    pub const FIRST: Lsn = Lsn(1);
18
19    /// The next LSN in sequence. Saturating, so `Lsn(u64::MAX).next()` does not
20    /// wrap (a 2^64-record log is not reachable in practice, but recovery must
21    /// never panic on arbitrary inputs — D11).
22    #[inline]
23    #[must_use]
24    pub const fn next(self) -> Lsn {
25        Lsn(self.0.saturating_add(1))
26    }
27
28    /// True for the reserved `Lsn(0)` sentinel.
29    #[inline]
30    #[must_use]
31    pub const fn is_none(self) -> bool {
32        self.0 == 0
33    }
34}
35
36impl fmt::Display for Lsn {
37    /// Formats the LSN as its underlying integer, so error and log messages can
38    /// use `{lsn}` instead of poking at the `.0` field.
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{}", self.0)
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[test]
49    fn none_and_first() {
50        assert_eq!(Lsn::NONE, Lsn(0));
51        assert_eq!(Lsn::FIRST, Lsn(1));
52        assert!(Lsn::NONE.is_none());
53        assert!(!Lsn::FIRST.is_none());
54    }
55
56    #[test]
57    fn next_is_monotone_and_dense() {
58        assert_eq!(Lsn::NONE.next(), Lsn::FIRST);
59        assert_eq!(Lsn(41).next(), Lsn(42));
60    }
61
62    #[test]
63    fn next_saturates_without_panicking() {
64        assert_eq!(Lsn(u64::MAX).next(), Lsn(u64::MAX));
65    }
66
67    #[test]
68    fn ordering() {
69        assert!(Lsn(1) < Lsn(2));
70        assert!(Lsn::NONE < Lsn::FIRST);
71    }
72
73    #[test]
74    fn display_is_the_underlying_integer() {
75        assert_eq!(Lsn(0).to_string(), "0");
76        assert_eq!(Lsn(100001).to_string(), "100001");
77    }
78}