1use crate::{MetricsError, Result};
14use std::sync::atomic::{AtomicU64, Ordering};
15use std::time::{Duration, Instant};
16
17#[repr(align(64))]
22pub struct Counter {
23 value: AtomicU64,
25 created_at: Instant,
27}
28
29#[derive(Debug, Clone)]
31pub struct CounterStats {
32 pub value: u64,
34 pub age: Duration,
36 pub rate_per_second: f64,
38 pub total: u64,
40}
41
42impl Counter {
43 #[inline]
45 pub fn new() -> Self {
46 Self {
47 value: AtomicU64::new(0),
48 created_at: Instant::now(),
49 }
50 }
51
52 #[inline]
54 pub fn with_value(initial: u64) -> Self {
55 Self {
56 value: AtomicU64::new(initial),
57 created_at: Instant::now(),
58 }
59 }
60
61 #[inline(always)]
68 pub fn inc(&self) {
69 self.value.fetch_add(1, Ordering::Relaxed);
70 }
71
72 #[inline(always)]
86 pub fn try_inc(&self) -> Result<()> {
87 loop {
90 let current = self.value.load(Ordering::Relaxed);
91 if current == u64::MAX {
92 return Err(MetricsError::Overflow);
93 }
94 match self.value.compare_exchange_weak(
95 current,
96 current + 1,
97 Ordering::Relaxed,
98 Ordering::Relaxed,
99 ) {
100 Ok(_) => return Ok(()),
101 Err(_) => continue,
102 }
103 }
104 }
105
106 #[inline(always)]
111 pub fn add(&self, amount: u64) {
112 self.value.fetch_add(amount, Ordering::Relaxed);
113 }
114
115 #[inline(always)]
128 pub fn try_add(&self, amount: u64) -> Result<()> {
129 if amount == 0 {
130 return Ok(());
131 }
132 loop {
134 let current = self.value.load(Ordering::Relaxed);
135 let new_val = current.checked_add(amount).ok_or(MetricsError::Overflow)?;
136 match self.value.compare_exchange_weak(
137 current,
138 new_val,
139 Ordering::Relaxed,
140 Ordering::Relaxed,
141 ) {
142 Ok(_) => return Ok(()),
143 Err(_) => continue,
144 }
145 }
146 }
147
148 #[must_use]
150 #[inline(always)]
151 pub fn get(&self) -> u64 {
152 self.value.load(Ordering::Relaxed)
153 }
154
155 #[inline]
159 pub fn reset(&self) {
160 self.value.store(0, Ordering::SeqCst);
161 }
162
163 #[inline]
167 pub fn set(&self, value: u64) {
168 self.value.store(value, Ordering::SeqCst);
169 }
170
171 #[inline]
175 pub fn try_set(&self, value: u64) -> Result<()> {
176 self.set(value);
177 Ok(())
178 }
179
180 #[inline]
184 pub fn compare_and_swap(&self, expected: u64, new: u64) -> core::result::Result<u64, u64> {
185 match self
186 .value
187 .compare_exchange(expected, new, Ordering::SeqCst, Ordering::SeqCst)
188 {
189 Ok(prev) => Ok(prev),
190 Err(current) => Err(current),
191 }
192 }
193
194 #[must_use]
196 #[inline]
197 pub fn fetch_add(&self, amount: u64) -> u64 {
198 self.value.fetch_add(amount, Ordering::Relaxed)
199 }
200
201 #[inline]
214 pub fn try_fetch_add(&self, amount: u64) -> Result<u64> {
215 if amount == 0 {
216 return Ok(self.get());
217 }
218 loop {
220 let current = self.value.load(Ordering::Relaxed);
221 let new_val = current.checked_add(amount).ok_or(MetricsError::Overflow)?;
222 match self.value.compare_exchange_weak(
223 current,
224 new_val,
225 Ordering::Relaxed,
226 Ordering::Relaxed,
227 ) {
228 Ok(prev) => return Ok(prev),
229 Err(_) => continue,
230 }
231 }
232 }
233
234 #[must_use]
236 #[inline]
237 pub fn add_and_get(&self, amount: u64) -> u64 {
238 self.value.fetch_add(amount, Ordering::Relaxed) + amount
239 }
240
241 #[must_use]
243 #[inline]
244 pub fn inc_and_get(&self) -> u64 {
245 self.value.fetch_add(1, Ordering::Relaxed) + 1
246 }
247
248 #[inline]
261 pub fn try_inc_and_get(&self) -> Result<u64> {
262 loop {
265 let current = self.value.load(Ordering::Relaxed);
266 let new_val = current.checked_add(1).ok_or(MetricsError::Overflow)?;
267 match self.value.compare_exchange_weak(
268 current,
269 new_val,
270 Ordering::Relaxed,
271 Ordering::Relaxed,
272 ) {
273 Ok(_) => return Ok(new_val),
274 Err(_) => continue,
275 }
276 }
277 }
278
279 #[must_use]
281 pub fn stats(&self) -> CounterStats {
282 let value = self.get();
283 let age = self.created_at.elapsed();
284 let age_seconds = age.as_secs_f64();
285
286 let rate_per_second = if age_seconds > 0.0 {
287 value as f64 / age_seconds
288 } else {
289 0.0
290 };
291
292 CounterStats {
293 value,
294 age,
295 rate_per_second,
296 total: value,
297 }
298 }
299
300 #[must_use]
302 #[inline]
303 pub fn age(&self) -> Duration {
304 self.created_at.elapsed()
305 }
306
307 #[must_use]
309 #[inline]
310 pub fn is_zero(&self) -> bool {
311 self.get() == 0
312 }
313
314 #[must_use]
316 #[inline]
317 pub fn rate_per_second(&self) -> f64 {
318 let age_seconds = self.age().as_secs_f64();
319 if age_seconds > 0.0 {
320 self.get() as f64 / age_seconds
321 } else {
322 0.0
323 }
324 }
325
326 #[inline]
328 pub fn saturating_add(&self, amount: u64) {
329 loop {
330 let current = self.get();
331 let new_value = current.saturating_add(amount);
332
333 if new_value == current {
335 break;
336 }
337
338 match self.compare_and_swap(current, new_value) {
340 Ok(_) => break,
341 Err(_) => continue, }
343 }
344 }
345}
346
347impl Default for Counter {
348 #[inline]
349 fn default() -> Self {
350 Self::new()
351 }
352}
353
354impl std::fmt::Display for Counter {
355 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
356 write!(f, "Counter({})", self.get())
357 }
358}
359
360impl std::fmt::Debug for Counter {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 f.debug_struct("Counter")
363 .field("value", &self.get())
364 .field("age", &self.age())
365 .field("rate_per_second", &self.rate_per_second())
366 .finish()
367 }
368}
369
370impl Counter {
376 #[inline]
380 pub fn batch_inc(&self, count: usize) {
381 if count > 0 {
382 self.add(count as u64);
383 }
384 }
385
386 #[inline]
388 pub fn inc_if(&self, condition: bool) {
389 if condition {
390 self.inc();
391 }
392 }
393
394 #[inline]
396 pub fn inc_max(&self, max_value: u64) -> bool {
397 loop {
398 let current = self.get();
399 if current >= max_value {
400 return false;
401 }
402
403 match self.compare_and_swap(current, current + 1) {
404 Ok(_) => return true,
405 Err(_) => continue, }
407 }
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use std::sync::Arc;
415 use std::thread;
416
417 #[test]
418 fn test_basic_operations() {
419 let counter = Counter::new();
420
421 assert_eq!(counter.get(), 0);
422 assert!(counter.is_zero());
423
424 counter.inc();
425 assert_eq!(counter.get(), 1);
426 assert!(!counter.is_zero());
427
428 counter.add(5);
429 assert_eq!(counter.get(), 6);
430
431 counter.reset();
432 assert_eq!(counter.get(), 0);
433
434 counter.set(42);
435 assert_eq!(counter.get(), 42);
436 }
437
438 #[test]
439 fn test_fetch_operations() {
440 let counter = Counter::new();
441
442 assert_eq!(counter.fetch_add(10), 0);
443 assert_eq!(counter.get(), 10);
444
445 assert_eq!(counter.inc_and_get(), 11);
446 assert_eq!(counter.add_and_get(5), 16);
447 }
448
449 #[test]
450 fn test_compare_and_swap() {
451 let counter = Counter::new();
452 counter.set(10);
453
454 assert_eq!(counter.compare_and_swap(10, 20), Ok(10));
456 assert_eq!(counter.get(), 20);
457
458 assert_eq!(counter.compare_and_swap(10, 30), Err(20));
460 assert_eq!(counter.get(), 20);
461 }
462
463 #[test]
464 fn test_saturating_add() {
465 let counter = Counter::new();
466 counter.set(u64::MAX - 5);
467
468 counter.saturating_add(10);
469 assert_eq!(counter.get(), u64::MAX);
470
471 counter.saturating_add(100);
473 assert_eq!(counter.get(), u64::MAX);
474 }
475
476 #[test]
477 fn test_conditional_operations() {
478 let counter = Counter::new();
479
480 counter.inc_if(true);
481 assert_eq!(counter.get(), 1);
482
483 counter.inc_if(false);
484 assert_eq!(counter.get(), 1);
485
486 assert!(counter.inc_max(5));
488 assert_eq!(counter.get(), 2);
489
490 counter.set(5);
491 assert!(!counter.inc_max(5));
492 assert_eq!(counter.get(), 5);
493 }
494
495 #[test]
496 fn test_statistics() {
497 let counter = Counter::new();
498 counter.add(100);
499
500 let stats = counter.stats();
501 assert_eq!(stats.value, 100);
502 assert_eq!(stats.total, 100);
503 assert!(stats.age > Duration::from_nanos(0));
504 assert!(stats.rate_per_second >= 0.0);
506 }
507
508 #[test]
509 fn test_high_concurrency() {
510 let counter = Arc::new(Counter::new());
511 let num_threads = 100;
512 let increments_per_thread = 1000;
513
514 let handles: Vec<_> = (0..num_threads)
515 .map(|_| {
516 let counter = Arc::clone(&counter);
517 thread::spawn(move || {
518 for _ in 0..increments_per_thread {
519 counter.inc();
520 }
521 })
522 })
523 .collect();
524
525 for handle in handles {
526 handle.join().unwrap();
527 }
528
529 assert_eq!(counter.get(), num_threads * increments_per_thread);
530
531 let stats = counter.stats();
532 assert!(stats.rate_per_second > 0.0);
533 }
534
535 #[test]
536 fn test_batch_operations() {
537 let counter = Counter::new();
538
539 counter.batch_inc(1000);
540 assert_eq!(counter.get(), 1000);
541
542 counter.batch_inc(0); assert_eq!(counter.get(), 1000);
544 }
545
546 #[test]
547 fn test_display_and_debug() {
548 let counter = Counter::new();
549 counter.set(42);
550
551 let display_str = format!("{counter}");
552 assert!(display_str.contains("42"));
553
554 let debug_str = format!("{counter:?}");
555 assert!(debug_str.contains("Counter"));
556 assert!(debug_str.contains("42"));
557 }
558
559 #[test]
560 fn test_checked_operations_and_overflow_paths() {
561 let counter = Counter::new();
562
563 counter.try_set(3).unwrap();
564 assert_eq!(counter.get(), 3);
565
566 counter.try_inc().unwrap();
567 assert_eq!(counter.get(), 4);
568
569 counter.try_add(0).unwrap();
570 assert_eq!(counter.get(), 4);
571
572 assert_eq!(counter.try_fetch_add(2).unwrap(), 4);
573 assert_eq!(counter.get(), 6);
574
575 assert_eq!(counter.try_fetch_add(0).unwrap(), 6);
576 assert_eq!(counter.try_inc_and_get().unwrap(), 7);
577
578 let overflow = Counter::with_value(u64::MAX);
579 assert!(matches!(overflow.try_inc(), Err(MetricsError::Overflow)));
580 assert!(matches!(overflow.try_add(1), Err(MetricsError::Overflow)));
581 assert!(matches!(
582 overflow.try_fetch_add(1),
583 Err(MetricsError::Overflow)
584 ));
585 assert!(matches!(
586 overflow.try_inc_and_get(),
587 Err(MetricsError::Overflow)
588 ));
589 }
590}
591
592#[cfg(all(test, feature = "bench-tests", not(tarpaulin)))]
593#[allow(unused_imports)]
594mod benchmarks {
595 use super::*;
596 use std::time::Instant;
597
598 #[cfg_attr(not(feature = "bench-tests"), ignore)]
599 #[test]
600 fn bench_counter_increment() {
601 let counter = Counter::new();
602 let iterations = 10_000_000;
603
604 let start = Instant::now();
605 for _ in 0..iterations {
606 counter.inc();
607 }
608 let elapsed = start.elapsed();
609
610 println!(
611 "Counter increment: {:.2} ns/op",
612 elapsed.as_nanos() as f64 / iterations as f64
613 );
614
615 assert!(elapsed.as_nanos() / iterations < 100);
617 assert_eq!(counter.get(), iterations as u64);
618 }
619
620 #[cfg_attr(not(feature = "bench-tests"), ignore)]
621 #[test]
622 fn bench_counter_add() {
623 let counter = Counter::new();
624 let iterations = 1_000_000;
625
626 let start = Instant::now();
627 for i in 0..iterations {
628 counter.add(i + 1);
629 }
630 let elapsed = start.elapsed();
631
632 println!(
633 "Counter add: {:.2} ns/op",
634 elapsed.as_nanos() as f64 / iterations as f64
635 );
636
637 assert!(elapsed.as_nanos() / (iterations as u128) < 200);
639 }
640
641 #[cfg_attr(not(feature = "bench-tests"), ignore)]
642 #[test]
643 fn bench_counter_get() {
644 let counter = Counter::new();
645 counter.set(42);
646 let iterations = 100_000_000;
647
648 let start = Instant::now();
649 let mut sum = 0;
650 for _ in 0..iterations {
651 sum += counter.get();
652 }
653 let elapsed = start.elapsed();
654
655 println!(
656 "Counter get: {:.2} ns/op",
657 elapsed.as_nanos() as f64 / iterations as f64
658 );
659
660 assert_eq!(sum, 42 * iterations);
662
663 assert!(elapsed.as_nanos() / (iterations as u128) < 50);
665 }
666}