add math utils; integrate checked expression macros
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
611f09829e
commit
5e72d36800
7 changed files with 68 additions and 0 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -504,6 +504,15 @@ version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "checked_ops"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b491d76efc1d99d74de3c8529bee64c62312c275c7eb124f9185291de45801d5"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.38"
|
version = "0.4.38"
|
||||||
|
@ -658,6 +667,7 @@ dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"axum 0.7.5",
|
"axum 0.7.5",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"checked_ops",
|
||||||
"chrono",
|
"chrono",
|
||||||
"either",
|
"either",
|
||||||
"figment",
|
"figment",
|
||||||
|
|
|
@ -411,6 +411,9 @@ rev = "de26100b0db03e419a3d8e1dd26895d170d1fe50"
|
||||||
version = "0.29.4"
|
version = "0.29.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
|
[workspace.dependencies.checked_ops]
|
||||||
|
version = "0.1"
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Patches
|
# Patches
|
||||||
|
|
|
@ -53,6 +53,7 @@ sha256_media = []
|
||||||
argon2.workspace = true
|
argon2.workspace = true
|
||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
|
checked_ops.workspace = true
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
either.workspace = true
|
either.workspace = true
|
||||||
figment.workspace = true
|
figment.workspace = true
|
||||||
|
|
|
@ -35,6 +35,12 @@ pub enum Error {
|
||||||
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
FromUtf8Error(#[from] std::string::FromUtf8Error),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
TryFromSliceError(#[from] std::array::TryFromSliceError),
|
||||||
|
#[error("{0}")]
|
||||||
|
TryFromIntError(#[from] std::num::TryFromIntError),
|
||||||
|
#[error("{0}")]
|
||||||
|
ParseIntError(#[from] std::num::ParseIntError),
|
||||||
|
#[error("{0}")]
|
||||||
|
ParseFloatError(#[from] std::num::ParseFloatError),
|
||||||
|
|
||||||
// third-party
|
// third-party
|
||||||
#[error("Regex error: {0}")]
|
#[error("Regex error: {0}")]
|
||||||
|
@ -63,6 +69,8 @@ pub enum Error {
|
||||||
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
InconsistentRoomState(&'static str, ruma::OwnedRoomId),
|
||||||
|
|
||||||
// conduwuit
|
// conduwuit
|
||||||
|
#[error("Arithmetic operation failed: {0}")]
|
||||||
|
Arithmetic(&'static str),
|
||||||
#[error("There was a problem with your configuration: {0}")]
|
#[error("There was a problem with your configuration: {0}")]
|
||||||
BadConfig(String),
|
BadConfig(String),
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
|
|
26
src/core/utils/math.rs
Normal file
26
src/core/utils/math.rs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
use std::{cmp, time::Duration};
|
||||||
|
|
||||||
|
pub use checked_ops::checked_ops;
|
||||||
|
|
||||||
|
/// Checked arithmetic expression. Returns a Result<R, Error::Arithmetic>
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! checked {
|
||||||
|
($($input:tt)*) => {
|
||||||
|
$crate::utils::math::checked_ops!($($input)*)
|
||||||
|
.ok_or_else(|| $crate::Error::Arithmetic("operation overflowed or result invalid"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// in release-mode. Use for performance when the expression is obviously safe.
|
||||||
|
/// The check remains in debug-mode for regression analysis.
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! validated {
|
||||||
|
($($input:tt)*) => { Ok($($input)*) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! validated {
|
||||||
|
($($input:tt)*) => { $crate::checked!($($input)*) }
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ pub mod defer;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod html;
|
pub mod html;
|
||||||
pub mod json;
|
pub mod json;
|
||||||
|
pub mod math;
|
||||||
pub mod mutex_map;
|
pub mod mutex_map;
|
||||||
pub mod rand;
|
pub mod rand;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
|
|
@ -62,3 +62,22 @@ fn common_prefix_none() {
|
||||||
let output = string::common_prefix(&input);
|
let output = string::common_prefix(&input);
|
||||||
assert_eq!(output, "");
|
assert_eq!(output, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn checked_add() {
|
||||||
|
use utils::math::checked;
|
||||||
|
|
||||||
|
let a = 1234;
|
||||||
|
let res = checked!(a + 1).unwrap();
|
||||||
|
assert_eq!(res, 1235);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "overflow")]
|
||||||
|
fn checked_add_overflow() {
|
||||||
|
use utils::math::checked;
|
||||||
|
|
||||||
|
let a: u64 = u64::MAX;
|
||||||
|
let res = checked!(a + 1).expect("overflow");
|
||||||
|
assert_eq!(res, 0);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue