use std::{
str as std_str,
time::{
Duration,
Instant,
},
};
pub(crate) mod gram_utils {
pub fn calc_doom(v : u64) -> u32 {
if v >= 100000000 {
} else {
if v >= 10000 {
if v >= 1000000 {
if v >= 10000000 {
return 8;
} else {
return 7;
}
} else {
if v >= 100000 {
return 6;
} else {
return 5;
}
}
} else {
if v >= 100 {
if v >= 1000 {
return 4;
} else {
return 3;
}
} else {
if v >= 10 {
return 2;
} else {
if v > 0 {
return 1;
} else {
return 0;
}
}
}
}
}
if 0 == v {
0
} else {
let mut r = 0;
let mut v = v;
while 0 != v {
v /= 10;
r += 1;
}
r
}
}
pub fn gram_doom_to_char(
doom : u32,
ch_0 : u8,
ch_overflow : u8,
range : &[u8],
) -> u8 {
if 0 == doom {
ch_0
} else {
if doom as usize > range.len() {
ch_overflow
} else {
range[doom as usize - 1]
}
}
}
}
#[derive(Debug)]
#[derive(Default)]
pub struct DoomGram {
event_count : usize,
event_time_total : u64,
has_overflowed : bool,
min_event_time : Option<u64>,
max_event_time : Option<u64>,
num_events_in_1ns : u64,
num_events_in_10ns : u64,
num_events_in_100ns : u64,
num_events_in_1us : u64,
num_events_in_10us : u64,
num_events_in_100us : u64,
num_events_in_1ms : u64,
num_events_in_10ms : u64,
num_events_in_100ms : u64,
num_events_in_1s : u64,
num_events_in_10s : u64,
num_events_ge_100s : u64,
}
impl DoomGram {
}
impl DoomGram {
pub fn clear(&mut self)
{
*self = Default::default();
}
pub fn push_event_duration(
&mut self,
duration : Duration,
)
{
self.push_event_time_ns(duration.as_nanos() as u64);
}
pub fn push_event_time_ns(
&mut self,
time_in_ns : u64,
) -> bool {
if self.try_add_ns_to_total_and_update_minmax_and_count_(time_in_ns) {
self.event_count += 1;
self.push_event_time_ns_(time_in_ns);
true
} else {
false
}
}
pub fn push_event_time_us(
&mut self,
time_in_us : u64,
) -> bool {
let time_in_ns = 1000 * time_in_us;
if self.try_add_ns_to_total_and_update_minmax_and_count_(time_in_ns) {
self.event_count += 1;
self.push_event_time_ns_(time_in_ns);
true
} else {
false
}
}
pub fn push_event_time_ms(
&mut self,
time_in_ms : u64,
) -> bool {
let time_in_ns = 1000 * 1000 * time_in_ms;
if self.try_add_ns_to_total_and_update_minmax_and_count_(time_in_ns) {
self.event_count += 1;
self.push_event_time_ns_(time_in_ns);
true
} else {
false
}
}
pub fn push_event_time_s(
&mut self,
time_in_s : u64,
) -> bool {
let time_in_ns = 1000 * 1000 * 1000 * time_in_s;
if self.try_add_ns_to_total_and_update_minmax_and_count_(time_in_ns) {
self.event_count += 1;
self.push_event_time_ns_(time_in_ns);
true
} else {
false
}
}
}
impl DoomGram {
pub fn event_count(&self) -> usize {
self.event_count
}
pub fn event_time_total(&self) -> Option<u64> {
if self.has_overflowed {
None
} else {
Some(self.event_time_total)
}
}
pub fn event_time_total_raw(&self) -> u64 {
self.event_time_total
}
pub fn has_overflowed(&self) -> bool {
self.has_overflowed
}
pub fn min_event_time(&self) -> Option<u64> {
self.min_event_time
}
pub fn max_event_time(&self) -> Option<u64> {
self.max_event_time
}
pub fn num_events_in_1ns(&self) -> u64 {
self.num_events_in_1ns
}
pub fn num_events_in_10ns(&self) -> u64 {
self.num_events_in_10ns
}
pub fn num_events_in_100ns(&self) -> u64 {
self.num_events_in_100ns
}
pub fn num_events_in_1us(&self) -> u64 {
self.num_events_in_1us
}
pub fn num_events_in_10us(&self) -> u64 {
self.num_events_in_10us
}
pub fn num_events_in_100us(&self) -> u64 {
self.num_events_in_100us
}
pub fn num_events_in_1ms(&self) -> u64 {
self.num_events_in_1ms
}
pub fn num_events_in_10ms(&self) -> u64 {
self.num_events_in_10ms
}
pub fn num_events_in_100ms(&self) -> u64 {
self.num_events_in_100ms
}
pub fn num_events_in_1s(&self) -> u64 {
self.num_events_in_1s
}
pub fn num_events_in_10s(&self) -> u64 {
self.num_events_in_10s
}
pub fn num_events_ge_100s(&self) -> u64 {
self.num_events_ge_100s
}
pub fn to_strip(&self) -> String {
use gram_utils::{
calc_doom,
gram_doom_to_char,
};
let mut strip : [u8; 12] = Default::default();
let ch_0 = b'_';
let ch_overflow = b'*';
let range = b"abcdefghijklmnopqrstuvwxyz";
strip[0] = gram_doom_to_char(calc_doom(self.num_events_in_1ns()), ch_0, ch_overflow, range);
strip[1] = gram_doom_to_char(calc_doom(self.num_events_in_10ns()), ch_0, ch_overflow, range);
strip[2] = gram_doom_to_char(calc_doom(self.num_events_in_100ns()), ch_0, ch_overflow, range);
strip[3] = gram_doom_to_char(calc_doom(self.num_events_in_1us()), ch_0, ch_overflow, range);
strip[4] = gram_doom_to_char(calc_doom(self.num_events_in_10us()), ch_0, ch_overflow, range);
strip[5] = gram_doom_to_char(calc_doom(self.num_events_in_100us()), ch_0, ch_overflow, range);
strip[6] = gram_doom_to_char(calc_doom(self.num_events_in_1ms()), ch_0, ch_overflow, range);
strip[7] = gram_doom_to_char(calc_doom(self.num_events_in_10ms()), ch_0, ch_overflow, range);
strip[8] = gram_doom_to_char(calc_doom(self.num_events_in_100ms()), ch_0, ch_overflow, range);
strip[9] = gram_doom_to_char(calc_doom(self.num_events_in_1s()), ch_0, ch_overflow, range);
strip[10] = gram_doom_to_char(calc_doom(self.num_events_in_10s()), ch_0, ch_overflow, range);
strip[11] = gram_doom_to_char(calc_doom(self.num_events_ge_100s()), ch_0, ch_overflow, range);
let s = unsafe { std_str::from_utf8_unchecked(&strip) };
s.into()
}
}
impl DoomGram {
fn push_event_time_ns_(
&mut self,
time_in_ns : u64,
) {
if time_in_ns >= 1000000 {
if time_in_ns >= 1000000000 {
if time_in_ns >= 100000000000 {
self.num_events_ge_100s += 1;
} else if time_in_ns >= 10000000000 {
self.num_events_in_10s += 1;
} else {
self.num_events_in_1s += 1;
}
} else {
if time_in_ns >= 100000000 {
self.num_events_in_100ms += 1;
} else if time_in_ns >= 10000000 {
self.num_events_in_10ms += 1;
} else {
self.num_events_in_1ms += 1;
}
}
} else {
if time_in_ns >= 1000 {
if time_in_ns >= 100000 {
self.num_events_in_100us += 1;
} else if time_in_ns >= 10000 {
self.num_events_in_10us += 1;
} else {
self.num_events_in_1us += 1;
}
} else {
if time_in_ns >= 100 {
self.num_events_in_100ns += 1;
} else if time_in_ns >= 10 {
self.num_events_in_10ns += 1;
} else if time_in_ns >= 1 {
self.num_events_in_1ns += 1;
}
}
}
}
fn try_add_ns_to_total_and_update_minmax_and_count_(
&mut self,
time_in_ns : u64
) -> bool {
if self.has_overflowed {
return false;
}
match self.event_time_total.checked_add(time_in_ns) {
Some(new_total) => {
self.event_time_total = new_total;
match self.min_event_time {
Some(min_event_time) => {
if time_in_ns < min_event_time {
self.min_event_time = Some(time_in_ns);
}
},
None => {
self.min_event_time = Some(time_in_ns);
}
};
match self.max_event_time {
Some(max_event_time) => {
if time_in_ns > max_event_time {
self.max_event_time = Some(time_in_ns);
}
},
None => {
self.max_event_time = Some(time_in_ns);
}
};
true
},
None => {
self.has_overflowed = true;
false
},
}
}
}
pub fn doom_scope<F, R>(
dg : &mut DoomGram,
work : F
) -> (
R, // work_result
u64, // measured_elapsed_time_in_ns
)
where
F : FnOnce() -> R
{
let before = Instant::now();
let work_result = work();
let after = Instant::now();
let measured_elapsed_time_in_ns = (after - before).as_nanos() as u64;
dg.push_event_time_ns(measured_elapsed_time_in_ns);
(
work_result,
measured_elapsed_time_in_ns,
)
}
#[cfg(test)]
mod tests {
#![allow(non_snake_case)]
use super::{
DoomGram,
doom_scope,
};
use std::{
thread as std_thread,
time::Duration,
};
#[test]
fn TEST_DoomGram_Default() {
let dg = DoomGram::default();
assert_eq!(0, dg.event_count());
assert_eq!(Some(0), dg.event_time_total());
assert_eq!(0, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(None, dg.min_event_time());
assert_eq!(None, dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(0, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(0, dg.num_events_in_1us());
assert_eq!(0, dg.num_events_in_10us());
assert_eq!(0, dg.num_events_in_100us());
assert_eq!(0, dg.num_events_in_1ms());
assert_eq!(0, dg.num_events_in_10ms());
assert_eq!(0, dg.num_events_in_100ms());
assert_eq!(0, dg.num_events_in_1s());
assert_eq!(0, dg.num_events_in_10s());
assert_eq!(0, dg.num_events_ge_100s());
assert_eq!("____________", dg.to_strip());
}
#[test]
fn TEST_DoomGram_SINGLE_TIMING_EVENT() {
let mut dg = DoomGram::default();
dg.push_event_time_ms(13);
assert_eq!(1, dg.event_count());
assert_eq!(Some(13000000), dg.event_time_total());
assert_eq!(13000000, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(13000000), dg.min_event_time());
assert_eq!(Some(13000000), dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(0, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(0, dg.num_events_in_1us());
assert_eq!(0, dg.num_events_in_10us());
assert_eq!(0, dg.num_events_in_100us());
assert_eq!(0, dg.num_events_in_1ms());
assert_eq!(1, dg.num_events_in_10ms());
assert_eq!(0, dg.num_events_in_100ms());
assert_eq!(0, dg.num_events_in_1s());
assert_eq!(0, dg.num_events_in_10s());
assert_eq!(0, dg.num_events_ge_100s());
assert_eq!("_______a____", dg.to_strip());
}
#[test]
fn TEST_doomgram_ZERO_TIME_EVENTS() {
let mut dg = DoomGram::default();
dg.push_event_time_ns(0);
dg.push_event_time_us(0);
dg.push_event_time_ms(0);
dg.push_event_time_s(0);
assert_eq!(4, dg.event_count());
assert_eq!(Some(0), dg.event_time_total());
assert_eq!(0, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(0), dg.min_event_time());
assert_eq!(Some(0), dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(0, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(0, dg.num_events_in_1us());
assert_eq!(0, dg.num_events_in_10us());
assert_eq!(0, dg.num_events_in_100us());
assert_eq!(0, dg.num_events_in_1ms());
assert_eq!(0, dg.num_events_in_10ms());
assert_eq!(0, dg.num_events_in_100ms());
assert_eq!(0, dg.num_events_in_1s());
assert_eq!(0, dg.num_events_in_10s());
assert_eq!(0, dg.num_events_ge_100s());
assert_eq!("____________", dg.to_strip());
}
#[test]
fn TEST_doomgram_UNIFORM_SPREAD_TIMINGS_1() {
let mut dg = DoomGram::default();
dg.push_event_time_ns( 9);
dg.push_event_time_ns( 80);
dg.push_event_time_ns(700);
dg.push_event_time_us( 6);
dg.push_event_time_us( 50);
dg.push_event_time_us(400);
dg.push_event_time_ms( 3);
dg.push_event_time_ms( 20);
dg.push_event_time_ms(100);
dg.push_event_time_s( 9);
dg.push_event_time_s( 80);
dg.push_event_time_s( 700);
assert_eq!(12, dg.event_count());
assert_eq!(Some(789123456789), dg.event_time_total());
assert_eq!(789123456789, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(9), dg.min_event_time());
assert_eq!(Some(700000000000), dg.max_event_time());
assert_eq!(1, dg.num_events_in_1ns());
assert_eq!(1, dg.num_events_in_10ns());
assert_eq!(1, dg.num_events_in_100ns());
assert_eq!(1, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(1, dg.num_events_in_100us());
assert_eq!(1, dg.num_events_in_1ms());
assert_eq!(1, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(1, dg.num_events_in_1s());
assert_eq!(1, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("aaaaaaaaaaaa", dg.to_strip());
}
#[test]
fn TEST_doomgram_UNIFORM_SPREAD_TIMINGS_2() {
let mut dg = DoomGram::default();
dg.push_event_time_ns( 9);
dg.push_event_time_ns( 80);
dg.push_event_time_ns( 700);
dg.push_event_time_ns( 6000);
dg.push_event_time_ns( 50000);
dg.push_event_time_ns(400000);
dg.push_event_time_ms( 3);
dg.push_event_time_ms( 20);
dg.push_event_time_ms( 100);
dg.push_event_time_ms( 9000);
dg.push_event_time_ms( 80000);
dg.push_event_time_ms(700000);
assert_eq!(12, dg.event_count());
assert_eq!(Some(789123456789), dg.event_time_total());
assert_eq!(789123456789, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(9), dg.min_event_time());
assert_eq!(Some(700000000000), dg.max_event_time());
assert_eq!(1, dg.num_events_in_1ns());
assert_eq!(1, dg.num_events_in_10ns());
assert_eq!(1, dg.num_events_in_100ns());
assert_eq!(1, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(1, dg.num_events_in_100us());
assert_eq!(1, dg.num_events_in_1ms());
assert_eq!(1, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(1, dg.num_events_in_1s());
assert_eq!(1, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("aaaaaaaaaaaa", dg.to_strip());
}
#[test]
fn TEST_doomgram_UNIFORM_SPREAD_TIMINGS_3() {
let mut dg = DoomGram::default();
dg.push_event_time_ns( 9);
dg.push_event_time_ns( 80);
dg.push_event_time_ns( 700);
dg.push_event_time_ns( 6000);
dg.push_event_time_ns( 50000);
dg.push_event_time_ns( 400000);
dg.push_event_time_ns( 3000000);
dg.push_event_time_ns( 20000000);
dg.push_event_time_ns( 100000000);
dg.push_event_time_ns( 9000000000);
dg.push_event_time_ns( 80000000000);
dg.push_event_time_ns(700000000000);
assert_eq!(12, dg.event_count());
assert_eq!(Some(789123456789), dg.event_time_total());
assert_eq!(789123456789, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(9), dg.min_event_time());
assert_eq!(Some(700000000000), dg.max_event_time());
assert_eq!(1, dg.num_events_in_1ns());
assert_eq!(1, dg.num_events_in_10ns());
assert_eq!(1, dg.num_events_in_100ns());
assert_eq!(1, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(1, dg.num_events_in_100us());
assert_eq!(1, dg.num_events_in_1ms());
assert_eq!(1, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(1, dg.num_events_in_1s());
assert_eq!(1, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("aaaaaaaaaaaa", dg.to_strip());
}
#[test]
fn TEST_doomgram_UNIFORM_SPREAD_TIMINGS_4() {
let mut dg = DoomGram::default();
dg.push_event_time_us( 6);
dg.push_event_time_us( 50);
dg.push_event_time_us( 400);
dg.push_event_time_us( 3000);
dg.push_event_time_us( 20000);
dg.push_event_time_us( 100000);
dg.push_event_time_us( 9000000);
dg.push_event_time_us( 80000000);
dg.push_event_time_us(700000000);
assert_eq!(9, dg.event_count());
assert_eq!(Some(789123456000), dg.event_time_total());
assert_eq!(789123456000, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(6000), dg.min_event_time());
assert_eq!(Some(700000000000), dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(0, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(1, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(1, dg.num_events_in_100us());
assert_eq!(1, dg.num_events_in_1ms());
assert_eq!(1, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(1, dg.num_events_in_1s());
assert_eq!(1, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("___aaaaaaaaa", dg.to_strip());
}
#[test]
fn TEST_doomgram_SEVERAL_DISTINCT_TIMINGS() {
let mut dg = DoomGram::default();
dg.push_event_time_ns(23);
dg.push_event_time_ns(10);
dg.push_event_time_us(7);
dg.push_event_time_us(7);
dg.push_event_time_us(89);
dg.push_event_time_ms(248);
dg.push_event_time_s(5);
dg.push_event_time_s(309);
assert_eq!(8, dg.event_count());
assert_eq!(Some(314248103033), dg.event_time_total());
assert_eq!(314248103033, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(10), dg.min_event_time());
assert_eq!(Some(309000000000), dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(2, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(2, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(0, dg.num_events_in_100us());
assert_eq!(0, dg.num_events_in_1ms());
assert_eq!(0, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(1, dg.num_events_in_1s());
assert_eq!(0, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("_a_aa___aa_a", dg.to_strip());
}
#[test]
fn TEST_doomgram_SEVERAL_INTERSECTING_TIMINGS() {
let mut dg = DoomGram::default();
dg.push_event_time_ns(11);
dg.push_event_time_ns(19);
dg.push_event_time_ns(19);
dg.push_event_time_us(7);
dg.push_event_time_us(7);
dg.push_event_time_us(89);
dg.push_event_time_ms(248);
dg.push_event_time_ms(4321);
dg.push_event_time_s(5);
dg.push_event_time_s(309);
assert_eq!(10, dg.event_count());
assert_eq!(Some(318569103049), dg.event_time_total());
assert_eq!(318569103049, dg.event_time_total_raw());
assert!(!dg.has_overflowed());
assert_eq!(Some(11), dg.min_event_time());
assert_eq!(Some(309000000000), dg.max_event_time());
assert_eq!(0, dg.num_events_in_1ns());
assert_eq!(3, dg.num_events_in_10ns());
assert_eq!(0, dg.num_events_in_100ns());
assert_eq!(2, dg.num_events_in_1us());
assert_eq!(1, dg.num_events_in_10us());
assert_eq!(0, dg.num_events_in_100us());
assert_eq!(0, dg.num_events_in_1ms());
assert_eq!(0, dg.num_events_in_10ms());
assert_eq!(1, dg.num_events_in_100ms());
assert_eq!(2, dg.num_events_in_1s());
assert_eq!(0, dg.num_events_in_10s());
assert_eq!(1, dg.num_events_ge_100s());
assert_eq!("_a_aa___aa_a", dg.to_strip());
}
#[test]
fn TEST_doomgram_OVERFLOW_BY_SECONDS() {
let mut dg = DoomGram::default();
{
let r = dg.push_event_time_s(18446744073);
assert!(r);
}
{
let r = dg.push_event_time_s(0);
assert!(r);
}
{
let r = dg.push_event_time_s(1);
assert!(!r);
}
}
#[test]
fn TEST_doomgram_OVERFLOW_BY_MICROSECONDS() {
let mut dg = DoomGram::default();
{
let r = dg.push_event_time_us(18446744073709550);
assert!(r);
}
{
let r = dg.push_event_time_us(1);
assert!(r);
}
{
let r = dg.push_event_time_us(0);
assert!(r);
}
{
let r = dg.push_event_time_us(1);
assert!(!r);
}
}
#[test]
fn TEST_doom_scope_1() {
{
let mut dg = DoomGram::default();
let (_, t) = doom_scope(&mut dg, || {});
assert_eq!(dg.event_time_total_raw(), t);
}
{
let mut dg = DoomGram::default();
let (r, t) = doom_scope(&mut dg, || {
std_thread::sleep(Duration::from_micros(1));
return 123;
});
assert_eq!(123, r);
assert_eq!(dg.event_time_total_raw(), t);
assert!(t >= 1_000);
}
}
}