width_counters/counter/
mod.rs

1//! Counters
2
3use core::{
4  cmp, fmt,
5  hash::{Hash, Hasher},
6  i16, i32, i64, i8, isize,
7  ops,
8  sync::atomic::*,
9  u16, u32, u64, u8, usize,
10};
11#[cfg(feature = "iterators")]
12use crate::iterators::*;
13use enumflags2::{BitFlags, _internal::RawBitFlags};
14use paste::paste;
15#[cfg(feature = "serde")]
16use serde::{ser::SerializeStruct, Deserialize, Serialize};
17
18pub mod behavior;
19pub use behavior::*;
20
21mod private {
22    use super::*;
23    /// An AsRef/AsMut trait that shouldn't be used outside this crate  
24    pub trait As<T> {
25        fn as_ref(&self) -> &T;
26        fn as_mut(&mut self) -> &mut T;
27    }
28
29    /// An ordering definition for serde
30    #[derive(strum::AsRefStr)]
31    #[cfg_attr(feature = "serde", derive(Deserialize))]
32    #[non_exhaustive]
33    pub enum O {
34        AcqRel,
35        Acquire,
36        Relaxed,
37        Release,
38        SeqCst,
39    }
40    impl From<Ordering> for O {
41        fn from(value: Ordering) -> Self {
42            match value {
43                Ordering::AcqRel => Self::AcqRel,
44                Ordering::Acquire => Self::Acquire,
45                Ordering::Relaxed => Self::Relaxed,
46                Ordering::Release => Self::Release,
47                Ordering::SeqCst => Self::SeqCst,
48                _ => Self::SeqCst,
49            }
50        }
51    }
52    impl From<O> for Ordering {
53        fn from(value: O) -> Self {
54            #[allow(unreachable_patterns)]
55            match value {
56                O::AcqRel => Self::AcqRel,
57                O::Acquire => Self::Acquire,
58                O::Relaxed => Self::Relaxed,
59                O::Release => Self::Release,
60                O::SeqCst => Self::SeqCst,
61                _ => Self::SeqCst,
62            }
63        }
64    }
65    #[cfg(feature = "serde")]
66    impl Serialize for O {
67        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
68        where
69            S: serde::Serializer,
70        {
71            macro_rules! serialize {
72        (@it $V:ident) => {{ serializer.serialize_unit_variant("O", Self::$V as u32, Self::$V.as_ref()) }};
73        ($($V:ident,)+) => {{
74          #[allow(unreachable_patterns)]
75          match self {
76            $(Self::$V => serialize!(@it $V),)+
77            _ => serialize!(@it SeqCst),
78          }
79        }}
80      }
81            serialize! { AcqRel, Acquire, Relaxed, Release, SeqCst, }
82        }
83    }
84}
85use private::*;
86
87
88/// Implements a counter or multiple counters
89macro_rules! make_counter {
90  (@Main $Prefix:ident => $Unit:ident | $Atomic:ident ) => {
91    paste!{
92      #[doc = r#"An atomic counter using ["# $Atomic r#"] / ([core::"# $Unit r#"])."#]
93      #[doc = ""]
94      #[doc = r"### Behavior  "]
95      #[doc = "1. The default ordering is [Sequentially Consistent](Ordering::SeqCst).  "]
96      #[doc = "2. The ordering used for atomic operations is customizable for operations ending in `with_ordering`.  "]
97      #[doc = "3. The choice of ordering *intentionally* impacts **ALMOST EVERYTHING** about how this counter works, including de/serialization, incrementing, decrementing, equality comparisons, partial ordering comparisons, etc.  "]
98      #[doc = "4. Total (non-partial) ordering comparisons always use the default ordering.  "]
99      #[doc = "5. Unlike the underlying [" $Atomic "], this will not wrap on overflow unless the cyclic behavior mode is set. "]
100      #[doc = "6. The default behavior is non-monotonic, so the counter can increment and decrement. "]
101      #[doc = r"### Ordering  "]
102      #[doc = "- PartialEq is implemented such that counters with differing orderings are never equal.  "]
103      #[doc = "- PartialOrd is implemented such that counters with differing [(atomic) orderings](Ordering) produce no [(comparison) ordering](core::cmp::Ordering).  "]
104      #[doc = "- **(Saturating) arithmetic operations are implemented such that differing atomic orderings between the operands are ignored!**"]
105      #[doc = r"### Miscellaneous"]
106      #[doc = r"You can use the [to_x](Self::to_x) method to convert to any type that implements From\<" $Unit r"\>"]
107      #[derive(Debug)]
108      pub struct [<$Prefix $Unit:camel>] {
109        /// The underlying counter
110        inner: $Atomic,
111        /// The ordering used for all operations
112        pub (crate) ordering: Ordering,
113        /// A bitmask of counting modes
114        pub (crate) counting_behavior: BitFlags<CountingBehavior>,
115        /// Conflicts 
116        conflicts: AllCountingBehaviorConflicts,
117      }
118
119      #[cfg(feature = "serde")]
120      #[allow(non_snake_case)]
121      mod [<serde_impls_ $Prefix $Unit:camel >] {
122        use super::*;
123        impl Serialize for [<$Prefix $Unit:camel>] {
124          fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
125            where
126              S: serde::Serializer
127          {
128            let name_str = stringify!([<$Prefix $Unit:camel>]);
129            let mut counter = serializer.serialize_struct( name_str, 2 )?;
130            counter.serialize_field("ordering", &O::from(self.ordering))?;
131            counter.serialize_field("inner", &self.get())?;
132            counter.serialize_field("counting_behavior", &self.counting_behavior)?;
133            counter.end()
134          }
135        }
136
137        impl<'de> Deserialize<'de> for [<$Prefix $Unit:camel>] {
138          fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139              where
140                  D: serde::Deserializer<'de>
141          {
142            let name_str = stringify!([<$Prefix $Unit:camel>]);
143
144            #[derive(Deserialize)]
145            #[serde(field_identifier, rename_all = "snake_case")]
146            enum Field { Inner, Ordering, CountingBehavior }
147
148            struct [<$Prefix $Unit:camel Visitor>];
149            impl<'de> serde::de::Visitor<'de> for [<$Prefix $Unit:camel Visitor>] {
150              type Value = [<$Prefix $Unit:camel >];
151
152              fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
153                let expecting_str = stringify!(struct [<$Prefix $Unit:camel>]);
154                formatter.write_str(expecting_str)
155              }
156
157              fn visit_map<V>(self, mut map: V) -> Result< [<$Prefix $Unit:camel >], V::Error>
158                where V: serde::de::MapAccess<'de>
159              {
160                let mut inner = None;
161                let mut ordering = None;
162                let mut counting_behavior = None;
163                while let Some(key) = map.next_key()? {
164                  match key {
165                    Field::Inner => {
166                      if inner.is_some() {
167                        return Err(serde::de::Error::duplicate_field("inner"));
168                      }
169                      let val: $Unit = map.next_value()?;
170                      inner = Some(val.into())
171                    },
172                    Field::Ordering => {
173                      if ordering.is_some() {
174                        return Err(serde::de::Error::duplicate_field("ordering"));
175                      }
176                      let val: O = map.next_value()?;
177                      ordering = Some(val.into())
178                    },
179                    Field::CountingBehavior => {
180                      if counting_behavior.is_some() {
181                        return Err(serde::de::Error::duplicate_field("counting_behavior"));
182                      }
183                      let val: BitFlags<CountingBehavior> = map.next_value()?;
184                      counting_behavior = Some(val.into())
185                    },
186                  }
187                }
188                let ordering = ordering.ok_or_else(|| serde::de::Error::missing_field("ordering"))?;
189                let inner = inner.ok_or_else(|| serde::de::Error::missing_field("inner"))?;
190                let counting_behavior: BitFlags<CountingBehavior> = counting_behavior
191                  .ok_or_else(|| serde::de::Error::missing_field("inner"))?;
192                let conflicts = counting_behavior.get_behavior_conflicts();
193                Ok([<$Prefix $Unit:camel >]{
194                  ordering, 
195                  inner, 
196                  counting_behavior,
197                  conflicts
198                })
199              }
200            }
201            const FIELDS: &'static [&'static str] = &["ordering", "inner"];
202            deserializer.deserialize_struct(name_str, FIELDS, [<$Prefix $Unit:camel Visitor>])
203          }
204        }
205
206        #[cfg(test)]
207        mod test_serde{
208          use super::*;
209          #[test]
210          fn serialize_and_deserialize() {
211            use [<$Prefix $Unit:camel >] as C;
212            // use $Unit as U;
213            let c = C::new_from_offset(21);
214            let ron_c = ron::to_string(&c).expect(stringify!(Must serialize [<$Prefix $Unit:camel >]));
215            let d = ron::from_str(&ron_c).expect(stringify!(Must deserialize [<$Prefix $Unit:camel >]));
216            assert_eq!(c, d, "Counter deserialization equals original serialized counter");
217            // assert_eq!(c.get(), d.get(), "Counter deserialization equals original serialized counter");
218          }
219        }
220      }
221
222      #[cfg(feature = "iterators")]
223      impl IntoIterator for [<$Prefix $Unit:camel>] {
224        type Item = $Unit;
225        type IntoIter = CounterIterator<Self>;
226        fn into_iter(self) -> Self::IntoIter {
227          CounterIterator::new(self)
228        }
229      }
230
231      #[doc = "PartialOrd only produces [cmp ordering](cmp::Ordering) when [atomic orderings](Ordering) are equal"]
232      #[doc = r"```"]
233      #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
234      #[doc = "use core::sync::atomic::Ordering;"]
235      #[doc = r#"let a = C::new_from_offset_with_ordering(32, Ordering::Relaxed);"# ]
236      #[doc = r#"let b = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
237      #[doc = r#"assert!(a < b, "same-cmp::ordering counters must be ordered by when counts");"# ]
238      #[doc = r#"assert!(b > a, "same-cmp::ordering counters must be ordered by when counts");"# ]
239      #[doc = r#"let m = 20;"# ]
240      #[doc = r#"(0..m).for_each(|_| { a.inc_one(); b.inc_one(); });"# ]
241      #[doc = r#"assert!(a < b, "cmp::ordering preserved after counting same amount");"# ]
242      #[doc = r#"assert!(b > a, "cmp::ordering preserved after counting same amount");"# ]
243      impl cmp::PartialOrd for [<$Prefix $Unit:camel >] {
244        fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
245          self.ordering
246            .eq(&other.ordering)
247            .then(|| ())
248            .and_then(|()| self.get().partial_cmp(&other.get()))
249        }
250      }
251
252      impl cmp::Ord for [<$Prefix $Unit:camel >] {
253        fn cmp(&self, other:&Self) -> cmp::Ordering {
254          self.get_with_ordering(Self::DEFAULT_ORDERING)
255            .cmp(&other.get_with_ordering(Self::DEFAULT_ORDERING))
256        }
257      }
258
259      impl fmt::Display for [<$Prefix $Unit:camel>] {
260        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261          write!(f, "{}", self.get())
262        }
263      }
264
265      impl As<$Atomic> for [<$Prefix $Unit:camel>] {
266        fn as_ref(&self) -> &$Atomic { &self.inner}
267        fn as_mut(&mut self) -> &mut $Atomic { &mut self.inner}
268      }
269      impl As<Ordering> for [<$Prefix $Unit:camel>] {
270        fn as_ref(&self) -> &Ordering { &self.ordering}
271        fn as_mut(&mut self) -> &mut Ordering { &mut self.ordering}
272      }
273      impl AsRef<Ordering> for [<$Prefix $Unit:camel>] {
274        fn as_ref(&self) -> &Ordering { <Self as As<Ordering>>::as_ref(self) }
275      }
276
277      impl Clone for [<$Prefix $Unit:camel>] {
278        fn clone(&self) -> Self {
279          Self::new_from_offset_with_ordering(self.get(), self.ordering)
280        }
281      }
282
283      impl Default for [<$Prefix $Unit:camel>] {
284        fn default() -> Self { Self::new() }
285      }
286
287      #[allow(non_snake_case)]
288      mod [<eq_partial_eq_hash_ $Prefix $Unit:camel >] {
289        use super::*;
290        impl Eq for [<$Prefix $Unit:camel>] {}
291        #[doc = "PartialEq is only equal when orderings and counting behaviors are equal"]
292        #[doc = r"```"]
293        #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
294        #[doc = "use core::sync::atomic::Ordering;"]
295        #[doc = r#"let a = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
296        #[doc = r#"let b = C::new_from_offset_with_ordering(33, Ordering::Relaxed);"# ]
297        #[doc = r#"assert_eq!(a, b, "counters must be equal when counts and orderings are equal");"# ]
298        #[doc = r#"assert_eq!(a, b, "counters must be equal when counts and orderings are equal");"# ]
299        #[doc = r#"let m = 20;"# ]
300        #[doc = r#"(0..m).for_each(|_| { a.inc_one(); b.inc_one(); });"# ]
301        #[doc = r#"assert_eq!(a, b, "counters must be equal after counting same amount");"# ]
302        #[doc = r#"assert_eq!(a, b, "counters must be equal after counting same amount");"# ]
303        #[doc = r#"a.inc_one();"# ]
304        #[doc = r#"assert_ne!(a, b, "counters must not be equal after counting different amounts");"# ]
305        #[doc = r#"let c = C::new_from_offset_with_ordering(44, Ordering::Relaxed);"# ]
306        #[doc = r#"let d = C::new_from_offset_with_ordering(44, Ordering::Release);"# ]
307        #[doc = r#"assert_ne!(c, d, "ordering-inequal counters must not be equal with same count");"# ]
308        impl PartialEq for [<$Prefix $Unit:camel>] {
309          fn eq(&self, rhs: &Self) -> bool {
310            self.ordering.eq(&rhs.ordering)
311            && self.counting_behavior.eq(&rhs.counting_behavior)
312            && self.get().eq(&rhs.get())
313          }
314        }
315
316        impl Hash for [<$Prefix $Unit:camel >] {
317          fn hash<H: Hasher>(&self, state: &mut H) {
318            self.ordering.hash(state);
319            self.counting_behavior.hash(state);
320            self.get().hash(state);
321          }
322        }
323
324        #[cfg(test)]
325        mod hash_and_eq {
326          extern crate std;
327          use super::*;
328          use std::collections::hash_map::DefaultHasher;
329          #[test]
330          fn hash_and_eq_property() {
331            use [<$Prefix $Unit:camel >] as C;
332            // use $Unit as U;
333            let c = C::new_from_offset(21);
334            let d = C::new_from_offset(21);
335            assert_eq!(c, d, "Test counters must equal");
336            let hasher_c = &mut DefaultHasher::new();
337            c.hash(hasher_c);
338            let hash_c = hasher_c.finish();
339            let hasher_d = &mut DefaultHasher::new();
340            d.hash(hasher_d);
341            let hash_d = hasher_d.finish();
342            assert_eq!(hash_c, hash_d, "When impelementing Hash and Eq, the property k1 == k2 -> hash(k1) == hash(k2) must hold");
343          }
344        }
345      }
346
347      impl From<$Unit> for [<$Prefix $Unit:camel>] {
348        fn from(x: $Unit) -> Self {
349          let counting_behavior = Self::DEFAULT_COUNTING_BEHAVIOR;
350          let conflicts = counting_behavior.get_behavior_conflicts(); 
351          Self{ 
352            inner: $Atomic::new(x), 
353            ordering: Self::DEFAULT_ORDERING,
354            counting_behavior,
355            conflicts 
356          }  
357        }
358      }
359
360      impl From<&[<$Prefix $Unit:camel>]> for $Unit {
361        fn from(counter: &[<$Prefix $Unit:camel>]) -> Self { counter.get() }
362      }
363      
364      impl HasCountingBehavior for [<$Prefix $Unit:camel>] {
365        fn get_behavior_ref(&self) -> &BitFlags<CountingBehavior> { &self.counting_behavior }
366        fn get_behavior_conflicts(&self) -> AllCountingBehaviorConflicts { self.conflicts }
367      }
368      impl IsCounter for [<$Prefix $Unit:camel >] { 
369        type Unit = $Unit;
370        fn get_ordering_ref(&self) -> &Ordering { &self.ordering } 
371        fn get_current(&self) -> Self::Unit { self.get() } 
372      }
373
374      impl [<$Prefix $Unit:camel>] {
375        #[doc = "Largest [representable value](" $Unit "::MAX)"]
376        pub const MAX: $Unit = $Unit::MAX;
377        #[doc = "Smallest [representable value](" $Unit "::MIN)"]
378        pub const MIN: $Unit = $Unit::MIN;
379        /// Default [Atomic ordering](Ordering)
380        pub const DEFAULT_ORDERING: Ordering = Ordering::SeqCst;
381        /// Default [counting behavior](CountingBehavior) 
382        pub const DEFAULT_COUNTING_BEHAVIOR: BitFlags<CountingBehavior> = BitFlags::<CountingBehavior>::from_bits_truncate_c(
383          CountingBehavior::DEFAULT,
384          BitFlags::CONST_TOKEN
385        );
386        /// Instantiate
387        pub fn new() -> Self { 
388          let counting_behavior = Self::DEFAULT_COUNTING_BEHAVIOR;
389          let conflicts = counting_behavior.get_behavior_conflicts();
390          Self { 
391            inner: $Atomic::new(0), 
392            ordering: Self::DEFAULT_ORDERING,
393            counting_behavior,
394            conflicts
395          }
396        }
397        /// Instantiate with ordering
398        pub fn new_with_ordering(ordering: Ordering) -> Self {
399          let mut s = Self::new();
400          s.ordering = ordering;
401          s
402        }
403        /// Instantiate with offset value
404        pub fn new_from_offset(offset: $Unit) -> Self {
405          let mut s = Self::new();
406          s.inner = $Atomic::new(offset);
407          s
408        }
409        /// Instantiate with offset value and ordering
410        pub fn new_from_offset_with_ordering(offset: $Unit, ordering: Ordering) -> Self {
411          let mut s = Self::new_from_offset(offset);
412          s.ordering = ordering;
413          s
414        }
415        /// Set counting behavior 
416        pub fn set_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
417          &mut self, 
418          counting_behavior: B
419        ) {
420          self.counting_behavior = counting_behavior.into();
421          self.conflicts = self.counting_behavior.get_behavior_conflicts();
422        }
423        /// Instantiate with counting behaviors 
424        pub fn new_with_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
425          counting_behavior: B
426        ) -> Self {
427          let mut s = Self::new();
428          s.set_counting_behavior(counting_behavior);
429          s
430        } 
431        /// Instantiate with offset value and counting behavior
432        pub fn new_from_offset_with_counting_behavior<B: Into<BitFlags<CountingBehavior>>>(
433          offset: $Unit, 
434          counting_behavior: B
435        ) -> Self {
436          let mut s = Self::new_from_offset(offset);
437          s.set_counting_behavior(counting_behavior);
438          s
439        }
440
441        #[doc = "Get current value with the default [ordering](Ordering)"]
442        #[doc = r"```"]
443        #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
444        #[doc = "use " $Unit " as U;"]
445        #[doc = r#"let c = C::new();"# ]
446        #[doc = r#"assert_eq!(c.get(), 0, "get returns initial value");"# ]
447        #[doc = r#"c.inc_one();"# ]
448        #[doc = r#"c.inc_one();"# ]
449        #[doc = r#"c.inc_one();"# ]
450        #[doc = r#"assert_eq!(c.get(), 3, "get returns post-increment value");"# ]
451        pub fn get(&self) -> $Unit { self.inner.load(self.ordering) }
452        /// Get current value with a specific [ordering](Ordering)
453        pub fn get_with_ordering(&self, ordering: Ordering) -> $Unit { self.inner.load(ordering) }
454        /// Convenience method for getting the current value as i128 
455        pub fn get_i128(&self) -> i128 { self.get() as i128 }  
456        #[doc = r#"Convert to some type that impls [From] "# $Unit r#"."#]
457        pub fn to_x<X: From<$Unit>> (&self) -> X { X::from(self.get()) } 
458      }
459
460    }
461  };
462  (@CountsNonMonotonically $Prefix:ident => $Unit:ident | $Atomic:ident => 
463  $(
464    {$CountingDesc:ident ($pro_op:ident/$anti_op:ident) 
465    as $counting_prefix:ident using $counting_op:ident until $test_limit:ident
466    or $cyclic_counting_op:ident};
467  )+) => {
468    paste!{
469      impl CountsNonmotonically for [<$Prefix $Unit:camel>] {
470      $(
471          #[doc = $CountingDesc " by one "]
472          #[doc = r"```"]
473          #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
474          #[doc = "use core::ops::*; "]
475          #[doc = "use enumflags2::{make_bitflags};"]
476          #[doc = "use " $Unit " as U;"]
477          #[doc = r#"let offset = U::MAX/2;"# ]
478          #[doc = r#"let c = C::new_from_offset(offset);"# ]
479          #[doc = r#"let m = 20;"# ]
480          #[doc = r#"(0..m).for_each(|_| { c."# [<$counting_prefix _one>] r#"(); });"# ]
481          #[doc = r#"assert_eq!(c.get(), (offset)."# $pro_op r#"(20), "counter must "# $CountingDesc r#"/"# $pro_op r#" number of times given as per sequential ordering");"# ]
482          #[doc = r#"let d = C::new_from_offset(U::"# $test_limit r#");"# ]
483          #[doc = r#"d."# [<$counting_prefix _one>] r#"();"# ]
484          #[doc = r#"d."# [<$counting_prefix _one>] r#"();"# ]
485          #[doc = r#"assert_eq!(d.get(), U::"# $test_limit r#", "counter must stop at "# $test_limit r#" ");"# ]
486          fn [<$counting_prefix _one>](&self) { self.[<$counting_prefix _by_with_ordering>](1, self.ordering) }
487      
488          #[doc = "Can the counter " $CountingDesc:lower " any further?"]
489          #[doc = ""]
490          #[doc = "- It halts " $CountingDesc:lower " ing at Self::"$test_limit ]
491          #[doc = ""]
492          #[doc = r"```"]
493          #[doc = "use width_counters::{*, " [<$Prefix $Unit:camel>] " as C, CountingBehavior as B };"]
494          #[doc = "use " $Unit " as U;"]
495          #[doc = "use core::ops::*; "]
496          #[doc = r#"let m = 3"# $Unit r#";"# ]
497          #[doc = r#"let offset = C::MAX/2;"# ]
498          #[doc = r#"let d = C::new_from_offset_with_counting_behavior(offset, B::"# $CountingDesc r#");"# ]
499          #[doc = r#"assert_eq!(d."# [< can_ $counting_prefix >] r#"(), true, "counter must detect when it can "# $CountingDesc r#"");"# ]
500          #[doc = r#"let offset = C::"# $test_limit r#";"# ]
501          #[doc = r#"let d = C::new_from_offset_with_counting_behavior(offset, B::"# $CountingDesc r#");"# ]
502          #[doc = r#"assert_eq!(d."# [< can_ $counting_prefix >] r#"(), false, "counter must detect when it can no longer "# $CountingDesc r#"");"# ]
503          fn [< can_ $counting_prefix >](&self) -> bool { 
504            self.[<is_ $counting_prefix rementable>]() 
505            && self.[<is_within_ $counting_prefix rement_bound >]()     
506          }
507
508          #[doc = "Is the current index advanceable with the given operation type"]
509          fn [<is_ $counting_prefix rementable>](&self) -> bool {
510            !self.counting_behavior.is_empty()
511            && !self.conflicts.contains(&CountingBehaviorConflict::Always) 
512            && ( 
513              self.counting_behavior.contains(CountingBehavior::$CountingDesc)
514              || self.counting_behavior.contains(CountingBehavior::Nonmonotonic)
515            ) 
516          } 
517
518          #[doc = "Is it within(inclusive) the " $CountingDesc:lower " bound"]
519          #[doc = ""]
520          #[doc = "- **The bound is:** [Self::"$test_limit "]"]
521          #[doc = "- The bound never applies in:** [acyclic mode](CountingBehavior)" ]
522          fn [<is_within_ $counting_prefix rement_bound >](&self) -> bool {
523            self.get() != Self::$test_limit
524            || self.counting_behavior.contains(CountingBehavior::Cyclic)
525            // !self.[<is_at_ $counting_prefix rement_bound >]()
526            //   || self.counting_behavior.contains(CountingBehavior::Cyclic)
527          } 
528          
529          #[doc = "Is it at the " $CountingDesc:lower " bound"]
530          #[doc = ""]
531          #[doc = "- **The bound is:** [Self::"$test_limit "]"]
532          fn [<is_at_ $counting_prefix rement_bound >](&self) -> bool {
533            self.get() == Self::$test_limit
534          } 
535
536        )+
537      }
538    }
539  };
540  (@Counting $Prefix:ident => 
541    $Unit:ident | $Atomic:ident => 
542    $CountingDesc:ident ($pro_op:ident/$anti_op:ident) 
543    as $counting_prefix:ident using $counting_op:ident until $test_limit:ident
544    or $cyclic_counting_op:ident
545  ) => {
546    paste!{
547      impl [<$Prefix $Unit:camel>] {
548        #[doc = $CountingDesc " by one with ordering"]
549        pub fn [<$counting_prefix _one_with_ordering>](&self, ordering: Ordering) { self.[<$counting_prefix _by_with_ordering>](1, ordering) }
550    
551        #[doc = $CountingDesc " by specified amount"]
552        #[doc = r"```"]
553        #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
554        #[doc = "use core::ops::*; "]
555        #[doc = "use " $Unit " as U;"]
556        #[doc = r#"let offset = U::MAX/2;"# ]
557        #[doc = r#"let c = C::new_from_offset(offset);"# ]
558        #[doc = r#"let m = 20;"# ]
559        #[doc = r#"(0..m).for_each(|_| { c."# [<$counting_prefix _by>] r#"(2); });"# ]
560        #[doc = r#"assert_eq!((c.get() as i128)."# $anti_op r#"((20*2) as i128), ((offset) as i128), "counter must "# $CountingDesc r#" by specified amount");"# ]
561        pub fn [<$counting_prefix _by>](&self, amount: $Unit) { self.[<$counting_prefix _by_with_ordering>](amount, self.ordering); }
562     
563        #[doc = $CountingDesc " by specified amount with ordering"]
564        #[doc = r"```"]
565        #[doc = "use width_counters::{ *, " [<$Prefix $Unit:camel>] " as C };"]
566        #[doc = "use " $Unit " as U;"]
567        #[doc = "use core::ops::*; "]
568        #[doc = r#"let m = 3"# $Unit r#";"# ]
569        #[doc = r#"let d = C::new_from_offset(U::"# $test_limit r#"."# $anti_op r#"(m * 2));"# ]
570        #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
571        #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
572        #[doc = r#"d."# [<$counting_prefix _by>] r#"(m);"# ]
573        #[doc = r#"assert_eq!(d.get(), U::"# $test_limit r#", "counter must stop at "# $test_limit r#"");"# ]
574        pub fn [<$counting_prefix _by_with_ordering>](&self, amount: $Unit, ordering: Ordering) {
575          if self.[<is_ $counting_prefix rementable>]() {
576            let current = self.get_with_ordering(ordering);
577            if self.counting_behavior.contains(CountingBehavior::Cyclic) {
578              let _ = self.inner.swap(current.$cyclic_counting_op(amount), ordering);
579            } else {
580              let _ = self.inner.swap(current.$counting_op(amount), ordering);
581            }
582          }
583        }
584      
585        #[doc = "Combine the " $CountingDesc:lower " (by one) and get operations" ]
586        #[doc = ""]
587        #[doc = "Returns the value **before** the " $CountingDesc:lower " operation"]
588        pub fn [<get_and_ $counting_prefix _one>](&self,) -> $Unit {
589          self.[<get_and_ $counting_prefix _by>](1)
590        }
591        
592        #[doc = "Combine the " $CountingDesc:lower " (by the given amount) and get operations" ]
593        #[doc = ""]
594        #[doc = "Returns the value **before** the " $CountingDesc:lower "operation"]
595        pub fn [<get_and_ $counting_prefix _by>](&self, amount: $Unit) -> $Unit {
596          self.[<get_and_ $counting_prefix _by_with_ordering>](amount, self.ordering)
597        }
598        
599        #[doc = "Combine the " $CountingDesc:lower " (by the given amount) and get operations" ]
600        #[doc = ""]
601        #[doc = "Returns the value **before** the " $CountingDesc:lower " operation"]
602        pub fn [<get_and_ $counting_prefix _by_with_ordering>](&self, amount: $Unit, ordering: Ordering) -> $Unit {
603          let u = self.get();
604          self.[<$counting_prefix _by_with_ordering>](amount, ordering);
605          u
606        }
607      }
608    }
609  };
610  (@Op $Prefix:ident => $Unit:ident op $Op:ty : $op_fn:ident ) => {
611    paste!{
612      impl $Op for [<$Prefix $Unit:camel>] {
613        type Output = Self; 
614        #[doc = r" - This operation is implemented with saturating arithmetic"]
615        #[doc = r" - **This operation IGNORES dissimilar [atomic orderings](Ordering)!**"]
616        fn $op_fn(self, rhs: Self) -> Self::Output {
617          Self::new_from_offset(self.get().[<saturating_ $op_fn>](rhs.get()))
618        }
619      }
620    }
621  };
622  (@Ops $Prefix:ident => $Unit:ident => [
623    $($Op:ty : $op_fn:ident;)+
624  ] ) => {
625    $(make_counter!{@Op $Prefix => $Unit op $Op : $op_fn})+
626  };
627  ($Prefix:ident => [$($Unit:ident | $Atomic:ident, )+]) => {
628    $(make_counter!{@Main $Prefix => $Unit | $Atomic})+
629    $(make_counter!{@Counting $Prefix => $Unit | $Atomic => Increment (add/sub) as inc using saturating_add until MAX or wrapping_add })+
630    $(make_counter!{@Counting $Prefix => $Unit | $Atomic => Decrement (sub/add) as dec using saturating_sub until MIN or wrapping_sub })+
631    $(make_counter!{@CountsNonMonotonically $Prefix => $Unit | $Atomic  =>
632      {Increment (add/sub) as inc using saturating_add until MAX or wrapping_add};
633      {Decrement (sub/add) as dec using saturating_sub until MIN or wrapping_sub};    
634    })+
635    $(make_counter!{@Ops $Prefix => $Unit => [
636      ops::Add : add;
637      ops::Sub : sub;
638      ops::Mul : mul;
639      ops::Div : div;
640    ]})+
641  };
642}
643
644make_counter! {Counter => [
645  u8 | AtomicU8,
646  u16 | AtomicU16,
647  u32 | AtomicU32,
648  u64 | AtomicU64,
649  // u128 | AtomicU128,
650  usize | AtomicUsize,
651
652  i8 | AtomicI8,
653  i16 | AtomicI16,
654  i32 | AtomicI32,
655  i64 | AtomicI64,
656  // i128 | AtomicI128,
657  isize | AtomicIsize,
658]}
659
660
661#[cfg(test)]
662mod general{
663  use super::*;
664  use enumflags2::make_bitflags;
665
666  #[test]
667  fn nonmonotonic() {
668    // The same as the doc tests but more readable and only for one type
669    let c = CounterI8::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Nonmonotonic}));
670    (0..100).for_each(|_| c.inc_one() );
671    assert_eq!(c.get(), 100);
672    (0..100).for_each(|_| c.dec_one() );
673    assert_eq!(c.get(), 0);
674    (0..100).for_each(|_| c.inc_by(2) );
675    assert_eq!(c.get(), i8::MAX);
676    (0..100).for_each(|_| c.dec_by(5) );
677    assert_eq!(c.get(), i8::MIN); 
678  }
679
680  #[test]
681  fn get_and() {
682    let c = CounterI32::new_from_offset_with_counting_behavior(33, make_bitflags!(CountingBehavior::{Decrement}));
683    assert_eq!(c.get_and_dec_by(34), 33, "get_and...method must return starting value");
684    assert_eq!(c.get(), -1, "counter must be set to new value following get_and... method")
685  }
686
687  #[test]
688  fn conflicting_behaviors() {
689    let c0 = CounterI16::new_with_counting_behavior(
690      CountingBehavior::make_behavior_flags(&[
691        CountingBehavior::Nonmonotonic,
692        CountingBehavior::Monotonic,
693      ])
694      // make_bitflags!(CountingBehavior::{Monotonic | Nonmonotonic})
695    );
696    assert!(
697      c0.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always), 
698      "Must detect conflicting monotonic/nonmonotonic counting behaviors"
699    );
700    // assert_eq!(c0.next(), None, "must not advance with conflicting counting behaviors");
701    let c1 = CounterI16::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Increment | Decrement}));
702    assert!(
703      c1.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always), 
704      "Must detect conflicting counting increment/decrement behaviors"
705    );
706
707    let c2 = CounterI16::new_with_counting_behavior(
708      make_bitflags!(CountingBehavior::{Acyclic | Cyclic})
709    );
710    assert!(
711      c2.get_behavior_conflicts().contains(&CountingBehaviorConflict::Overflowing), 
712      "Must detect conflicting counting acyclic/cyclic behaviors"
713    );
714   
715    let c3 = CounterI16::new_with_counting_behavior(make_bitflags!(CountingBehavior::{Monotonic | Increment}));
716    assert!(
717      !c3.get_behavior_conflicts().contains(&CountingBehaviorConflict::Always), 
718      "Must ignore nonconflicting counting behaviors"
719    );
720  }
721
722
723  #[test]
724  fn cyclic() {
725    // The same as the doc tests but more readable and only for one type
726    let c = CounterU8::new_with_counting_behavior(
727      make_bitflags!(
728        CountingBehavior::{Monotonic | Increment | Cyclic}
729      )
730    );
731    let m = 100u8;
732    (0..m).for_each(|_| c.inc_one() );
733    assert_eq!(c.get(), m, "must increment specified amount");
734    (0..m).for_each(|_| c.dec_one() );
735    assert_eq!(c.get(), m, "must remain monotonic non-decreasing");
736    (0..(u8::MAX-m)).for_each(|_| c.inc_by(1) );
737    assert_eq!(c.get(), u8::MAX, "must increment to maximum");
738    (0..(m+1)).for_each(|_| c.inc_one() );
739    assert_eq!(c.get(), m, "must cycle around"); 
740  }
741
742}