1use std::sync::atomic::{AtomicU64, Ordering};
14use std::time::{Duration, Instant};
15
16#[repr(align(64))]
21pub struct Counter {
22 value: AtomicU64,
24 created_at: Instant,
26}
27
28#[derive(Debug, Clone)]
30pub struct CounterStats {
31 pub value: u64,
33 pub age: Duration,
35 pub rate_per_second: f64,
37 pub total: u64,
39}
40
41impl Counter {
42 #[inline]
44 pub fn new() -> Self {
45 Self {
46 value: AtomicU64::new(0),
47 created_at: Instant::now(),
48 }
49 }
50
51 #[inline]
53 pub fn with_value(initial: u64) -> Self {
54 Self {
55 value: AtomicU64::new(initial),
56 created_at: Instant::now(),
57 }
58 }
59
60 #[inline(always)]
67 pub fn inc(&self) {
68 self.value.fetch_add(1, Ordering::Relaxed);
69 }
70
71 #[inline(always)]
76 pub fn add(&self, amount: u64) {
77 self.value.fetch_add(amount, Ordering::Relaxed);
78 }
79
80 #[inline(always)]
82 pub fn get(&self) -> u64 {
83 self.value.load(Ordering::Relaxed)
84 }
85
86 #[inline]
90 pub fn reset(&self) {
91 self.value.store(0, Ordering::SeqCst);
92 }
93
94 #[inline]
98 pub fn set(&self, value: u64) {
99 self.value.store(value, Ordering::SeqCst);
100 }
101
102 #[inline]
106 pub fn compare_and_swap(&self, expected: u64, new: u64) -> Result<u64, u64> {
107 match self
108 .value
109 .compare_exchange(expected, new, Ordering::SeqCst, Ordering::SeqCst)
110 {
111 Ok(prev) => Ok(prev),
112 Err(current) => Err(current),
113 }
114 }
115
116 #[inline]
118 pub fn fetch_add(&self, amount: u64) -> u64 {
119 self.value.fetch_add(amount, Ordering::Relaxed)
120 }
121
122 #[inline]
124 pub fn add_and_get(&self, amount: u64) -> u64 {
125 self.value.fetch_add(amount, Ordering::Relaxed) + amount
126 }
127
128 #[inline]
130 pub fn inc_and_get(&self) -> u64 {
131 self.value.fetch_add(1, Ordering::Relaxed) + 1
132 }
133
134 pub fn stats(&self) -> CounterStats {
136 let value = self.get();
137 let age = self.created_at.elapsed();
138 let age_seconds = age.as_secs_f64();
139
140 let rate_per_second = if age_seconds > 0.0 {
141 value as f64 / age_seconds
142 } else {
143 0.0
144 };
145
146 CounterStats {
147 value,
148 age,
149 rate_per_second,
150 total: value,
151 }
152 }
153
154 #[inline]
156 pub fn age(&self) -> Duration {
157 self.created_at.elapsed()
158 }
159
160 #[inline]
162 pub fn is_zero(&self) -> bool {
163 self.get() == 0
164 }
165
166 #[inline]
168 pub fn rate_per_second(&self) -> f64 {
169 let age_seconds = self.age().as_secs_f64();
170 if age_seconds > 0.0 {
171 self.get() as f64 / age_seconds
172 } else {
173 0.0
174 }
175 }
176
177 #[inline]
179 pub fn saturating_add(&self, amount: u64) {
180 loop {
181 let current = self.get();
182 let new_value = current.saturating_add(amount);
183
184 if new_value == current {
186 break;
187 }
188
189 match self.compare_and_swap(current, new_value) {
191 Ok(_) => break,
192 Err(_) => continue, }
194 }
195 }
196}
197
198impl Default for Counter {
199 #[inline]
200 fn default() -> Self {
201 Self::new()
202 }
203}
204
205impl std::fmt::Display for Counter {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 write!(f, "Counter({})", self.get())
208 }
209}
210
211impl std::fmt::Debug for Counter {
212 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213 f.debug_struct("Counter")
214 .field("value", &self.get())
215 .field("age", &self.age())
216 .field("rate_per_second", &self.rate_per_second())
217 .finish()
218 }
219}
220
221unsafe impl Send for Counter {}
223unsafe impl Sync for Counter {}
224
225impl Counter {
227 #[inline]
231 pub fn batch_inc(&self, count: usize) {
232 if count > 0 {
233 self.add(count as u64);
234 }
235 }
236
237 #[inline]
239 pub fn inc_if(&self, condition: bool) {
240 if condition {
241 self.inc();
242 }
243 }
244
245 #[inline]
247 pub fn inc_max(&self, max_value: u64) -> bool {
248 loop {
249 let current = self.get();
250 if current >= max_value {
251 return false;
252 }
253
254 match self.compare_and_swap(current, current + 1) {
255 Ok(_) => return true,
256 Err(_) => continue, }
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use std::sync::Arc;
266 use std::thread;
267
268 #[test]
269 fn test_basic_operations() {
270 let counter = Counter::new();
271
272 assert_eq!(counter.get(), 0);
273 assert!(counter.is_zero());
274
275 counter.inc();
276 assert_eq!(counter.get(), 1);
277 assert!(!counter.is_zero());
278
279 counter.add(5);
280 assert_eq!(counter.get(), 6);
281
282 counter.reset();
283 assert_eq!(counter.get(), 0);
284
285 counter.set(42);
286 assert_eq!(counter.get(), 42);
287 }
288
289 #[test]
290 fn test_fetch_operations() {
291 let counter = Counter::new();
292
293 assert_eq!(counter.fetch_add(10), 0);
294 assert_eq!(counter.get(), 10);
295
296 assert_eq!(counter.inc_and_get(), 11);
297 assert_eq!(counter.add_and_get(5), 16);
298 }
299
300 #[test]
301 fn test_compare_and_swap() {
302 let counter = Counter::new();
303 counter.set(10);
304
305 assert_eq!(counter.compare_and_swap(10, 20), Ok(10));
307 assert_eq!(counter.get(), 20);
308
309 assert_eq!(counter.compare_and_swap(10, 30), Err(20));
311 assert_eq!(counter.get(), 20);
312 }
313
314 #[test]
315 fn test_saturating_add() {
316 let counter = Counter::new();
317 counter.set(u64::MAX - 5);
318
319 counter.saturating_add(10);
320 assert_eq!(counter.get(), u64::MAX);
321
322 counter.saturating_add(100);
324 assert_eq!(counter.get(), u64::MAX);
325 }
326
327 #[test]
328 fn test_conditional_operations() {
329 let counter = Counter::new();
330
331 counter.inc_if(true);
332 assert_eq!(counter.get(), 1);
333
334 counter.inc_if(false);
335 assert_eq!(counter.get(), 1);
336
337 assert!(counter.inc_max(5));
339 assert_eq!(counter.get(), 2);
340
341 counter.set(5);
342 assert!(!counter.inc_max(5));
343 assert_eq!(counter.get(), 5);
344 }
345
346 #[test]
347 fn test_statistics() {
348 let counter = Counter::new();
349 counter.add(100);
350
351 let stats = counter.stats();
352 assert_eq!(stats.value, 100);
353 assert_eq!(stats.total, 100);
354 assert!(stats.age > Duration::from_nanos(0));
355 assert!(stats.rate_per_second >= 0.0);
357 }
358
359 #[test]
360 fn test_high_concurrency() {
361 let counter = Arc::new(Counter::new());
362 let num_threads = 100;
363 let increments_per_thread = 1000;
364
365 let handles: Vec<_> = (0..num_threads)
366 .map(|_| {
367 let counter = Arc::clone(&counter);
368 thread::spawn(move || {
369 for _ in 0..increments_per_thread {
370 counter.inc();
371 }
372 })
373 })
374 .collect();
375
376 for handle in handles {
377 handle.join().unwrap();
378 }
379
380 assert_eq!(counter.get(), num_threads * increments_per_thread);
381
382 let stats = counter.stats();
383 assert!(stats.rate_per_second > 0.0);
384 }
385
386 #[test]
387 fn test_batch_operations() {
388 let counter = Counter::new();
389
390 counter.batch_inc(1000);
391 assert_eq!(counter.get(), 1000);
392
393 counter.batch_inc(0); assert_eq!(counter.get(), 1000);
395 }
396
397 #[test]
398 fn test_display_and_debug() {
399 let counter = Counter::new();
400 counter.set(42);
401
402 let display_str = format!("{}", counter);
403 assert!(display_str.contains("42"));
404
405 let debug_str = format!("{:?}", counter);
406 assert!(debug_str.contains("Counter"));
407 assert!(debug_str.contains("42"));
408 }
409}
410
411#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
412#[allow(unused_imports)]
413mod benchmarks {
414 use super::*;
415 use std::time::Instant;
416
417 #[cfg_attr(not(feature = "bench-tests"), ignore)]
418 #[test]
419 fn bench_counter_increment() {
420 let counter = Counter::new();
421 let iterations = 10_000_000;
422
423 let start = Instant::now();
424 for _ in 0..iterations {
425 counter.inc();
426 }
427 let elapsed = start.elapsed();
428
429 println!(
430 "Counter increment: {:.2} ns/op",
431 elapsed.as_nanos() as f64 / iterations as f64
432 );
433
434 assert!(elapsed.as_nanos() / iterations < 100);
436 assert_eq!(counter.get(), iterations as u64);
437 }
438
439 #[cfg_attr(not(feature = "bench-tests"), ignore)]
440 #[test]
441 fn bench_counter_add() {
442 let counter = Counter::new();
443 let iterations = 1_000_000;
444
445 let start = Instant::now();
446 for i in 0..iterations {
447 counter.add(i + 1);
448 }
449 let elapsed = start.elapsed();
450
451 println!(
452 "Counter add: {:.2} ns/op",
453 elapsed.as_nanos() as f64 / iterations as f64
454 );
455
456 assert!(elapsed.as_nanos() / (iterations as u128) < 200);
458 }
459
460 #[cfg_attr(not(feature = "bench-tests"), ignore)]
461 #[test]
462 fn bench_counter_get() {
463 let counter = Counter::new();
464 counter.set(42);
465 let iterations = 100_000_000;
466
467 let start = Instant::now();
468 let mut sum = 0;
469 for _ in 0..iterations {
470 sum += counter.get();
471 }
472 let elapsed = start.elapsed();
473
474 println!(
475 "Counter get: {:.2} ns/op",
476 elapsed.as_nanos() as f64 / iterations as f64
477 );
478
479 assert_eq!(sum, 42 * iterations);
481
482 assert!(elapsed.as_nanos() / (iterations as u128) < 50);
484 }
485}