opentelemetry_spanprocessor_any/metrics/
number.rs

1use std::cmp;
2use std::fmt;
3use std::sync::atomic::{AtomicU64, Ordering};
4
5/// Number represents either an integral or a floating point value. It
6/// needs to be accompanied with a source of NumberKind that describes
7/// the actual type of the value stored within Number.
8#[derive(Clone, Debug, Default)]
9pub struct Number(u64);
10
11impl Number {
12    /// Create an atomic version of the current number
13    pub fn to_atomic(&self) -> AtomicNumber {
14        AtomicNumber(AtomicU64::new(self.0))
15    }
16
17    /// Compares this number to the given other number. Both should be of the same kind.
18    pub fn partial_cmp(&self, number_kind: &NumberKind, other: &Number) -> Option<cmp::Ordering> {
19        match number_kind {
20            NumberKind::I64 => (self.0 as i64).partial_cmp(&(other.0 as i64)),
21            NumberKind::F64 => {
22                let current = u64_to_f64(self.0);
23                let other = u64_to_f64(other.0);
24                current.partial_cmp(&other)
25            }
26            NumberKind::U64 => self.0.partial_cmp(&other.0),
27        }
28    }
29
30    /// Casts the number to `i64`. May result in data/precision loss.
31    pub fn to_i64(&self, number_kind: &NumberKind) -> i64 {
32        match number_kind {
33            NumberKind::F64 => u64_to_f64(self.0) as i64,
34            NumberKind::U64 | NumberKind::I64 => self.0 as i64,
35        }
36    }
37
38    /// Casts the number to `f64`. May result in data/precision loss.
39    pub fn to_f64(&self, number_kind: &NumberKind) -> f64 {
40        match number_kind {
41            NumberKind::I64 => (self.0 as i64) as f64,
42            NumberKind::F64 => u64_to_f64(self.0),
43            NumberKind::U64 => self.0 as f64,
44        }
45    }
46
47    /// Casts the number to `u64`. May result in data/precision loss.
48    pub fn to_u64(&self, number_kind: &NumberKind) -> u64 {
49        match number_kind {
50            NumberKind::F64 => u64_to_f64(self.0) as u64,
51            NumberKind::U64 | NumberKind::I64 => self.0,
52        }
53    }
54
55    /// Checks if this value ia an f64 nan value. Do not use on non-f64 values.
56    pub fn is_nan(&self) -> bool {
57        u64_to_f64(self.0).is_nan()
58    }
59
60    /// `true` if the actual value is less than zero.
61    pub fn is_negative(&self, number_kind: &NumberKind) -> bool {
62        match number_kind {
63            NumberKind::I64 => (self.0 as i64).is_negative(),
64            NumberKind::F64 => u64_to_f64(self.0).is_sign_negative(),
65            NumberKind::U64 => false,
66        }
67    }
68
69    /// Return loaded data for debugging purposes
70    pub fn to_debug(&self, kind: &NumberKind) -> Box<dyn fmt::Debug> {
71        match kind {
72            NumberKind::I64 => Box::new(self.0 as i64),
73            NumberKind::F64 => Box::new(u64_to_f64(self.0)),
74            NumberKind::U64 => Box::new(self.0),
75        }
76    }
77}
78
79/// An atomic version of `Number`
80#[derive(Debug, Default)]
81pub struct AtomicNumber(AtomicU64);
82
83impl AtomicNumber {
84    /// Stores a `Number` into the atomic number.
85    pub fn store(&self, val: &Number) {
86        self.0.store(val.0, Ordering::Relaxed)
87    }
88
89    /// Adds to the current number. Both numbers must be of the same kind.
90    ///
91    /// This operation wraps around on overflow for `u64` and `i64` types and is
92    /// `inf` for `f64`.
93    pub fn fetch_add(&self, number_kind: &NumberKind, val: &Number) {
94        match number_kind {
95            NumberKind::I64 => {
96                let mut old = self.0.load(Ordering::Acquire);
97                loop {
98                    let new = (old as i64).wrapping_add(val.0 as i64) as u64;
99                    match self.0.compare_exchange_weak(
100                        old,
101                        new,
102                        Ordering::AcqRel,
103                        Ordering::Acquire,
104                    ) {
105                        Ok(_) => break,
106                        Err(x) => old = x,
107                    };
108                }
109            }
110            NumberKind::F64 => {
111                let mut old = self.0.load(Ordering::Acquire);
112                loop {
113                    let new = u64_to_f64(old) + u64_to_f64(val.0);
114                    match self.0.compare_exchange_weak(
115                        old,
116                        f64_to_u64(new),
117                        Ordering::AcqRel,
118                        Ordering::Acquire,
119                    ) {
120                        Ok(_) => break,
121                        Err(x) => old = x,
122                    };
123                }
124            }
125            NumberKind::U64 => {
126                self.0.fetch_add(val.0, Ordering::AcqRel);
127            }
128        }
129    }
130
131    /// Subtracts from the current number. Both numbers must be of the same kind.
132    ///
133    /// This operation wraps around on overflow for `u64` and `i64` types and is
134    /// `-inf` for `f64`.
135    pub fn fetch_sub(&self, number_kind: &NumberKind, val: &Number) {
136        match number_kind {
137            NumberKind::I64 => {
138                let mut old = self.0.load(Ordering::Acquire);
139                loop {
140                    let new = (old as i64).wrapping_sub(val.0 as i64) as u64;
141                    match self.0.compare_exchange_weak(
142                        old,
143                        new,
144                        Ordering::AcqRel,
145                        Ordering::Relaxed,
146                    ) {
147                        Ok(_) => break,
148                        Err(x) => old = x,
149                    };
150                }
151            }
152            NumberKind::F64 => {
153                let mut old = self.0.load(Ordering::Acquire);
154                loop {
155                    let new = u64_to_f64(old) - u64_to_f64(val.0);
156                    match self.0.compare_exchange_weak(
157                        old,
158                        f64_to_u64(new),
159                        Ordering::AcqRel,
160                        Ordering::Acquire,
161                    ) {
162                        Ok(_) => break,
163                        Err(x) => old = x,
164                    };
165                }
166            }
167            NumberKind::U64 => {
168                self.0.fetch_sub(val.0, Ordering::AcqRel);
169            }
170        }
171    }
172
173    /// Loads the current `Number`.
174    pub fn load(&self) -> Number {
175        Number(self.0.load(Ordering::Relaxed))
176    }
177}
178
179impl Clone for AtomicNumber {
180    fn clone(&self) -> Self {
181        AtomicNumber(AtomicU64::new(self.0.load(Ordering::Relaxed)))
182    }
183}
184
185impl From<f64> for Number {
186    fn from(f: f64) -> Self {
187        Number(f64_to_u64(f))
188    }
189}
190
191impl From<i64> for Number {
192    fn from(i: i64) -> Self {
193        Number(i as u64)
194    }
195}
196
197impl From<u64> for Number {
198    fn from(u: u64) -> Self {
199        Number(u)
200    }
201}
202
203/// A descriptor for the encoded data type of a `Number`
204#[derive(Clone, Debug, PartialEq, Hash)]
205pub enum NumberKind {
206    /// A Number that stores `i64` values.
207    I64,
208    /// A Number that stores `f64` values.
209    F64,
210    /// A Number that stores `u64` values.
211    U64,
212}
213
214impl NumberKind {
215    /// Returns the zero value for each kind
216    pub fn zero(&self) -> Number {
217        match self {
218            NumberKind::I64 => 0i64.into(),
219            NumberKind::F64 => 0f64.into(),
220            NumberKind::U64 => 0u64.into(),
221        }
222    }
223
224    /// Returns the max value for each kind
225    pub fn max(&self) -> Number {
226        match self {
227            NumberKind::I64 => std::i64::MAX.into(),
228            NumberKind::F64 => std::f64::MAX.into(),
229            NumberKind::U64 => std::u64::MAX.into(),
230        }
231    }
232
233    /// Returns the min value for each kind
234    pub fn min(&self) -> Number {
235        match self {
236            NumberKind::I64 => std::i64::MIN.into(),
237            NumberKind::F64 => std::f64::MIN.into(),
238            NumberKind::U64 => std::u64::MIN.into(),
239        }
240    }
241}
242
243#[inline]
244fn u64_to_f64(val: u64) -> f64 {
245    f64::from_bits(val)
246}
247
248#[inline]
249fn f64_to_u64(val: f64) -> u64 {
250    f64::to_bits(val)
251}