Skip to main content

tuwunel_core/utils/
math.rs

1mod expect_into;
2mod expected;
3mod tried;
4
5use std::convert::TryFrom;
6
7pub use checked_ops::checked_ops;
8
9pub use self::{expect_into::ExpectInto, expected::Expected, tried::Tried};
10use crate::{Err, Error, Result, debug::type_name, err};
11
12/// Checked arithmetic expression. Returns a Result<R, Error::Arithmetic>
13#[macro_export]
14#[collapse_debuginfo(yes)]
15macro_rules! checked {
16	($($input:tt)+) => {
17		$crate::utils::math::checked_ops!($($input)+)
18			.ok_or_else(
19				// The compiler will now attempt to inline the math predicate
20				// while moving the error handling out to .text.unlikely.
21				#[cold]
22				|| $crate::err!(Arithmetic("operation overflowed or result invalid"))
23			)
24	};
25}
26
27/// Checked arithmetic expression which panics on failure. This is for
28/// expressions which do not meet the threshold for validated! but the caller
29/// has no realistic expectation for error and no interest in cluttering the
30/// callsite with result handling from checked!.
31#[macro_export]
32#[collapse_debuginfo(yes)]
33macro_rules! expected {
34	($msg:literal, $($input:tt)+) => {
35		$crate::checked!($($input)+).expect($msg)
36	};
37
38	($($input:tt)+) => {
39		$crate::expected!("arithmetic expression expectation failure", $($input)+)
40	};
41}
42
43/// Unchecked arithmetic expression in release-mode. Use for performance when
44/// the expression is obviously safe. The check remains in debug-mode for
45/// regression analysis.
46#[cfg(not(debug_assertions))]
47#[macro_export]
48#[collapse_debuginfo(yes)]
49macro_rules! validated {
50	($($input:tt)+) => {
51		{
52			// TODO rewrite when stmt_expr_attributes is stable
53			#[expect(clippy::arithmetic_side_effects)]
54			let __res = ($($input)+);
55			__res
56		}
57	};
58}
59
60/// Checked arithmetic expression in debug-mode. Use for performance when
61/// the expression is obviously safe. The check is elided in release-mode.
62#[cfg(debug_assertions)]
63#[macro_export]
64#[collapse_debuginfo(yes)]
65macro_rules! validated {
66	($($input:tt)+) => {
67		$crate::expected!("validated arithmetic expression failed", $($input)+)
68	}
69}
70
71#[inline]
72pub fn usize_from_f64(val: f64) -> Result<usize, Error> {
73	if val < 0.0 {
74		return Err!(Arithmetic("Converting negative float to unsigned integer"));
75	}
76
77	//SAFETY: <https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked>
78	Ok(unsafe { val.to_int_unchecked::<usize>() })
79}
80
81#[inline]
82#[must_use]
83pub fn usize_from_ruma(val: ruma::UInt) -> usize {
84	usize::try_from(val).expect("failed conversion from ruma::UInt to usize")
85}
86
87#[inline]
88#[must_use]
89pub fn ruma_from_u64(val: u64) -> ruma::UInt {
90	ruma::UInt::try_from(val).expect("failed conversion from u64 to ruma::UInt")
91}
92
93#[inline]
94#[must_use]
95pub fn ruma_from_usize(val: usize) -> ruma::UInt {
96	ruma::UInt::try_from(val).expect("failed conversion from usize to ruma::UInt")
97}
98
99#[inline]
100#[must_use]
101#[expect(clippy::as_conversions, clippy::cast_possible_truncation)]
102pub fn usize_from_u64_truncated(val: u64) -> usize { val as usize }
103
104#[inline]
105pub fn expect_into<Dst: TryFrom<Src>, Src>(src: Src) -> Dst {
106	try_into(src).expect("failed conversion from Src to Dst")
107}
108
109#[inline]
110pub fn try_into<Dst: TryFrom<Src>, Src>(src: Src) -> Result<Dst> {
111	Dst::try_from(src).map_err(|_| {
112		err!(Arithmetic(
113			"failed to convert from {} to {}",
114			type_name::<Src>(),
115			type_name::<Dst>()
116		))
117	})
118}