use rustc_serialize::json::Json;
use std::marker::PhantomData;
use std::mem::size_of;
use std::sync::atomic::{AtomicBool, Ordering};
use indexing::*;
use misc::{vec_with_size, Flatten, LinearBuckets, SerializationFormat};
use service::{PrivateAccess, Service};
use task::{BackEnd, Op, PlainRawStorage};
pub trait Histogram<T>: Clone {
fn record(&self, value: T) {
self.record_cb(|| Some(value))
}
fn record_cb<F>(&self, _: F)
where
F: FnOnce() -> Option<T>;
}
impl BackEnd<Plain> {
fn raw_record(&self, k: &Key<Plain>, value: u32) {
self.sender.send(Op::RecordPlain(k.index, value)).unwrap();
}
fn raw_record_cb<F, T>(&self, cb: F) -> bool
where
F: FnOnce() -> Option<T>,
T: Flatten,
{
if let Some(k) = self.get_key() {
if let Some(v) = cb() {
self.raw_record(&k, v.as_u32());
true
} else {
false
}
} else {
false
}
}
}
#[derive(Default)]
pub struct Ignoring<T> {
witness: PhantomData<T>,
}
impl<T> Ignoring<T> {
pub fn new() -> Ignoring<T> {
Ignoring {
witness: PhantomData,
}
}
}
impl<T> Histogram<T> for Ignoring<T> {
fn record_cb<F>(&self, _: F)
where
F: FnOnce() -> Option<T>,
{
}
}
impl<T> Clone for Ignoring<T> {
fn clone(&self) -> Self {
Ignoring {
witness: PhantomData,
}
}
}
pub struct Flag {
back_end: BackEnd<Plain>,
cache: AtomicBool,
}
struct FlagStorage {
encountered: bool,
}
impl PlainRawStorage for FlagStorage {
fn store(&mut self, _: u32) {
self.encountered = true;
}
fn to_json(&self, format: &SerializationFormat) -> Json {
match format {
SerializationFormat::SimpleJson => Json::I64(if self.encountered { 1 } else { 0 }),
}
}
}
impl Histogram<()> for Flag {
fn record_cb<F>(&self, cb: F)
where
F: FnOnce() -> Option<()>,
{
if self.cache.load(Ordering::Relaxed) {
return;
}
if self.back_end.raw_record_cb(cb) {
self.cache.store(true, Ordering::Relaxed);
}
}
}
impl Flag {
pub fn new(service: &Service, name: String) -> Flag {
let storage = Box::new(FlagStorage { encountered: false });
let key = PrivateAccess::register_plain(service, name, storage);
Flag {
back_end: BackEnd::new(service, key),
cache: AtomicBool::new(false),
}
}
}
impl Clone for Flag {
fn clone(&self) -> Self {
Flag {
back_end: self.back_end.clone(),
cache: AtomicBool::new(self.cache.load(Ordering::Relaxed)),
}
}
}
pub struct Linear<T>
where
T: Flatten,
{
witness: PhantomData<T>,
back_end: BackEnd<Plain>,
}
impl<T> Histogram<T> for Linear<T>
where
T: Flatten,
{
fn record_cb<F>(&self, cb: F)
where
F: FnOnce() -> Option<T>,
{
self.back_end.raw_record_cb(cb);
}
}
impl<T> Linear<T>
where
T: Flatten,
{
pub fn new(service: &Service, name: String, min: u32, max: u32, buckets: usize) -> Linear<T> {
assert!(size_of::<u32>() <= size_of::<usize>());
assert!(min < max);
assert!(max - min + 1 >= buckets as u32);
let shape = LinearBuckets::new(min, max, buckets);
let storage = Box::new(LinearStorage::new(shape));
let key = PrivateAccess::register_plain(service, name, storage);
Linear {
witness: PhantomData,
back_end: BackEnd::new(service, key),
}
}
}
struct LinearStorage {
values: Vec<u32>, shape: LinearBuckets,
}
impl LinearStorage {
fn new(shape: LinearBuckets) -> LinearStorage {
let vec = vec_with_size(shape.buckets, 0);
LinearStorage { values: vec, shape }
}
}
impl PlainRawStorage for LinearStorage {
fn store(&mut self, value: u32) {
let index = self.shape.get_bucket(value);
self.values[index] += 1;
}
fn to_json(&self, _: &SerializationFormat) -> Json {
let json = Json::Array(self.values.iter().map(|&x| Json::I64(x as i64)).collect());
json
}
}
impl<T> Clone for Linear<T>
where
T: Flatten,
{
fn clone(&self) -> Self {
Linear {
witness: PhantomData,
back_end: self.back_end.clone(),
}
}
}
#[derive(Clone)]
pub struct Count {
back_end: BackEnd<Plain>,
}
struct CountStorage {
value: u32,
}
impl PlainRawStorage for CountStorage {
fn store(&mut self, value: u32) {
self.value += value;
}
fn to_json(&self, format: &SerializationFormat) -> Json {
match format {
SerializationFormat::SimpleJson => Json::I64(self.value as i64),
}
}
}
impl Histogram<u32> for Count {
fn record_cb<F>(&self, cb: F)
where
F: FnOnce() -> Option<u32>,
{
self.back_end.raw_record_cb(cb);
}
}
impl Count {
pub fn new(service: &Service, name: String) -> Count {
let storage = Box::new(CountStorage { value: 0 });
let key = PrivateAccess::register_plain(service, name, storage);
Count {
back_end: BackEnd::new(service, key),
}
}
}
pub struct Enum<K>
where
K: Flatten,
{
witness: PhantomData<K>,
back_end: BackEnd<Plain>,
}
struct EnumStorage {
values: Vec<u32>,
}
impl PlainRawStorage for EnumStorage {
fn store(&mut self, value: u32) {
self.values.resize(value as usize + 1, 0);
self.values[value as usize] += 1;
}
fn to_json(&self, format: &SerializationFormat) -> Json {
match format {
SerializationFormat::SimpleJson => {
Json::Array(self.values.iter().map(|&x| Json::I64(x as i64)).collect())
}
}
}
}
impl<K> Histogram<K> for Enum<K>
where
K: Flatten,
{
fn record_cb<F>(&self, cb: F)
where
F: FnOnce() -> Option<K>,
{
self.back_end.raw_record_cb(cb);
}
}
impl<K> Enum<K>
where
K: Flatten,
{
pub fn new(service: &Service, name: String) -> Enum<K> {
let storage = Box::new(EnumStorage { values: Vec::new() });
let key = PrivateAccess::register_plain(service, name, storage);
Enum {
witness: PhantomData,
back_end: BackEnd::new(service, key),
}
}
}
impl<K> Clone for Enum<K>
where
K: Flatten,
{
fn clone(&self) -> Self {
Enum {
witness: PhantomData,
back_end: self.back_end.clone(),
}
}
}