chiark / gitweb /
arithmetic checking: Provide new CheckedArith trait
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 21 Feb 2021 16:09:45 +0000 (16:09 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 21 Feb 2021 16:09:45 +0000 (16:09 +0000)
Nothing uses this yet so NFC.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
src/utils.rs

index a5753a902c00bbea5616a520a942ebb0d1d081c3..dc94e3c32bef30b385e1d75cc76fabe79ec82f9e 100644 (file)
@@ -5,6 +5,8 @@
 use crate::imports::*;
 use crate::prelude::*;
 
+use std::ops::Neg;
+
 #[macro_export]
 macro_rules! ensure_eq {
   ($v1:expr, $v2:expr) => {
@@ -176,6 +178,56 @@ impl<Y: Sync, E: Sync, F: Sync + FnOnce() -> Result<Y,E>>
 
 // todo: DerefMut
 
+#[derive(Error,Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize)]
+#[error("error parsing Z coordinate")]
+pub struct FooParseError;
+
+#[derive(Error,Clone,Copy,Debug,Serialize,Deserialize)]
+#[error("error parsing Z coordinate")]
+pub struct CoordinateOverflow;
+
+pub trait CheckedArith: Copy + Clone + Debug + 'static {
+  fn checked_add(self, rhs: Self) -> Result<Self, CoordinateOverflow>;
+  fn checked_sub(self, rhs: Self) -> Result<Self, CoordinateOverflow>;
+  fn checked_mul(self, rhs: Self) -> Result<Self, CoordinateOverflow>;
+  fn checked_neg(self)            -> Result<Self, CoordinateOverflow>;
+  fn checked_mulf(self, rhs: f64) -> Result<Self, CoordinateOverflow>;
+}
+
+macro_rules! checked_inherent { {$n:ident($($formal:tt)*) $($actual:tt)*} => {
+  fn $n(self $($formal)*) -> Result<Self, CoordinateOverflow> {
+    self.$n($($actual)*).ok_or(CoordinateOverflow)
+  }
+} }
+
+impl CheckedArith for i32 {
+  checked_inherent!{checked_add(, rhs: Self) rhs}
+  checked_inherent!{checked_sub(, rhs: Self) rhs}
+  checked_inherent!{checked_mul(, rhs: Self) rhs}
+  checked_inherent!{checked_neg(           )    }
+  fn checked_mulf(self, rhs: f64) -> Result<Self, CoordinateOverflow> {
+    let lhs: f64 = self.into();
+    let out: f64 = lhs.checked_mul(rhs)?;
+    let out: Self = num::NumCast::from(out).ok_or(CoordinateOverflow)?;
+    Ok(out)
+  }
+}
+
+macro_rules! checked_float { {$n:ident($($formal:tt)*) $($modify:tt)*} => {
+  fn $n(self $($formal)*) -> Result<Self, CoordinateOverflow> {
+    let out = self $($modify)*;
+    if out.is_finite() { Ok(out) } else { Err(CoordinateOverflow) }
+  }
+} }
+
+impl CheckedArith for f64 {
+  checked_float!{checked_add (, rhs: Self)  + rhs }
+  checked_float!{checked_sub (, rhs: Self)  - rhs }
+  checked_float!{checked_mul (, rhs: Self)  * rhs }
+  checked_float!{checked_mulf(, rhs: Self)  * rhs }
+  checked_float!{checked_neg()              .neg()}
+}
+
 pub fn toml_merge<'u,
                   S: 'u + AsRef<str>,
                   KV: IntoIterator<Item=(&'u S, &'u toml::Value)>