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 | |