use crate::error::Error;
pub trait CountMinValue: private::CountMinValue {}
pub trait UnsignedCountMinValue: CountMinValue + private::UnsignedCountMinValue {}
mod private {
use std::ops::Add;
use crate::error::Error;
pub trait CountMinValue: Sized + Copy + Ord + Add<Output = Self> {
const ZERO: Self;
const ONE: Self;
const MAX: Self;
fn checked_abs(self) -> Option<Self>;
fn checked_add(self, other: Self) -> Option<Self>;
fn scale_nonnegative(self, factor: f64) -> Self;
fn to_bytes(self) -> [u8; 8];
fn try_from_bytes(bytes: [u8; 8]) -> Result<Self, Error>;
}
pub trait UnsignedCountMinValue: CountMinValue {
fn halve(self) -> Self;
}
}
#[inline]
fn scale_u64(value: u64, factor: f64) -> u64 {
debug_assert!(factor > 0.0 && factor <= 1.0);
let bits = factor.to_bits();
let exponent = ((bits >> 52) & 0x7ff) as u32;
let fraction = bits & ((1_u64 << 52) - 1);
let (significand, shift) = if exponent == 0 {
(fraction, 1074)
} else {
(fraction | (1_u64 << 52), 1075 - exponent)
};
(u128::from(value) * u128::from(significand))
.checked_shr(shift)
.unwrap_or(0) as u64
}
macro_rules! impl_signed {
($name:ty, $min:expr, $max:expr) => {
impl private::CountMinValue for $name {
const ZERO: Self = 0;
const ONE: Self = 1;
const MAX: Self = $max;
#[inline(always)]
fn checked_abs(self) -> Option<Self> {
self.checked_abs()
}
#[inline(always)]
fn checked_add(self, other: Self) -> Option<Self> {
self.checked_add(other)
}
#[inline(always)]
fn scale_nonnegative(self, factor: f64) -> Self {
debug_assert!(self >= 0);
scale_u64(self as u64, factor) as $name
}
#[inline(always)]
fn to_bytes(self) -> [u8; 8] {
let value = self as i64;
value.to_le_bytes()
}
#[inline(always)]
fn try_from_bytes(bytes: [u8; 8]) -> Result<Self, Error> {
let value = i64::from_le_bytes(bytes);
if value < $min as i64 || value > $max as i64 {
return Err(Error::deserial(format!(
"value {} out of range for {}",
value,
stringify!($name)
)));
}
Ok(value as $name)
}
}
impl CountMinValue for $name {}
};
}
impl_signed!(i8, i8::MIN, i8::MAX);
impl_signed!(i16, i16::MIN, i16::MAX);
impl_signed!(i32, i32::MIN, i32::MAX);
impl_signed!(i64, i64::MIN, i64::MAX);
macro_rules! impl_unsigned {
($name:ty, $max:expr) => {
impl private::CountMinValue for $name {
const ZERO: Self = 0;
const ONE: Self = 1;
const MAX: Self = $max;
#[inline(always)]
fn checked_abs(self) -> Option<Self> {
Some(self)
}
#[inline(always)]
fn checked_add(self, other: Self) -> Option<Self> {
self.checked_add(other)
}
#[inline(always)]
fn scale_nonnegative(self, factor: f64) -> Self {
scale_u64(self as u64, factor) as $name
}
#[inline(always)]
fn to_bytes(self) -> [u8; 8] {
let value = self as u64;
value.to_le_bytes()
}
#[inline(always)]
fn try_from_bytes(bytes: [u8; 8]) -> Result<Self, Error> {
let value = u64::from_le_bytes(bytes);
if value > $max as u64 {
return Err(Error::deserial(format!(
"value {} out of range for {}",
value,
stringify!($name)
)));
}
Ok(value as $name)
}
}
impl private::UnsignedCountMinValue for $name {
#[inline(always)]
fn halve(self) -> Self {
self >> 1
}
}
impl CountMinValue for $name {}
impl UnsignedCountMinValue for $name {}
};
}
impl_unsigned!(u8, u8::MAX);
impl_unsigned!(u16, u16::MAX);
impl_unsigned!(u32, u32::MAX);
impl_unsigned!(u64, u64::MAX);