Skip to main content

tuwunel_core/matrix/pdu/
count.rs

1#![expect(
2	clippy::cast_possible_wrap,
3	clippy::cast_sign_loss,
4	clippy::as_conversions
5)]
6
7use std::{cmp::Ordering, fmt, fmt::Display, str::FromStr};
8
9use ruma::api::Direction;
10
11use crate::{Error, Result, err};
12
13#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
14// PDU's sequence number
15pub enum Count {
16	Normal(u64),
17	Backfilled(i64),
18}
19
20impl Count {
21	#[inline]
22	#[must_use]
23	pub fn to_be_bytes(self) -> [u8; size_of::<u64>()] { self.into_unsigned().to_be_bytes() }
24
25	#[inline]
26	#[must_use]
27	pub fn from_unsigned(unsigned: u64) -> Self { Self::from_signed(unsigned as i64) }
28
29	#[inline]
30	#[must_use]
31	pub fn from_signed(signed: i64) -> Self {
32		match signed {
33			| i64::MIN..=0 => Self::Backfilled(signed),
34			| _ => Self::Normal(signed as u64),
35		}
36	}
37
38	#[inline]
39	#[must_use]
40	pub fn into_unsigned(self) -> u64 {
41		self.debug_assert_valid();
42		match self {
43			| Self::Normal(i) => i,
44			| Self::Backfilled(i) => i as u64,
45		}
46	}
47
48	#[inline]
49	#[must_use]
50	pub fn into_signed(self) -> i64 {
51		self.debug_assert_valid();
52		match self {
53			| Self::Normal(i) => i as i64,
54			| Self::Backfilled(i) => i,
55		}
56	}
57
58	#[inline]
59	#[must_use]
60	pub fn into_normal(self) -> Self {
61		self.debug_assert_valid();
62		match self {
63			| Self::Normal(i) => Self::Normal(i),
64			| Self::Backfilled(_) => Self::Normal(0),
65		}
66	}
67
68	#[inline]
69	pub fn checked_inc(self, dir: Direction) -> Result<Self, Error> {
70		match dir {
71			| Direction::Forward => self.checked_add(1),
72			| Direction::Backward => self.checked_sub(1),
73		}
74	}
75
76	#[inline]
77	pub fn checked_add(self, add: u64) -> Result<Self, Error> {
78		Ok(match self {
79			| Self::Normal(i) => Self::Normal(
80				i.checked_add(add)
81					.ok_or_else(|| err!(Arithmetic("Count::Normal overflow")))?,
82			),
83			| Self::Backfilled(i) => Self::Backfilled(
84				i.checked_add(add as i64)
85					.ok_or_else(|| err!(Arithmetic("Count::Backfilled overflow")))?,
86			),
87		})
88	}
89
90	#[inline]
91	pub fn checked_sub(self, sub: u64) -> Result<Self, Error> {
92		Ok(match self {
93			| Self::Normal(i) => Self::Normal(
94				i.checked_sub(sub)
95					.ok_or_else(|| err!(Arithmetic("Count::Normal underflow")))?,
96			),
97			| Self::Backfilled(i) => Self::Backfilled(
98				i.checked_sub(sub as i64)
99					.ok_or_else(|| err!(Arithmetic("Count::Backfilled underflow")))?,
100			),
101		})
102	}
103
104	#[inline]
105	#[must_use]
106	pub fn saturating_inc(self, dir: Direction) -> Self {
107		match dir {
108			| Direction::Forward => self.saturating_add(1),
109			| Direction::Backward => self.saturating_sub(1),
110		}
111	}
112
113	#[inline]
114	#[must_use]
115	pub fn saturating_add(self, add: u64) -> Self {
116		match self {
117			| Self::Normal(i) => Self::Normal(i.saturating_add(add)),
118			| Self::Backfilled(i) => Self::Backfilled(i.saturating_add(add as i64)),
119		}
120	}
121
122	#[inline]
123	#[must_use]
124	pub fn saturating_sub(self, sub: u64) -> Self {
125		match self {
126			| Self::Normal(i) => Self::Normal(i.saturating_sub(sub)),
127			| Self::Backfilled(i) => Self::Backfilled(i.saturating_sub(sub as i64)),
128		}
129	}
130
131	#[inline]
132	#[must_use]
133	pub const fn min() -> Self { Self::Backfilled(i64::MIN) }
134
135	#[inline]
136	#[must_use]
137	pub const fn max() -> Self { Self::Normal(i64::MAX as u64) }
138
139	#[inline]
140	pub(crate) fn debug_assert_valid(&self) {
141		if let Self::Backfilled(i) = self {
142			debug_assert!(*i <= 0, "Backfilled sequence must be negative");
143		}
144	}
145}
146
147impl Display for Count {
148	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
149		self.debug_assert_valid();
150		match self {
151			| Self::Normal(i) => write!(f, "{i}"),
152			| Self::Backfilled(i) => write!(f, "{i}"),
153		}
154	}
155}
156
157impl From<i64> for Count {
158	#[inline]
159	fn from(signed: i64) -> Self { Self::from_signed(signed) }
160}
161
162impl From<u64> for Count {
163	#[inline]
164	fn from(unsigned: u64) -> Self { Self::from_unsigned(unsigned) }
165}
166
167impl FromStr for Count {
168	type Err = Error;
169
170	fn from_str(token: &str) -> Result<Self, Self::Err> { Ok(Self::from_signed(token.parse()?)) }
171}
172
173impl PartialOrd for Count {
174	fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
175}
176
177impl Ord for Count {
178	fn cmp(&self, other: &Self) -> Ordering { self.into_signed().cmp(&other.into_signed()) }
179}
180
181impl Default for Count {
182	fn default() -> Self { Self::Normal(0) }
183}