| 1 | use std::any::TypeId; |
| 2 | |
| 3 | use crate::{ |
| 4 | counter::{ |
| 5 | BytesCount, BytesFormat, CharsCount, CyclesCount, IntoCounter, ItemsCount, MaxCountUInt, |
| 6 | }, |
| 7 | time::FineDuration, |
| 8 | util::{fmt::DisplayThroughput, ty::TypeCast}, |
| 9 | }; |
| 10 | |
| 11 | /// Type-erased `Counter`. |
| 12 | /// |
| 13 | /// This does not implement `Copy` because in the future it will contain |
| 14 | /// user-defined counters. |
| 15 | #[derive (Clone)] |
| 16 | pub(crate) struct AnyCounter { |
| 17 | pub kind: KnownCounterKind, |
| 18 | count: MaxCountUInt, |
| 19 | } |
| 20 | |
| 21 | impl AnyCounter { |
| 22 | #[inline ] |
| 23 | pub(crate) fn new<C: IntoCounter>(counter: C) -> Self { |
| 24 | let counter = counter.into_counter(); |
| 25 | |
| 26 | if let Some(bytes) = counter.cast_ref::<BytesCount>() { |
| 27 | Self::bytes(bytes.count) |
| 28 | } else if let Some(chars) = counter.cast_ref::<CharsCount>() { |
| 29 | Self::chars(chars.count) |
| 30 | } else if let Some(cycles) = counter.cast_ref::<CyclesCount>() { |
| 31 | Self::cycles(cycles.count) |
| 32 | } else if let Some(items) = counter.cast_ref::<ItemsCount>() { |
| 33 | Self::items(items.count) |
| 34 | } else { |
| 35 | unreachable!() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | #[inline ] |
| 40 | pub(crate) fn known(kind: KnownCounterKind, count: MaxCountUInt) -> Self { |
| 41 | Self { kind, count } |
| 42 | } |
| 43 | |
| 44 | #[inline ] |
| 45 | pub(crate) fn bytes(count: MaxCountUInt) -> Self { |
| 46 | Self::known(KnownCounterKind::Bytes, count) |
| 47 | } |
| 48 | |
| 49 | #[inline ] |
| 50 | pub(crate) fn chars(count: MaxCountUInt) -> Self { |
| 51 | Self::known(KnownCounterKind::Chars, count) |
| 52 | } |
| 53 | |
| 54 | #[inline ] |
| 55 | pub(crate) fn cycles(count: MaxCountUInt) -> Self { |
| 56 | Self::known(KnownCounterKind::Cycles, count) |
| 57 | } |
| 58 | |
| 59 | #[inline ] |
| 60 | pub(crate) fn items(count: MaxCountUInt) -> Self { |
| 61 | Self::known(KnownCounterKind::Items, count) |
| 62 | } |
| 63 | |
| 64 | pub(crate) fn display_throughput( |
| 65 | &self, |
| 66 | duration: FineDuration, |
| 67 | bytes_format: BytesFormat, |
| 68 | ) -> DisplayThroughput { |
| 69 | DisplayThroughput { counter: self, picos: duration.picos as f64, bytes_format } |
| 70 | } |
| 71 | |
| 72 | #[inline ] |
| 73 | pub(crate) fn count(&self) -> MaxCountUInt { |
| 74 | self.count |
| 75 | } |
| 76 | |
| 77 | #[inline ] |
| 78 | pub(crate) fn known_kind(&self) -> KnownCounterKind { |
| 79 | self.kind |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | /// Kind of `Counter` defined by this crate. |
| 84 | #[derive (Clone, Copy, PartialEq, Eq, Debug)] |
| 85 | pub(crate) enum KnownCounterKind { |
| 86 | Bytes, |
| 87 | Chars, |
| 88 | Cycles, |
| 89 | Items, |
| 90 | } |
| 91 | |
| 92 | impl KnownCounterKind { |
| 93 | pub const COUNT: usize = 4; |
| 94 | |
| 95 | pub const ALL: [Self; Self::COUNT] = [Self::Bytes, Self::Chars, Self::Cycles, Self::Items]; |
| 96 | |
| 97 | /// The maximum width for columns displaying counters. |
| 98 | pub const MAX_COMMON_COLUMN_WIDTH: usize = "1.111 Kitem/s" .len(); |
| 99 | |
| 100 | #[inline ] |
| 101 | pub fn of<C: IntoCounter>() -> Self { |
| 102 | let id: TypeId = TypeId::of::<C::Counter>(); |
| 103 | if id == TypeId::of::<BytesCount>() { |
| 104 | Self::Bytes |
| 105 | } else if id == TypeId::of::<CharsCount>() { |
| 106 | Self::Chars |
| 107 | } else if id == TypeId::of::<CyclesCount>() { |
| 108 | Self::Cycles |
| 109 | } else if id == TypeId::of::<ItemsCount>() { |
| 110 | Self::Items |
| 111 | } else { |
| 112 | unreachable!() |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | #[cfg (test)] |
| 118 | mod tests { |
| 119 | use super::*; |
| 120 | |
| 121 | #[test ] |
| 122 | fn known_counter_kind() { |
| 123 | macro_rules! test { |
| 124 | ($t:ident, $k:ident) => { |
| 125 | assert_eq!(KnownCounterKind::of::<$t>(), KnownCounterKind::$k); |
| 126 | }; |
| 127 | } |
| 128 | |
| 129 | test !(BytesCount, Bytes); |
| 130 | test !(CharsCount, Chars); |
| 131 | test !(CyclesCount, Cycles); |
| 132 | test !(ItemsCount, Items); |
| 133 | } |
| 134 | |
| 135 | mod display_throughput { |
| 136 | use super::*; |
| 137 | |
| 138 | #[test ] |
| 139 | fn bytes() { |
| 140 | #[track_caller ] |
| 141 | fn test( |
| 142 | bytes: MaxCountUInt, |
| 143 | picos: u128, |
| 144 | expected_binary: &str, |
| 145 | expected_decimal: &str, |
| 146 | ) { |
| 147 | for (bytes_format, expected) in [ |
| 148 | (BytesFormat::Binary, expected_binary), |
| 149 | (BytesFormat::Decimal, expected_decimal), |
| 150 | ] { |
| 151 | assert_eq!( |
| 152 | AnyCounter::bytes(bytes) |
| 153 | .display_throughput(FineDuration { picos }, bytes_format) |
| 154 | .to_string(), |
| 155 | expected |
| 156 | ); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | #[track_caller ] |
| 161 | fn test_all(bytes: MaxCountUInt, picos: u128, expected: &str) { |
| 162 | test (bytes, picos, expected, expected); |
| 163 | } |
| 164 | |
| 165 | test_all(1, 0, "inf B/s" ); |
| 166 | test_all(MaxCountUInt::MAX, 0, "inf B/s" ); |
| 167 | |
| 168 | test_all(0, 0, "0 B/s" ); |
| 169 | test_all(0, 1, "0 B/s" ); |
| 170 | test_all(0, u128::MAX, "0 B/s" ); |
| 171 | } |
| 172 | |
| 173 | #[test ] |
| 174 | fn chars() { |
| 175 | #[track_caller ] |
| 176 | fn test(chars: MaxCountUInt, picos: u128, expected: &str) { |
| 177 | assert_eq!( |
| 178 | AnyCounter::chars(chars) |
| 179 | .display_throughput(FineDuration { picos }, BytesFormat::default()) |
| 180 | .to_string(), |
| 181 | expected |
| 182 | ); |
| 183 | } |
| 184 | |
| 185 | test (1, 0, "inf char/s" ); |
| 186 | test (MaxCountUInt::MAX, 0, "inf char/s" ); |
| 187 | |
| 188 | test (0, 0, "0 char/s" ); |
| 189 | test (0, 1, "0 char/s" ); |
| 190 | test (0, u128::MAX, "0 char/s" ); |
| 191 | } |
| 192 | |
| 193 | #[test ] |
| 194 | fn cycles() { |
| 195 | #[track_caller ] |
| 196 | fn test(cycles: MaxCountUInt, picos: u128, expected: &str) { |
| 197 | assert_eq!( |
| 198 | AnyCounter::cycles(cycles) |
| 199 | .display_throughput(FineDuration { picos }, BytesFormat::default()) |
| 200 | .to_string(), |
| 201 | expected |
| 202 | ); |
| 203 | } |
| 204 | |
| 205 | test (1, 0, "inf Hz" ); |
| 206 | test (MaxCountUInt::MAX, 0, "inf Hz" ); |
| 207 | |
| 208 | test (0, 0, "0 Hz" ); |
| 209 | test (0, 1, "0 Hz" ); |
| 210 | test (0, u128::MAX, "0 Hz" ); |
| 211 | } |
| 212 | |
| 213 | #[test ] |
| 214 | fn items() { |
| 215 | #[track_caller ] |
| 216 | fn test(items: MaxCountUInt, picos: u128, expected: &str) { |
| 217 | assert_eq!( |
| 218 | AnyCounter::items(items) |
| 219 | .display_throughput(FineDuration { picos }, BytesFormat::default()) |
| 220 | .to_string(), |
| 221 | expected |
| 222 | ); |
| 223 | } |
| 224 | |
| 225 | test (1, 0, "inf item/s" ); |
| 226 | test (MaxCountUInt::MAX, 0, "inf item/s" ); |
| 227 | |
| 228 | test (0, 0, "0 item/s" ); |
| 229 | test (0, 1, "0 item/s" ); |
| 230 | test (0, u128::MAX, "0 item/s" ); |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | |