use crate::encoding::{EncodeMetric, MetricEncoder, NoLabelSet};
use super::{MetricType, TypedMetric};
use std::marker::PhantomData;
#[cfg(target_has_atomic = "64")]
use std::sync::atomic::AtomicU64;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::Arc;
#[cfg(target_has_atomic = "64")]
#[derive(Debug)]
pub struct Counter<N = u64, A = AtomicU64> {
value: Arc<A>,
phantom: PhantomData<N>,
}
#[cfg(not(target_has_atomic = "64"))]
#[derive(Debug)]
pub struct Counter<N = u32, A = AtomicU32> {
value: Arc<A>,
phantom: PhantomData<N>,
}
impl<N, A> Clone for Counter<N, A> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
phantom: PhantomData,
}
}
}
impl<N, A: Default> Default for Counter<N, A> {
fn default() -> Self {
Counter {
value: Arc::new(A::default()),
phantom: PhantomData,
}
}
}
impl<N, A: Atomic<N>> Counter<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 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 get(&self) -> N;
}
#[cfg(target_has_atomic = "64")]
impl Atomic<u64> for AtomicU64 {
fn inc(&self) -> u64 {
self.inc_by(1)
}
fn inc_by(&self, v: u64) -> u64 {
self.fetch_add(v, Ordering::Relaxed)
}
fn get(&self) -> u64 {
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 get(&self) -> u32 {
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 get(&self) -> f64 {
f64::from_bits(self.load(Ordering::Relaxed))
}
}
impl Atomic<f32> for AtomicU32 {
fn inc(&self) -> f32 {
self.inc_by(1.0)
}
fn inc_by(&self, v: f32) -> f32 {
let mut old_u32 = self.load(Ordering::Relaxed);
let mut old_f32;
loop {
old_f32 = f32::from_bits(old_u32);
let new = f32::to_bits(old_f32 + v);
match self.compare_exchange_weak(old_u32, new, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => break,
Err(x) => old_u32 = x,
}
}
old_f32
}
fn get(&self) -> f32 {
f32::from_bits(self.load(Ordering::Relaxed))
}
}
impl<N, A> TypedMetric for Counter<N, A> {
const TYPE: MetricType = MetricType::Counter;
}
impl<N, A> EncodeMetric for Counter<N, A>
where
N: crate::encoding::EncodeCounterValue,
A: Atomic<N>,
{
fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
encoder.encode_counter::<NoLabelSet, _, u64>(&self.get(), None)
}
fn metric_type(&self) -> MetricType {
Self::TYPE
}
}
#[derive(Debug, Default)]
pub struct ConstCounter<N = u64> {
value: N,
}
impl<N> ConstCounter<N> {
pub fn new(value: N) -> Self {
Self { value }
}
}
impl<N> TypedMetric for ConstCounter<N> {
const TYPE: MetricType = MetricType::Counter;
}
impl<N> EncodeMetric for ConstCounter<N>
where
N: crate::encoding::EncodeCounterValue,
{
fn encode(&self, mut encoder: MetricEncoder) -> Result<(), std::fmt::Error> {
encoder.encode_counter::<NoLabelSet, _, u64>(&self.value, None)
}
fn metric_type(&self) -> MetricType {
Self::TYPE
}
}
#[cfg(test)]
mod tests {
use super::*;
use quickcheck::QuickCheck;
#[test]
fn inc_and_get() {
let counter: Counter = Counter::default();
assert_eq!(0, counter.inc());
assert_eq!(1, counter.get());
}
#[cfg(target_has_atomic = "64")]
#[test]
fn f64_stored_in_atomic_u64() {
fn prop(fs: Vec<f64>) {
let fs: Vec<f64> = fs
.into_iter()
.map(|f| if f.is_normal() { f } else { 0.0 })
.collect();
let sum = fs.iter().sum();
let counter = Counter::<f64, AtomicU64>::default();
for f in fs {
counter.inc_by(f);
}
assert_eq!(counter.get(), sum)
}
QuickCheck::new().tests(10).quickcheck(prop as fn(_))
}
}