use crate::encoding::{EncodeGaugeValue, EncodeMetric, MetricEncoder};
use super::{MetricType, TypedMetric};
use std::marker::PhantomData;
use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::{AtomicI64, AtomicU64};
use std::sync::Arc;
#[cfg(target_has_atomic = "64")]
#[derive(Debug)]
pub struct Gauge<N = i64, A = AtomicI64> {
value: Arc<A>,
phantom: PhantomData<N>,
}
#[cfg(not(target_has_atomic = "64"))]
#[derive(Debug)]
pub struct Gauge<N = i32, A = AtomicI32> {
value: Arc<A>,
phantom: PhantomData<N>,
}
impl<N, A> Clone for Gauge<N, A> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
phantom: PhantomData,
}
}
}
impl<N, A: Default> Default for Gauge<N, A> {
fn default() -> Self {
Self {
value: Arc::new(A::default()),
phantom: PhantomData,
}
}
}
impl<N, A: Atomic<N>> Gauge<N, A> {
pub fn inc(&self) -> N {
self.value.inc()
}
pub fn inc_by(&self, v: N) -> N {
self.value.inc_by(v)
}
pub fn dec(&self) -> N {
self.value.dec()
}
pub fn dec_by(&self, v: N) -> N {
self.value.dec_by(v)
}
pub fn set(&self, v: N) -> N {
self.value.set(v)
}
pub fn get(&self) -> N {
self.value.get()
}
pub fn inner(&self) -> &A {
&self.value
}
}
pub trait Atomic<N> {
fn inc(&self) -> N;
fn inc_by(&self, v: N) -> N;
fn dec(&self) -> N;
fn dec_by(&self, v: N) -> N;
fn set(&self, v: N) -> N;
fn get(&self) -> N;
}
impl Atomic<i32> for AtomicI32 {
fn inc(&self) -> i32 {
self.inc_by(1)
}
fn inc_by(&self, v: i32) -> i32 {
self.fetch_add(v, Ordering::Relaxed)
}
fn dec(&self) -> i32 {
self.dec_by(1)
}
fn dec_by(&self, v: i32) -> i32 {
self.fetch_sub(v, Ordering::Relaxed)
}
fn set(&self, v: i32) -> i32 {
self.swap(v, Ordering::Relaxed)
}
fn get(&self) -> i32 {
self.load(Ordering::Relaxed)
}
}
impl Atomic<u32> for AtomicU32 {
fn inc(&self) -> u32 {
self.inc_by(1)
}
fn inc_by(&self, v: u32) -> u32 {
self.fetch_add(v, Ordering::Relaxed)
}
fn dec(&self) -> u32 {
self.dec_by(1)
}
fn dec_by(&self, v: u32) -> u32 {
self.fetch_sub(v, Ordering::Relaxed)
}
fn set(&self, v: u32) -> u32 {
self.swap(v, Ordering::Relaxed)
}
fn get(&self) -> u32 {
self.load(Ordering::Relaxed)
}
}
#[cfg(target_has_atomic = "64")]
impl Atomic<i64> for AtomicI64 {
fn inc(&self) -> i64 {
self.inc_by(1)
}
fn inc_by(&self, v: i64) -> i64 {
self.fetch_add(v, Ordering::Relaxed)
}
fn dec(&self) -> i64 {
self.dec_by(1)
}
fn dec_by(&self, v: i64) -> i64 {
self.fetch_sub(v, Ordering::Relaxed)
}
fn set(&self, v: i64) -> i64 {
self.swap(v, Ordering::Relaxed)
}
fn get(&self) -> i64 {
self.load(Ordering::Relaxed)
}
}
#[cfg(target_has_atomic = "64")]
impl Atomic<f64> for AtomicU64 {
fn inc(&self) -> f64 {
self.inc_by(1.0)
}
fn inc_by(&self, v: f64) -> f64 {
let mut old_u64 = self.load(Ordering::Relaxed);
let mut old_f64;
loop {
old_f64 = f64::from_bits(old_u64);
let new = f64::to_bits(old_f64 + v);
match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => break,
Err(x) => old_u64 = x,
}
}
old_f64
}
fn dec(&self) -> f64 {
self.dec_by(1.0)
}
fn dec_by(&self, v: f64) -> f64 {
let mut old_u64 = self.load(Ordering::Relaxed);
let mut old_f64;
loop {
old_f64 = f64::from_bits(old_u64);
let new = f64::to_bits(old_f64 - v);
match self.compare_exchange_weak(old_u64, new, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => break,
Err(x) => old_u64 = x,
}
}
old_f64
}
fn set(&self, v: f64) -> f64 {
f64::from_bits(self.swap(f64::to_bits(v), Ordering::Relaxed))
}
fn get(&self) -> f64 {
f64::from_bits(self.load(Ordering::Relaxed))
}
}
impl<N, A> TypedMetric for Gauge<N, A> {
const TYPE: MetricType = MetricType::Gauge;
}
impl<N, A> EncodeMetric for Gauge<N, A>
where
N: EncodeGaugeValue,
A: Atomic<N>,
{
fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
encoder.encode_gauge(&self.get())
}
fn metric_type(&self) -> MetricType {
Self::TYPE
}
}
#[derive(Debug, Default)]
pub struct ConstGauge<N = i64> {
value: N,
}
impl<N> ConstGauge<N> {
pub fn new(value: N) -> Self {
Self { value }
}
}
impl<N> TypedMetric for ConstGauge<N> {
const TYPE: MetricType = MetricType::Gauge;
}
impl<N> EncodeMetric for ConstGauge<N>
where
N: EncodeGaugeValue,
{
fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
encoder.encode_gauge(&self.value)
}
fn metric_type(&self) -> MetricType {
Self::TYPE
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inc_dec_and_get() {
let gauge: Gauge = Gauge::default();
assert_eq!(0, gauge.inc());
assert_eq!(1, gauge.get());
assert_eq!(1, gauge.dec());
assert_eq!(0, gauge.get());
assert_eq!(0, gauge.set(10));
assert_eq!(10, gauge.get());
}
}