fenv_bind/
lib.rs

1#![doc = include_str!("../README")]
2#![no_std]
3#![deny(missing_docs)]
4
5// enforce linking with libc.
6extern crate libc;
7
8#[path = "../binding/binding.rs"]
9#[allow(non_snake_case)]
10#[allow(non_upper_case_globals)]
11#[allow(non_camel_case_types)]
12#[allow(dead_code)]
13#[allow(clippy::useless_transmute)]
14#[allow(missing_docs)]
15mod binding;
16
17use binding::_bindgen_ty_1 as cfexcpt;
18use binding::_bindgen_ty_2 as cfround;
19
20use core::ffi::c_int;
21use core::num::NonZeroI32;
22
23#[allow(missing_docs)]
24pub mod raw {
25    pub use crate::binding::*;
26}
27
28// NOTE: uses NoneZeroI32 for optimization reasons.
29type Result<T> = core::result::Result<T, NonZeroI32>;
30
31#[inline(always)]
32fn result(result: core::ffi::c_int) -> Result<()> {
33    NonZeroI32::new(result).map(Err).unwrap_or(Ok(()))
34}
35
36macro_rules! flag_ty {
37    (
38        $(#[$($attr:meta)*])*
39        struct $ident:ident ($ty:ty);
40        $(
41            const $c_ident:ident = $c_expr:expr;
42        )*
43    ) => {
44        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
45        $(#[$($attr)*])*
46        pub struct $ident($ty);
47
48        impl $ident {
49            $(
50                #[allow(missing_docs)]
51                pub const $c_ident: Self = Self($c_expr);
52            )*
53
54            #[allow(missing_docs)]
55            #[inline(always)]
56            pub fn none() -> Self {
57                Self(0)
58            }
59
60            #[allow(missing_docs)]
61            #[inline(always)]
62            pub fn is_empty(self) -> bool {
63                self.as_raw() == 0
64            }
65
66            /// See if the given flags exist.
67            #[inline(always)]
68            pub fn has(self, other: Self) -> bool {
69                self.0 & other.0 == other.0
70            }
71
72            /// Return a new set of flags which enbable all mutual flags between `self` and `other`.
73            #[inline(always)]
74            pub fn or(self, other: Self) -> Self {
75                Self(self.0 | other.0)
76            }
77
78            /// Return a new set of flags which remove all mutual flags between `self` and `other`.
79            #[inline(always)]
80            pub fn not(self, other: Self) -> Self {
81                Self(self.0 & !other.0)
82            }
83
84            /// Retrieve the raw representation of the flags.
85            #[inline(always)]
86            pub fn as_raw(self) -> $ty {
87                self.0
88            }
89        }
90
91        impl core::ops::BitOr for $ident {
92            type Output = Self;
93
94            #[inline(always)]
95            fn bitor(self, rhs: Self) -> Self::Output {
96                self.or(rhs)
97            }
98        }
99
100        impl core::ops::BitOrAssign for $ident {
101            #[inline(always)]
102            fn bitor_assign(&mut self, rhs: Self) {
103                *self = self.or(rhs);
104            }
105        }
106    };
107}
108
109flag_ty! (
110    /// A wrapper over floating point exception flags.
111    ///
112    /// This type encapsulate the behaviour of manipulating
113    /// the configuration of floating point exceptions without
114    /// needing to deal with the raw API.
115    struct FExcept(cfexcpt::Type);
116    const INVALID = cfexcpt::FE_INVALID;
117    const DENORM = cfexcpt::__FE_DENORM;
118    const DIV_BY_ZERO = cfexcpt::FE_DIVBYZERO;
119    const OVERFLOW = cfexcpt::FE_OVERFLOW;
120    const UNDERFLOW = cfexcpt::FE_UNDERFLOW;
121    const INEXACT = cfexcpt::FE_INEXACT;
122    const FE_ALL = binding::FE_ALL_EXCEPT;
123);
124
125flag_ty!(
126    /// A wrapper over floating point rounding flags.
127    ///
128    /// This type encapsulates the behaviour of manipulating
129    /// the configuration of floating point rounding without
130    /// needing to deal with the raw API.
131    struct FRound(cfround::Type);
132    const NEAREST = cfround::FE_TONEAREST;
133    const DOWNWARD = cfround::FE_DOWNWARD;
134    const UPWARD = cfround::FE_UPWARD;
135    const TOWARD_ZERO = cfround::FE_TOWARDZERO;
136);
137
138impl FExcept {
139    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feexceptflag
140    pub fn from_env() -> Result<Self> {
141        let mut excepts = binding::fexcept_t::default();
142        result(unsafe {
143            binding::fegetexceptflag(&mut excepts as *mut _, FExcept::FE_ALL.as_raw() as c_int)
144        })
145        .map(|_| Self(excepts as cfexcpt::Type))
146    }
147
148    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feexceptflag
149    #[inline(always)]
150    pub fn set(self) -> Result<()> {
151        let excepts = self.as_raw() as binding::fexcept_t;
152        result(unsafe { binding::fesetexceptflag(&excepts as *const _, self.as_raw() as c_int) })
153    }
154    /// visit: https://en.cppreference.com/w/cpp/numeric/fenv/feclearexcept
155    #[inline(always)]
156    pub fn clear(self) -> Result<()> {
157        result(unsafe { binding::feclearexcept(self.as_raw() as c_int) })
158    }
159
160    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feraiseexcept
161    #[inline(always)]
162    pub fn raise(self) -> Result<()> {
163        result(unsafe { binding::feraiseexcept(self.as_raw() as c_int) })
164    }
165
166    /// visit: https://en.cppreference.com/w/c/numeric/fenv/fetestexcept
167    #[inline(always)]
168    pub fn test(self) -> Self {
169        Self(unsafe { binding::fetestexcept(self.as_raw() as c_int) } as cfexcpt::Type)
170    }
171}
172
173impl FRound {
174    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feround
175    #[inline(always)]
176    pub fn from_env() -> Self {
177        Self(unsafe { binding::fegetround() as cfround::Type })
178    }
179
180    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feround
181    #[inline(always)]
182    pub fn set(self) -> Result<()> {
183        result(unsafe { binding::fesetround(self.as_raw() as c_int) })
184    }
185}
186
187/// visit: https://en.cppreference.com/w/c/numeric/fenv/feround
188#[inline(always)]
189pub fn set_rounding_mode(flags: FRound) -> Result<()> {
190    flags.set()
191}
192
193/// visit: https://en.cppreference.com/w/c/numeric/fenv/feround
194#[inline(always)]
195pub fn get_rounding_mode() -> FRound {
196    FRound::from_env()
197}
198
199/// A wrapper around the floating point environment.
200///
201/// visit: https://cplusplus.com/reference/cfenv/fenv_t/
202/// for more information about the underlying `fenv_t` type.
203#[derive(Clone, Debug)]
204pub struct FEnv(binding::fenv_t);
205
206impl FEnv {
207    #[allow(missing_docs)]
208    pub fn from_env() -> Result<Self> {
209        let mut this: Self = unsafe { core::mem::zeroed() };
210        result(unsafe { binding::fegetenv(&mut this.0 as *mut _) }).map(|_| this)
211    }
212
213    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feenv
214    #[inline(always)]
215    pub fn set(&self) -> Result<()> {
216        result(unsafe { binding::fesetenv(&self.0 as *const _) })
217    }
218
219    /// visit: https://en.cppreference.com/w/c/numeric/fenv/feholdexcept
220    #[inline(always)]
221    pub fn hold(&mut self) -> Result<()> {
222        result(unsafe { binding::feholdexcept(&mut self.0 as *mut _) })
223    }
224
225    /// visit:
226    #[inline(always)]
227    pub fn update(&self) -> Result<()> {
228        result(unsafe { binding::feupdateenv(&self.0 as *const _) })
229    }
230
231    #[allow(missing_docs)]
232    #[inline(always)]
233    pub fn inner(&self) -> &binding::fenv_t {
234        &self.0
235    }
236
237    #[allow(missing_docs)]
238    #[inline(always)]
239    pub fn inner_mut(&mut self) -> &mut binding::fenv_t {
240        &mut self.0
241    }
242}
243
244impl core::ops::Deref for FEnv {
245    type Target = binding::fenv_t;
246
247    fn deref(&self) -> &Self::Target {
248        self.inner()
249    }
250}
251
252impl core::ops::DerefMut for FEnv {
253    fn deref_mut(&mut self) -> &mut Self::Target {
254        self.inner_mut()
255    }
256}